Compare commits

..

1 Commits

Author SHA1 Message Date
50e994f982 Store array of items in configuration. 2018-12-06 15:54:03 -08:00
468 changed files with 25341 additions and 15965 deletions

View File

@ -99,10 +99,10 @@ define([
GeneratorMetadataProvider.prototype.getMetadata = function (domainObject) {
return _.extend(
{},
domainObject.telemetry,
METADATA_BY_TYPE[domainObject.type]
);
{},
domainObject.telemetry,
METADATA_BY_TYPE[domainObject.type]
);
};
return GeneratorMetadataProvider;

View File

@ -37,25 +37,25 @@ define([
},
LIMITS = {
rh: {
cssClass: "is-limit--upr is-limit--red",
cssClass: "s-limit-upr s-limit-red",
low: RED,
high: Number.POSITIVE_INFINITY,
name: "Red High"
},
rl: {
cssClass: "is-limit--lwr is-limit--red",
cssClass: "s-limit-lwr s-limit-red",
high: -RED,
low: Number.NEGATIVE_INFINITY,
name: "Red Low"
},
yh: {
cssClass: "is-limit--upr is-limit--yellow",
cssClass: "s-limit-upr s-limit-yellow",
low: YELLOW,
high: RED,
name: "Yellow High"
},
yl: {
cssClass: "is-limit--lwr is-limit--yellow",
cssClass: "s-limit-lwr s-limit-yellow",
low: -RED,
high: -YELLOW,
name: "Yellow Low"

View File

@ -43,14 +43,15 @@ define([
form: [
{
name: "State Duration (seconds)",
control: "numberfield",
control: "textfield",
cssClass: "l-input-sm l-numeric",
key: "duration",
required: true,
property: [
"telemetry",
"duration"
]
],
pattern: "^\\d*(\\.\\d*)?$"
}
],
initialize: function (object) {

View File

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

View File

@ -1,9 +1,9 @@
<span class="h-indicator" ng-controller="NotificationLaunchController">
<!-- DO NOT ADD SPACES BETWEEN THE SPANS - IT ADDS WHITE SPACE!! -->
<div class="c-indicator c-indicator--clickable icon-bell s-status-available"><span class="label c-indicator__label">
<button ng-click="newInfo()">Success</button>
<button ng-click="newError()">Error</button>
<button ng-click="newAlert()">Alert</button>
<button ng-click="newProgress()">Progress</button>
<div class="ls-indicator icon-bell s-status-available"><span class="label">
<a ng-click="newInfo()">Success</a>
<a ng-click="newError()">Error</a>
<a ng-click="newAlert()">Alert</a>
<a ng-click="newProgress()">Progress</a>
</span></div>
</span>

View File

@ -27,7 +27,7 @@
<meta name="apple-mobile-web-app-capable" content="yes">
<title></title>
<script src="dist/openmct.js"></script>
<link rel="stylesheet" href="dist/styles/openmct.css">
<link rel="stylesheet" href="dist/openmct.css">
<link rel="icon" type="image/png" href="dist/favicons/favicon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="dist/favicons/favicon-96x96.png" sizes="96x96">
<link rel="icon" type="image/png" href="dist/favicons/favicon-16x16.png" sizes="16x16">
@ -36,9 +36,7 @@
<body>
</body>
<script>
const FIVE_MINUTES = 5 * 60 * 1000;
const THIRTY_MINUTES = 30 * 60 * 1000;
var THIRTY_MINUTES = 30 * 60 * 1000;
[
'example/eventGenerator',
'example/styleguide'
@ -50,12 +48,11 @@
openmct.install(openmct.plugins.Generator());
openmct.install(openmct.plugins.ExampleImagery());
openmct.install(openmct.plugins.UTCTimeSystem());
openmct.install(openmct.plugins.ImportExport());
openmct.install(openmct.plugins.FixedView());
openmct.install(openmct.plugins.AutoflowView({
type: "telemetry.panel"
}));
openmct.install(openmct.plugins.DisplayLayout({
showAsView: ['summary-widget', 'example.imagery']
}));
openmct.install(openmct.plugins.Conductor({
menuOptions: [
{
@ -71,17 +68,217 @@
timeSystem: 'utc',
clock: 'local',
clockOffsets: {
start: - THIRTY_MINUTES,
end: FIVE_MINUTES
start: -25 * 60 * 1000,
end: 5 * 60 * 1000
}
}
]
}));
openmct.install(openmct.plugins.SummaryWidget());
openmct.install(openmct.plugins.Notebook());
openmct.install(openmct.plugins.Filters(['table', 'telemetry.plot.overlay']));
openmct.install(openmct.plugins.ObjectMigration());
openmct.install(openmct.plugins.ClearData(['table', 'telemetry.plot.overlay', 'telemetry.plot.stacked']));
openmct.install(openmct.plugins.FolderView());
openmct.install(openmct.plugins.Tabs());
openmct.install(openmct.plugins.FlexibleLayout());
openmct.time.clock('local', {start: -THIRTY_MINUTES, end: 0});
openmct.time.timeSystem('utc');
openmct.start();
// openmct.toolbars.addProvider({
// name: "Testing Toolbar",
// key: "testing",
// description: "a mock toolbar that exercises all controls",
// forSelection: function (selection) {
// return true; // always applies.
// },
// toolbar: function (selection) {
// return [
// {
// control: 'menu',
// icon: 'icon-plus',
// label: 'Add',
// options: [
// { name: 'Box', class: 'icon-box', title: 'Add Box' },
// { name: 'Line', class: 'icon-line-horz', title: 'Add Line' },
// { name: 'Text', class: 'icon-font', title: 'Add Text' },
// { name: 'Image', class: 'icon-image', title: 'Add Image' }
// ]
// },
// {
// control: 'separator'
// },
// {
// control: 'color-picker',
// icon: 'icon-paint-bucket',
// value: '#33ff00',
// },
// {
// control: 'color-picker',
// icon: 'icon-pencil',
// value: '#ffffff',
// },
// {
// control: 'color-picker',
// icon: 'icon-font',
// value: '#333333',
// },
//
// {
// control: 'separator'
// },
// {
// control: 'select-menu',
// value: 11,
// options: [
// { value: 9, name: '9 px' },
// { value: 10, name: '10 px' },
// { value: 11, name: '11 px' },
// { value: 12, name: '12 px' },
// { value: 13, name: '13 px' },
// { value: 14, name: '14 px' },
// { value: 16, name: '16 px' },
// { value: 18, name: '18 px' },
// { value: 20, name: '20 px' },
// { value: 24, name: '24 px' },
// { value: 28, name: '28 px' },
// { value: 32, name: '32 px' },
// { value: 40, name: '40 px' },
// { value: 48, name: '48 px' },
// { value: 56, name: '56 px' },
// { value: 64, name: '64 px' },
// { value: 72, name: '72 px' },
// { value: 80, name: '80 px' },
// { value: 88, name: '88 px' },
// { value: 96, name: '96 px' },
// { value: 128, name: '128 px' },
// { value: 160, name: '160 px' }
// ]
// },
//
// {
// control: 'separator'
// },
// {
// control: 'menu',
// icon: 'icon-layers',
// options: [
// { name: 'Move to top', class: 'icon-arrow-double-up', title: 'Move to top' },
// { name: 'Move up', class: 'icon-arrow-up', title: 'Move up' },
// { name: 'Move down', class: 'icon-arrow-down', title: 'Move down' },
// { name: 'Move to bottom', class: 'icon-arrow-double-down', title: 'Move to bottom' }
// ]
// },
//
// {
// control: 'separator'
// },
// {
// control: 'button',
// icon: 'icon-gear'
// },
//
// {
// control: 'separator'
// },
// {
// control: 'input',
// type: 'number',
// label: 'X',
// value: 1,
// title: 'X position'
// },
// {
// control: 'input',
// type: 'number',
// label: 'Y',
// value: 2,
// title: 'Y position'
// },
// {
// control: 'input',
// type: 'number',
// label: 'W',
// value: 3,
// title: 'Width'
// },
// {
// control: 'input',
// type: 'number',
// label: 'H',
// value: 4,
// title: 'Height'
// },
//
// {
// control: 'separator'
// },
// {
// control: 'button',
// icon: 'icon-trash',
// label: 'delete',
// modifier: 'caution'
// },
//
// {
// control: 'separator'
// },
// {
// control: 'checkbox',
// name: 'this is a checkbox',
// },
// {
// control: 'separator'
// },
// {
// control: 'toggle-button',
// title: 'Toggle Frame',
// property: 'hideFrame',
// value: false,
// options: [
// {
// value: true,
// icon: 'icon-frame-hide'
// },
// {
// value: false,
// icon: 'icon-frame-show'
// }
// ]
// },
// {
// control: 'toggle-button',
// title: 'Snap to grid',
// property: 'snapToGrid',
// value: true,
// options: [
// {
// value: true,
// icon: 'icon-grid-snap-to'
// },
// {
// value: false,
// icon: 'icon-grid-snap-no'
// }
// ]
// },
// {
// control: 'toggle-button',
// title: 'Toggle label',
// property: 'showLabel',
// value: true,
// options: [
// {
// value: true,
// icon: 'icon-two-parts-both'
// },
// {
// value: false,
// icon: 'icon-two-parts-one-only'
// }
// ]
// }
// ];
// }
// });
</script>
</html>

View File

@ -4,7 +4,6 @@
"description": "The Open MCT core platform",
"dependencies": {},
"devDependencies": {
"acorn": "6.2.0",
"angular": "1.4.14",
"angular-route": "1.4.14",
"babel-eslint": "8.2.6",
@ -26,7 +25,7 @@
"eventemitter3": "^1.2.0",
"exports-loader": "^0.7.0",
"express": "^4.13.1",
"fast-sass-loader": "1.4.6",
"fast-sass-loader": "^1.4.5",
"file-loader": "^1.1.11",
"file-saver": "^1.3.8",
"git-rev-sync": "^1.4.0",
@ -56,9 +55,10 @@
"node-bourbon": "^4.2.3",
"node-sass": "^4.9.2",
"painterro": "^0.2.65",
"printj": "^1.2.1",
"printj": "^1.1.0",
"raw-loader": "^0.5.1",
"request": "^2.69.0",
"screenfull": "^3.3.2",
"split": "^1.0.0",
"style-loader": "^0.21.0",
"v8-compile-cache": "^1.1.0",

View File

@ -31,6 +31,8 @@ define([
"./src/navigation/NavigateAction",
"./src/navigation/OrphanNavigationHandler",
"./src/windowing/NewTabAction",
"./src/windowing/FullscreenAction",
"./src/windowing/WindowTitler",
"./res/templates/browse.html",
"./res/templates/browse-object.html",
"./res/templates/browse/object-header.html",
@ -51,6 +53,8 @@ define([
NavigateAction,
OrphanNavigationHandler,
NewTabAction,
FullscreenAction,
WindowTitler,
browseTemplate,
browseObjectTemplate,
objectHeaderTemplate,
@ -221,9 +225,24 @@ define([
"group": "windowing",
"cssClass": "icon-new-window",
"priority": "preferred"
},
{
"key": "fullscreen",
"implementation": FullscreenAction,
"category": "view-control",
"group": "windowing",
"priority": "default"
}
],
"runs": [
{
"implementation": WindowTitler,
"depends": [
"navigationService",
"$rootScope",
"$document"
]
},
{
"implementation": OrphanNavigationHandler,
"depends": [
@ -246,6 +265,18 @@ define([
key: "inspectorRegion",
template: inspectorRegionTemplate
}
],
"licenses": [
{
"name": "screenfull.js",
"version": "1.2.0",
"description": "Wrapper for cross-browser usage of fullscreen API",
"author": "Sindre Sorhus",
"website": "https://github.com/sindresorhus/screenfull.js/",
"copyright": "Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)",
"license": "license-mit",
"link": "https://github.com/sindresorhus/screenfull.js/blob/gh-pages/license"
}
]
}
});

View File

@ -0,0 +1,64 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/**
* Module defining FullscreenAction. Created by vwoeltje on 11/18/14.
*/
define(
["screenfull"],
function (screenfull) {
var ENTER_FULLSCREEN = "Enter full screen mode",
EXIT_FULLSCREEN = "Exit full screen mode";
/**
* The fullscreen action toggles between fullscreen display
* and regular in-window display.
* @memberof platform/commonUI/browse
* @constructor
* @implements {Action}
*/
function FullscreenAction(context) {
this.context = context;
}
FullscreenAction.prototype.perform = function () {
screenfull.toggle();
};
FullscreenAction.prototype.getMetadata = function () {
// We override getMetadata, because the icon cssClass and
// description need to be determined at run-time
// based on whether or not we are currently
// full screen.
var metadata = Object.create(FullscreenAction);
metadata.cssClass = screenfull.isFullscreen ? "icon-fullscreen-expand" : "icon-fullscreen-collapse";
metadata.description = screenfull.isFullscreen ?
EXIT_FULLSCREEN : ENTER_FULLSCREEN;
metadata.group = "windowing";
metadata.context = this.context;
return metadata;
};
return FullscreenAction;
}
);

View File

@ -0,0 +1,51 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
[],
function () {
/**
* Updates the title of the current window to reflect the name
* of the currently navigated-to domain object.
* @memberof platform/commonUI/browse
* @constructor
*/
function WindowTitler(navigationService, $rootScope, $document) {
// Look up name of the navigated domain object...
function getNavigatedObjectName() {
var navigatedObject = navigationService.getNavigation();
return navigatedObject && navigatedObject.getModel().name;
}
// Set the window title...
function setTitle(name) {
$document[0].title = name;
}
// Watch the former, and invoke the latter
$rootScope.$watch(getNavigatedObjectName, setTitle);
}
return WindowTitler;
}
);

View File

@ -0,0 +1,59 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/**
* MCTRepresentationSpec. Created by vwoeltje on 11/6/14.
*/
define(
["../../src/windowing/FullscreenAction", "screenfull"],
function (FullscreenAction, screenfull) {
describe("The fullscreen action", function () {
var action,
oldToggle;
beforeEach(function () {
// Screenfull is not shimmed or injected, so
// we need to spy on it in the global scope.
oldToggle = screenfull.toggle;
screenfull.toggle = jasmine.createSpy("toggle");
action = new FullscreenAction({});
});
afterEach(function () {
screenfull.toggle = oldToggle;
});
it("toggles fullscreen mode when performed", function () {
action.perform();
expect(screenfull.toggle).toHaveBeenCalled();
});
it("provides displayable metadata", function () {
expect(action.getMetadata().cssClass).toBeDefined();
});
});
}
);

View File

@ -0,0 +1,78 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/**
* WindowTitlerSpec. Created by vwoeltje on 11/6/14.
*/
define(
["../../src/windowing/WindowTitler"],
function (WindowTitler) {
describe("The window titler", function () {
var mockNavigationService,
mockRootScope,
mockDocument,
mockDomainObject,
titler; // eslint-disable-line
beforeEach(function () {
mockNavigationService = jasmine.createSpyObj(
'navigationService',
['getNavigation']
);
mockRootScope = jasmine.createSpyObj(
'$rootScope',
['$watch']
);
mockDomainObject = jasmine.createSpyObj(
'domainObject',
['getModel']
);
mockDocument = [{}];
mockDomainObject.getModel.and.returnValue({ name: 'Test name' });
mockNavigationService.getNavigation.and.returnValue(mockDomainObject);
titler = new WindowTitler(
mockNavigationService,
mockRootScope,
mockDocument
);
});
it("listens for changes to the name of the navigated object", function () {
expect(mockRootScope.$watch).toHaveBeenCalledWith(
jasmine.any(Function),
jasmine.any(Function)
);
expect(mockRootScope.$watch.calls.mostRecent().args[0]())
.toEqual('Test name');
});
it("sets the title to the name of the navigated object", function () {
mockRootScope.$watch.calls.mostRecent().args[1]("Some name");
expect(mockDocument[0].title).toEqual("Some name");
});
});
}
);

View File

@ -28,7 +28,6 @@ define([
"./res/templates/dialog.html",
"./res/templates/overlay-blocking-message.html",
"./res/templates/message.html",
"./res/templates/notification-message.html",
"./res/templates/overlay-message-list.html",
"./res/templates/overlay.html",
'legacyRegistry'
@ -40,7 +39,6 @@ define([
dialogTemplate,
overlayBlockingMessageTemplate,
messageTemplate,
notificationMessageTemplate,
overlayMessageListTemplate,
overlayTemplate,
legacyRegistry
@ -65,8 +63,7 @@ define([
"depends": [
"$document",
"$compile",
"$rootScope",
"$timeout"
"$rootScope"
]
}
],
@ -91,10 +88,6 @@ define([
"key": "message",
"template": messageTemplate
},
{
"key": "notification-message",
"template": notificationMessageTemplate
},
{
"key": "overlay-message-list",
"template": overlayMessageListTemplate

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -44,9 +44,8 @@ define(
* @memberof platform/commonUI/dialog
* @constructor
*/
function OverlayService($document, $compile, $rootScope, $timeout) {
function OverlayService($document, $compile, $rootScope) {
this.$compile = $compile;
this.$timeout = $timeout;
// Don't include $document and $rootScope directly;
// avoids https://docs.angularjs.org/error/ng/cpws
@ -94,14 +93,9 @@ define(
scope.key = key;
scope.typeClass = typeClass || 't-dialog';
this.$timeout(() => {
// Create the overlay element and add it to the document's body
element = this.$compile(TEMPLATE)(scope);
// Append so that most recent dialog is last in DOM. This means the most recent dialog will be on top when
// multiple overlays with the same z-index are active.
this.findBody().append(element);
});
// Create the overlay element and add it to the document's body
element = this.$compile(TEMPLATE)(scope);
this.findBody().prepend(element);
return {
dismiss: dismiss

View File

@ -35,20 +35,16 @@ define(
mockTemplate,
mockElement,
mockScope,
mockTimeout,
overlayService;
beforeEach(function () {
mockDocument = jasmine.createSpyObj("$document", ["find"]);
mockCompile = jasmine.createSpy("$compile");
mockRootScope = jasmine.createSpyObj("$rootScope", ["$new"]);
mockBody = jasmine.createSpyObj("body", ["append"]);
mockBody = jasmine.createSpyObj("body", ["prepend"]);
mockTemplate = jasmine.createSpy("template");
mockElement = jasmine.createSpyObj("element", ["remove"]);
mockScope = jasmine.createSpyObj("scope", ["$destroy"]);
mockTimeout = function (callback) {
callback();
}
mockDocument.find.and.returnValue(mockBody);
mockCompile.and.returnValue(mockTemplate);
@ -58,8 +54,7 @@ define(
overlayService = new OverlayService(
mockDocument,
mockCompile,
mockRootScope,
mockTimeout
mockRootScope
);
});
@ -72,7 +67,7 @@ define(
it("adds the templated element to the body", function () {
overlayService.createOverlay("test", {});
expect(mockBody.append).toHaveBeenCalledWith(mockElement);
expect(mockBody.prepend).toHaveBeenCalledWith(mockElement);
});
it("places the provided model/key in its template's scope", function () {

View File

@ -32,7 +32,11 @@ define([
"./src/actions/SaveAndStopEditingAction",
"./src/actions/SaveAsAction",
"./src/actions/CancelAction",
"./src/policies/EditActionPolicy",
"./src/policies/EditPersistableObjectsPolicy",
"./src/policies/EditableLinkPolicy",
"./src/policies/EditableMovePolicy",
"./src/policies/EditContextualActionPolicy",
"./src/representers/EditRepresenter",
"./src/capabilities/EditorCapability",
"./src/capabilities/TransactionCapabilityDecorator",
@ -63,7 +67,11 @@ define([
SaveAndStopEditingAction,
SaveAsAction,
CancelAction,
EditActionPolicy,
EditPersistableObjectsPolicy,
EditableLinkPolicy,
EditableMovePolicy,
EditContextualActionPolicy,
EditRepresenter,
EditorCapability,
TransactionCapabilityDecorator,
@ -160,13 +168,13 @@ define([
},
{
"key": "remove",
"category": "legacy",
"category": "contextual",
"implementation": RemoveAction,
"cssClass": "icon-trash",
"name": "Remove",
"description": "Remove this object from its containing object.",
"depends": [
"openmct",
"dialogService",
"navigationService"
]
},
@ -223,11 +231,28 @@ define([
}
],
"policies": [
{
"category": "action",
"implementation": EditActionPolicy
},
{
"category": "action",
"implementation": EditPersistableObjectsPolicy,
"depends": ["openmct"]
},
{
"category": "action",
"implementation": EditContextualActionPolicy,
"depends": ["navigationService", "editModeBlacklist", "nonEditContextBlacklist"]
},
{
"category": "action",
"implementation": EditableMovePolicy
},
{
"category": "action",
"implementation": EditableLinkPolicy
},
{
"implementation": CreationPolicy,
"category": "creation"
@ -324,6 +349,16 @@ define([
]
}
],
"constants": [
{
"key": "editModeBlacklist",
"value": ["copy", "follow", "link", "locate"]
},
{
"key": "nonEditContextBlacklist",
"value": ["copy", "follow", "properties", "move", "link", "remove", "locate"]
}
],
"capabilities": [
{
"key": "editor",

View File

@ -49,7 +49,7 @@ define(
name: "Properties",
rows: this.properties.map(function (property, index) {
// Property definition is same as form row definition
var row = JSON.parse(JSON.stringify(property.getDefinition()));
var row = Object.create(property.getDefinition());
row.key = index;
return row;
}).filter(function (row) {

View File

@ -23,7 +23,11 @@
/**
* Module defining RemoveAction. Created by vwoeltje on 11/17/14.
*/
define([], function () {
define([
'./RemoveDialog'
], function (
RemoveDialog
) {
/**
* Construct an action which will remove the provided object manifestation.
@ -38,9 +42,9 @@ define([], function () {
* @constructor
* @implements {Action}
*/
function RemoveAction(openmct, navigationService, context) {
function RemoveAction(dialogService, navigationService, context) {
this.domainObject = (context || {}).domainObject;
this.openmct = openmct;
this.dialogService = dialogService;
this.navigationService = navigationService;
}
@ -49,6 +53,7 @@ define([], function () {
*/
RemoveAction.prototype.perform = function () {
var dialog,
dialogService = this.dialogService,
domainObject = this.domainObject,
navigationService = this.navigationService;
/*
@ -99,18 +104,23 @@ define([], function () {
* capability. Based on object's location and selected object's location
* user may be navigated to existing parent object
*/
function removeFromContext() {
var contextCapability = domainObject.getCapability('context'),
function removeFromContext(object) {
var contextCapability = object.getCapability('context'),
parent = contextCapability.getParent();
// If currently within path of removed object(s),
// navigates to existing object up tree
checkObjectNavigation(domainObject, parent);
checkObjectNavigation(object, parent);
return parent.useCapability('mutation', doMutate);
}
removeFromContext();
/*
* Pass in the function to remove the domain object so it can be
* associated with an 'OK' button press
*/
dialog = new RemoveDialog(dialogService, domainObject, removeFromContext);
dialog.show();
};
// Object needs to have a parent for Remove to be applicable

View File

@ -0,0 +1,77 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define([], function () {
/**
* @callback removeCallback
* @param {DomainObject} domainObject the domain object to be removed
*/
/**
* Construct a new Remove dialog.
*
* @param {DialogService} dialogService the service that shows the dialog
* @param {DomainObject} domainObject the domain object to be removed
* @param {removeCallback} removeCallback callback that handles removal of the domain object
* @memberof platform/commonUI/edit
* @constructor
*/
function RemoveDialog(dialogService, domainObject, removeCallback) {
this.dialogService = dialogService;
this.domainObject = domainObject;
this.removeCallback = removeCallback;
}
/**
* Display a dialog to confirm the removal of a domain object.
*/
RemoveDialog.prototype.show = function () {
var dialog,
domainObject = this.domainObject,
removeCallback = this.removeCallback,
model = {
title: 'Remove ' + domainObject.getModel().name,
actionText: 'Warning! This action will permanently remove this object. Are you sure you want to continue?',
severity: 'alert',
primaryOption: {
label: 'OK',
callback: function () {
removeCallback(domainObject);
dialog.dismiss();
}
},
options: [
{
label: 'Cancel',
callback: function () {
dialog.dismiss();
}
}
]
};
setTimeout(() => this.removeCallback(domainObject));
};
return RemoveDialog;
});

View File

@ -92,7 +92,16 @@ function (
* @memberof platform/commonUI/edit.SaveAction#
*/
SaveAsAction.prototype.perform = function () {
return this.save();
// Discard the current root view (which will be the editing
// UI, which will have been pushed atop the Browse UI.)
function returnToBrowse(object) {
if (object) {
object.getCapability("action").perform("navigate");
}
return object;
}
return this.save().then(returnToBrowse);
};
/**
@ -160,22 +169,15 @@ function (
}
function saveAfterClone(clonedObject) {
return this.openmct.editor.save().then(() => {
// Force mutation for search indexing
return clonedObject;
})
return domainObject.getCapability("editor").save()
.then(resolveWith(clonedObject));
}
function finishEditing(clonedObject) {
return fetchObject(clonedObject.getId())
}
function indexForSearch(savedObject) {
savedObject.useCapability('mutation', (model) => {
return model;
});
return savedObject;
return domainObject.getCapability("editor").finish()
.then(function () {
return fetchObject(clonedObject.getId());
});
}
function onSuccess(object) {
@ -188,7 +190,7 @@ function (
if (reason !== "user canceled") {
self.notificationService.error("Save Failed");
}
throw reason;
return false;
}
return getParent(domainObject)
@ -199,7 +201,6 @@ function (
.then(undirtyOriginals)
.then(saveAfterClone)
.then(finishEditing)
.then(indexForSearch)
.then(hideBlockingDialog)
.then(onSuccess)
.catch(onFailure);

View File

@ -64,6 +64,7 @@ define(
* @returns boolean
*/
EditorCapability.prototype.inEditContext = function () {
console.warn('DEPRECATION WARNING: isEditing checks must be done via openmct.editor.');
return this.openmct.editor.isEditing();
};
@ -73,6 +74,7 @@ define(
* @returns {*}
*/
EditorCapability.prototype.isEditContextRoot = function () {
console.warn('DEPRECATION WARNING: isEditing checks must be done via openmct.editor.');
return this.openmct.editor.isEditing();
};

View File

@ -65,40 +65,24 @@ define(
CreateAction.prototype.perform = function () {
var newModel = this.type.getInitialModel(),
openmct = this.openmct,
newObject;
newObject,
editAction;
function onSave() {
openmct.editor.save();
}
function onCancel() {
openmct.editor.cancel();
}
function isFirstViewEditable(domainObject) {
let firstView = openmct.objectViews.get(domainObject)[0];
return firstView && firstView.canEdit && firstView.canEdit(domainObject);
}
function navigateAndEdit(object) {
let objectPath = object.getCapability('context').getPath(),
url = '#/browse/' + objectPath
.slice(1)
.map(function (o) {
return o && openmct.objects.makeKeyString(o.getId());
})
.join('/');
window.location.href = url;
if (isFirstViewEditable(object.useCapability('adapter'))) {
openmct.editor.edit();
}
}
newModel.type = this.type.getKey();
newModel.location = this.parent.getId();
newObject = this.parent.useCapability('instantiation', newModel);
openmct.editor.edit();
newObject.getCapability("action").perform("save-as").then(navigateAndEdit, onCancel);
editAction = newObject.getCapability("action").getActions("edit")[0];
newObject.getCapability("action").perform("save-as").then(onSave, onCancel);
// TODO: support editing object without saving object first.
// Which means we have to toggle createwizard afterwards. For now,
// We will disable this.

View File

@ -66,7 +66,7 @@ define(
name: "Properties",
rows: this.properties.map(function (property, index) {
// Property definition is same as form row definition
var row = JSON.parse(JSON.stringify(property.getDefinition()));
var row = Object.create(property.getDefinition());
// Use index as the key into the formValue;
// this correlates to the indexing provided by

View File

@ -0,0 +1,111 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
[],
function () {
/**
* Policy controlling when the `edit` and/or `properties` actions
* can appear as applicable actions of the `view-control` category
* (shown as buttons in the top-right of browse mode.)
* @memberof platform/commonUI/edit
* @constructor
* @implements {Policy.<Action, ActionContext>}
*/
function EditActionPolicy(policyService) {
this.policyService = policyService;
}
/**
* Get a count of views which are not flagged as non-editable.
* @private
*/
EditActionPolicy.prototype.countEditableViews = function (context) {
var domainObject = context.domainObject,
count = 0,
type, views;
if (!domainObject) {
return count;
}
type = domainObject.getCapability('type');
views = domainObject.useCapability('view');
// A view is editable unless explicitly flagged as not
(views || []).forEach(function (view) {
if (isEditable(view) ||
(view.key === 'plot' && type.getKey() === 'telemetry.panel') ||
(view.key === 'table' && type.getKey() === 'table') ||
(view.key === 'rt-table' && type.getKey() === 'rttable')
) {
count++;
}
});
function isEditable(view) {
if (typeof view.editable === Function) {
return view.editable(domainObject.useCapability('adapter'));
} else {
return view.editable === true;
}
}
return count;
};
/**
* Checks whether the domain object is currently being edited. If
* so, the edit action is not applicable.
* @param context
* @returns {*|boolean}
*/
function isEditing(context) {
var domainObject = (context || {}).domainObject;
return domainObject &&
domainObject.hasCapability('editor') &&
domainObject.getCapability('editor').isEditContextRoot();
}
EditActionPolicy.prototype.allow = function (action, context) {
var key = action.getMetadata().key,
category = (context || {}).category;
// Restrict 'edit' to cases where there are editable
// views (similarly, restrict 'properties' to when
// the converse is true), and where the domain object is not
// already being edited.
if (key === 'edit') {
return this.countEditableViews(context) > 0 && !isEditing(context);
} else if (key === 'properties' && category === 'view-control') {
return this.countEditableViews(context) < 1 && !isEditing(context);
}
// Like all policies, allow by default.
return true;
};
return EditActionPolicy;
}
);

View File

@ -0,0 +1,75 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
[],
function () {
/**
* Policy controlling whether the context menu is visible when
* objects are being edited
* @param navigationService
* @param editModeBlacklist A blacklist of actions disallowed from
* context menu when navigated object is being edited
* @param nonEditContextBlacklist A blacklist of actions disallowed
* from context menu of non-editable objects, when navigated object
* is being edited
* @constructor
* @param editModeBlacklist A blacklist of actions disallowed from
* context menu when navigated object is being edited
* @param nonEditContextBlacklist A blacklist of actions disallowed
* from context menu of non-editable objects, when navigated object
* @implements {Policy.<Action, ActionContext>}
*/
function EditContextualActionPolicy(navigationService, editModeBlacklist, nonEditContextBlacklist) {
this.navigationService = navigationService;
//The list of objects disallowed on target object when in edit mode
this.editModeBlacklist = editModeBlacklist;
//The list of objects disallowed on target object that is not in
// edit mode (ie. the context menu in the tree on the LHS).
this.nonEditContextBlacklist = nonEditContextBlacklist;
}
EditContextualActionPolicy.prototype.allow = function (action, context) {
var selectedObject = context.domainObject,
navigatedObject = this.navigationService.getNavigation(),
actionMetadata = action.getMetadata ? action.getMetadata() : {};
// FIXME: need to restore support for changing contextual actions
// based on edit mode.
// if (navigatedObject.hasCapability("editor") && navigatedObject.getCapability("editor").isEditContextRoot()) {
// if (selectedObject.hasCapability("editor") && selectedObject.getCapability("editor").inEditContext()) {
// return this.editModeBlacklist.indexOf(actionMetadata.key) === -1;
// } else {
// //Target is in the context menu
// return this.nonEditContextBlacklist.indexOf(actionMetadata.key) === -1;
// }
// } else {
// return true;
// }
return true;
};
return EditContextualActionPolicy;
}
);

View File

@ -0,0 +1,51 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define([], function () {
/**
* Policy suppressing links when the linked-to domain object is in
* edit mode. Domain objects being edited may not have been persisted,
* so creating links to these can result in inconsistent state.
*
* @memberof platform/commonUI/edit
* @constructor
* @implements {Policy.<View, DomainObject>}
*/
function EditableLinkPolicy() {
}
EditableLinkPolicy.prototype.allow = function (action, context) {
var key = action.getMetadata().key,
object;
if (key === 'link') {
object = context.selectedObject || context.domainObject;
return !(object.hasCapability("editor") && object.getCapability("editor").inEditContext());
}
// Like all policies, allow by default.
return true;
};
return EditableLinkPolicy;
});

View File

@ -0,0 +1,52 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define([], function () {
/**
* Policy suppressing move actions among editable and non-editable
* domain objects.
* @memberof platform/commonUI/edit
* @constructor
* @implements {Policy.<View, DomainObject>}
*/
function EditableMovePolicy() {
}
EditableMovePolicy.prototype.allow = function (action, context) {
var domainObject = context.domainObject,
selectedObject = context.selectedObject,
key = action.getMetadata().key,
isDomainObjectEditing = domainObject.hasCapability('editor') &&
domainObject.getCapability('editor').inEditContext();
if (key === 'move' && isDomainObjectEditing) {
return !!selectedObject && selectedObject.hasCapability('editor') &&
selectedObject.getCapability('editor').inEditContext();
}
// Like all policies, allow by default.
return true;
};
return EditableMovePolicy;
});

View File

@ -0,0 +1,49 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
[],
function () {
/**
* Policy controlling which views should be visible in Edit mode.
* @memberof platform/commonUI/edit
* @constructor
* @implements {Policy.<View, DomainObject>}
*/
function EditableViewPolicy() {
}
EditableViewPolicy.prototype.allow = function (view, domainObject) {
// If a view is flagged as non-editable, only allow it
// while we're not in Edit mode.
if ((view || {}).editable === false) {
return !(domainObject.hasCapability('editor') && domainObject.getCapability('editor').inEditContext());
}
// Like all policies, allow by default.
return true;
};
return EditableViewPolicy;
}
);

View File

@ -77,19 +77,14 @@ define([], function () {
return promiseFn().then(nextFn);
};
}
/**
* Clear any existing persistence calls for object with given ID. This ensures only the most recent persistence
* call is executed. This should prevent stale objects being persisted and overwriting fresh ones.
*/
if (this.isScheduled(id)) {
this.clearTransactionsFor(id);
}
this.clearTransactionFns[id] =
this.transactionService.addToTransaction(
chain(onCommit, release),
chain(onCancel, release)
);
if (!this.isScheduled(id)) {
this.clearTransactionFns[id] =
this.transactionService.addToTransaction(
chain(onCommit, release),
chain(onCancel, release)
);
}
};
/**

View File

@ -29,7 +29,7 @@ define(
actionContext,
capabilities,
mockContext,
mockOverlayAPI,
mockDialogService,
mockDomainObject,
mockMutation,
mockNavigationService,
@ -68,9 +68,9 @@ define(
}
};
mockOverlayAPI = jasmine.createSpyObj(
"overlayAPI",
["dialog"]
mockDialogService = jasmine.createSpyObj(
"dialogService",
["showBlockingMessage"]
);
mockNavigationService = jasmine.createSpyObj(
@ -96,7 +96,7 @@ define(
actionContext = { domainObject: mockDomainObject };
action = new RemoveAction({overlays: mockOverlayAPI}, mockNavigationService, actionContext);
action = new RemoveAction(mockDialogService, mockNavigationService, actionContext);
});
it("only applies to objects with parents", function () {
@ -118,7 +118,7 @@ define(
action.perform();
expect(mockOverlayAPI.dialog).toHaveBeenCalled();
expect(mockDialogService.showBlockingMessage).toHaveBeenCalled();
// Also check that no mutation happens at this point
expect(mockParent.useCapability).not.toHaveBeenCalledWith("mutation", jasmine.any(Function));
@ -158,13 +158,13 @@ define(
mockGrandchildContext = jasmine.createSpyObj("context", ["getParent"]);
mockRootContext = jasmine.createSpyObj("context", ["getParent"]);
mockOverlayAPI.dialog.and.returnValue(mockDialogHandle);
mockDialogService.showBlockingMessage.and.returnValue(mockDialogHandle);
});
it("mutates the parent when performed", function () {
action.perform();
mockOverlayAPI.dialog.calls.mostRecent().args[0]
.buttons[0].callback();
mockDialogService.showBlockingMessage.calls.mostRecent().args[0]
.primaryOption.callback();
expect(mockMutation.invoke)
.toHaveBeenCalledWith(jasmine.any(Function));
@ -174,8 +174,8 @@ define(
var mutator, result;
action.perform();
mockOverlayAPI.dialog.calls.mostRecent().args[0]
.buttons[0].callback();
mockDialogService.showBlockingMessage.calls.mostRecent().args[0]
.primaryOption.callback();
mutator = mockMutation.invoke.calls.mostRecent().args[0];
result = mutator(model);
@ -212,8 +212,8 @@ define(
mockType.hasFeature.and.returnValue(true);
action.perform();
mockOverlayAPI.dialog.calls.mostRecent().args[0]
.buttons[0].callback();
mockDialogService.showBlockingMessage.calls.mostRecent().args[0]
.primaryOption.callback();
// Expects navigation to parent of domainObject (removed object)
expect(mockNavigationService.setNavigation).toHaveBeenCalledWith(mockParent);
@ -242,8 +242,8 @@ define(
mockType.hasFeature.and.returnValue(true);
action.perform();
mockOverlayAPI.dialog.calls.mostRecent().args[0]
.buttons[0].callback();
mockDialogService.showBlockingMessage.calls.mostRecent().args[0]
.primaryOption.callback();
// Expects no navigation to occur
expect(mockNavigationService.setNavigation).not.toHaveBeenCalled();

View File

@ -0,0 +1,138 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
["../../src/policies/EditActionPolicy"],
function (EditActionPolicy) {
describe("The Edit action policy", function () {
var editableView,
nonEditableView,
testViews,
testContext,
mockDomainObject,
mockEditAction,
mockPropertiesAction,
mockTypeCapability,
mockEditorCapability,
capabilities,
plotView,
policy;
beforeEach(function () {
mockDomainObject = jasmine.createSpyObj(
'domainObject',
[
'useCapability',
'hasCapability',
'getCapability'
]
);
mockEditorCapability = jasmine.createSpyObj('editorCapability', ['isEditContextRoot']);
mockTypeCapability = jasmine.createSpyObj('type', ['getKey']);
capabilities = {
'editor': mockEditorCapability,
'type': mockTypeCapability
};
mockEditAction = jasmine.createSpyObj('edit', ['getMetadata']);
mockPropertiesAction = jasmine.createSpyObj('edit', ['getMetadata']);
mockDomainObject.getCapability.and.callFake(function (capability) {
return capabilities[capability];
});
mockDomainObject.hasCapability.and.callFake(function (capability) {
return !!capabilities[capability];
});
editableView = { editable: true };
nonEditableView = { editable: false };
plotView = { key: "plot", editable: false };
testViews = [];
mockDomainObject.useCapability.and.callFake(function (c) {
// Provide test views, only for the view capability
return c === 'view' && testViews;
});
mockEditAction.getMetadata.and.returnValue({ key: 'edit' });
mockPropertiesAction.getMetadata.and.returnValue({ key: 'properties' });
testContext = {
domainObject: mockDomainObject,
category: 'view-control'
};
policy = new EditActionPolicy();
});
it("allows the edit action when there are editable views", function () {
testViews = [editableView];
expect(policy.allow(mockEditAction, testContext)).toBe(true);
});
it("allows the edit properties action when there are no editable views", function () {
testViews = [nonEditableView, nonEditableView];
expect(policy.allow(mockPropertiesAction, testContext)).toBe(true);
});
it("disallows the edit action when there are no editable views", function () {
testViews = [nonEditableView, nonEditableView];
expect(policy.allow(mockEditAction, testContext)).toBe(false);
});
it("disallows the edit properties action when there are" +
" editable views", function () {
testViews = [editableView];
expect(policy.allow(mockPropertiesAction, testContext)).toBe(false);
});
it("disallows the edit action when object is already being" +
" edited", function () {
testViews = [editableView];
mockEditorCapability.isEditContextRoot.and.returnValue(true);
expect(policy.allow(mockEditAction, testContext)).toBe(false);
});
it("allows editing of panels in plot view", function () {
testViews = [plotView];
mockTypeCapability.getKey.and.returnValue('telemetry.panel');
expect(policy.allow(mockEditAction, testContext)).toBe(true);
});
it("disallows editing of plot view when object not a panel type", function () {
testViews = [plotView];
mockTypeCapability.getKey.and.returnValue('something.else');
expect(policy.allow(mockEditAction, testContext)).toBe(false);
});
it("allows the edit properties outside of the 'view-control' category", function () {
testViews = [nonEditableView];
testContext.category = "something-else";
expect(policy.allow(mockPropertiesAction, testContext)).toBe(true);
});
});
}
);

View File

@ -0,0 +1,120 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global describe,it,expect,beforeEach,jasmine*/
define(
["../../src/policies/EditContextualActionPolicy"],
function (EditContextualActionPolicy) {
describe("The Edit contextual action policy", function () {
var policy,
navigationService,
mockAction,
context,
navigatedObject,
mockDomainObject,
mockEditorCapability,
metadata,
editModeBlacklist = ["copy", "follow", "window", "link", "locate"],
nonEditContextBlacklist = ["copy", "follow", "properties", "move", "link", "remove", "locate"];
beforeEach(function () {
mockEditorCapability = jasmine.createSpyObj("editorCapability", ["isEditContextRoot", "inEditContext"]);
navigatedObject = jasmine.createSpyObj("navigatedObject", ["hasCapability", "getCapability"]);
navigatedObject.getCapability.and.returnValue(mockEditorCapability);
navigatedObject.hasCapability.and.returnValue(false);
mockDomainObject = jasmine.createSpyObj("domainObject", ["hasCapability", "getCapability"]);
mockDomainObject.hasCapability.and.returnValue(false);
mockDomainObject.getCapability.and.returnValue(mockEditorCapability);
navigationService = jasmine.createSpyObj("navigationService", ["getNavigation"]);
navigationService.getNavigation.and.returnValue(navigatedObject);
metadata = {key: "move"};
mockAction = jasmine.createSpyObj("action", ["getMetadata"]);
mockAction.getMetadata.and.returnValue(metadata);
context = {domainObject: mockDomainObject};
policy = new EditContextualActionPolicy(navigationService, editModeBlacklist, nonEditContextBlacklist);
});
it('Allows all actions when navigated object not in edit mode', function () {
expect(policy.allow(mockAction, context)).toBe(true);
});
it('Allows "window" action when navigated object in edit mode,' +
' but selected object not in edit mode ', function () {
navigatedObject.hasCapability.and.returnValue(true);
mockEditorCapability.isEditContextRoot.and.returnValue(true);
metadata.key = "window";
expect(policy.allow(mockAction, context)).toBe(true);
});
it('Allows "remove" action when navigated object in edit mode,' +
' and selected object not editable, but its parent is.',
function () {
var mockParent = jasmine.createSpyObj("parentObject", ["hasCapability"]),
mockContextCapability = jasmine.createSpyObj("contextCapability", ["getParent"]);
mockParent.hasCapability.and.returnValue(true);
mockContextCapability.getParent.and.returnValue(mockParent);
navigatedObject.hasCapability.and.returnValue(true);
mockDomainObject.getCapability.and.returnValue(mockContextCapability);
mockDomainObject.hasCapability.and.callFake(function (capability) {
switch (capability) {
case "editor": return false;
case "context": return true;
}
});
metadata.key = "remove";
expect(policy.allow(mockAction, context)).toBe(true);
});
it('Disallows "move" action when navigated object in edit mode,' +
' but selected object not in edit mode ', function () {
navigatedObject.hasCapability.and.returnValue(true);
mockEditorCapability.isEditContextRoot.and.returnValue(true);
mockEditorCapability.inEditContext.and.returnValue(false);
metadata.key = "move";
expect(policy.allow(mockAction, context)).toBe(false);
});
it('Disallows copy action when navigated object and' +
' selected object in edit mode', function () {
navigatedObject.hasCapability.and.returnValue(true);
mockDomainObject.hasCapability.and.returnValue(true);
mockEditorCapability.isEditContextRoot.and.returnValue(true);
mockEditorCapability.inEditContext.and.returnValue(true);
metadata.key = "copy";
expect(policy.allow(mockAction, context)).toBe(false);
});
});
}
);

View File

@ -0,0 +1,79 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
["../../src/policies/EditableViewPolicy"],
function (EditableViewPolicy) {
describe("The editable view policy", function () {
var mockDomainObject,
testMode,
policy;
beforeEach(function () {
testMode = true; // Act as if we're in Edit mode by default
mockDomainObject = jasmine.createSpyObj(
'domainObject',
['hasCapability', 'getCapability']
);
mockDomainObject.getCapability.and.returnValue({
inEditContext: function () {
return true;
}
});
mockDomainObject.hasCapability.and.callFake(function (c) {
return (c === 'editor') && testMode;
});
policy = new EditableViewPolicy();
});
it("disallows views in edit mode that are flagged as non-editable", function () {
expect(policy.allow({ editable: false }, mockDomainObject))
.toBeFalsy();
});
it("allows views in edit mode that are flagged as editable", function () {
expect(policy.allow({ editable: true }, mockDomainObject))
.toBeTruthy();
});
it("allows any view outside of edit mode", function () {
var testViews = [
{ editable: false },
{ editable: true },
{ someKey: "some value" }
];
testMode = false; // Act as if we're not in Edit mode
testViews.forEach(function (testView) {
expect(policy.allow(testView, mockDomainObject)).toBeTruthy();
});
});
it("treats views with no defined 'editable' property as editable", function () {
expect(policy.allow({ someKey: "some value" }, mockDomainObject))
.toBeTruthy();
});
});
}
);

View File

@ -93,33 +93,24 @@ define(
expect(mockOnCancel).toHaveBeenCalled();
});
describe("Adds callbacks to transaction", function () {
beforeEach(function () {
spyOn(manager, 'clearTransactionsFor');
manager.clearTransactionsFor.and.callThrough();
});
it("ignores subsequent calls for the same object", function () {
manager.addToTransaction(
testId,
jasmine.createSpy(),
jasmine.createSpy()
);
expect(mockTransactionService.addToTransaction.calls.count())
.toEqual(1);
});
it("and clears pending calls if same object", function () {
manager.addToTransaction(
testId,
jasmine.createSpy(),
jasmine.createSpy()
);
expect(manager.clearTransactionsFor).toHaveBeenCalledWith(testId);
});
it("and does not clear pending calls if different object", function () {
manager.addToTransaction(
'other-id',
jasmine.createSpy(),
jasmine.createSpy()
);
expect(manager.clearTransactionsFor).not.toHaveBeenCalled();
});
afterEach(function () {
expect(mockTransactionService.addToTransaction.calls.count()).toEqual(2);
});
it("accepts subsequent calls for other objects", function () {
manager.addToTransaction(
'other-id',
jasmine.createSpy(),
jasmine.createSpy()
);
expect(mockTransactionService.addToTransaction.calls.count())
.toEqual(2);
});
it("does not remove callbacks from the transaction", function () {

View File

@ -31,6 +31,7 @@ define([
"./src/controllers/TreeNodeController",
"./src/controllers/ActionGroupController",
"./src/controllers/ToggleController",
"./src/controllers/ContextMenuController",
"./src/controllers/ClickAwayController",
"./src/controllers/ViewSwitcherController",
"./src/controllers/GetterSetterController",
@ -48,6 +49,8 @@ define([
"./src/directives/MCTSplitter",
"./src/directives/MCTTree",
"./src/directives/MCTIndicators",
"./src/directives/MCTPreview",
"./src/actions/MCTPreviewAction",
"./src/filters/ReverseFilter",
"./res/templates/bottombar.html",
"./res/templates/controls/action-button.html",
@ -62,11 +65,13 @@ define([
"./res/templates/tree-node.html",
"./res/templates/label.html",
"./res/templates/controls/action-group.html",
"./res/templates/menu/context-menu.html",
"./res/templates/controls/switcher.html",
"./res/templates/object-inspector.html",
"./res/templates/controls/selector.html",
"./res/templates/controls/datetime-picker.html",
"./res/templates/controls/datetime-field.html",
"./res/templates/preview.html",
'legacyRegistry'
], function (
UrlService,
@ -79,6 +84,7 @@ define([
TreeNodeController,
ActionGroupController,
ToggleController,
ContextMenuController,
ClickAwayController,
ViewSwitcherController,
GetterSetterController,
@ -96,6 +102,8 @@ define([
MCTSplitter,
MCTTree,
MCTIndicators,
MCTPreview,
MCTPreviewAction,
ReverseFilter,
bottombarTemplate,
actionButtonTemplate,
@ -110,11 +118,13 @@ define([
treeNodeTemplate,
labelTemplate,
actionGroupTemplate,
contextMenuTemplate,
switcherTemplate,
objectInspectorTemplate,
selectorTemplate,
datetimePickerTemplate,
datetimeFieldTemplate,
previewTemplate,
legacyRegistry
) {
@ -242,6 +252,13 @@ define([
"key": "ToggleController",
"implementation": ToggleController
},
{
"key": "ContextMenuController",
"implementation": ContextMenuController,
"depends": [
"$scope"
]
},
{
"key": "ClickAwayController",
"implementation": ClickAwayController,
@ -377,6 +394,31 @@ define([
"key": "mctIndicators",
"implementation": MCTIndicators,
"depends": ['openmct']
},
{
"key": "mctPreview",
"implementation": MCTPreview,
"depends": [
"$document"
]
}
],
"actions": [
{
"key": "mct-preview-action",
"implementation": MCTPreviewAction,
"name": "Preview",
"cssClass": "hide-in-t-main-view icon-eye-open",
"description": "Preview in large dialog",
"category": [
"contextual",
"view-control"
],
"depends": [
"$compile",
"$rootScope"
],
"priority": "preferred"
}
],
"constants": [
@ -475,6 +517,13 @@ define([
"action"
]
},
{
"key": "context-menu",
"template": contextMenuTemplate,
"uses": [
"action"
]
},
{
"key": "switcher",
"template": switcherTemplate,
@ -485,6 +534,10 @@ define([
{
"key": "object-inspector",
"template": objectInspectorTemplate
},
{
"key": "mct-preview",
"template": previewTemplate
}
],
"controls": [

View File

@ -20,8 +20,8 @@
at runtime from the About dialog for additional information.
-->
<!-- DO NOT ADD SPACES BETWEEN THE SPANS - IT ADDS WHITE SPACE!! -->
<div class="c-indicator {{ngModel.getCssClass()}}"
<div class="ls-indicator {{ngModel.getCssClass()}}"
title="{{ngModel.getDescription()}}"
ng-show="ngModel.getText().length > 0">
<span class="label c-indicator__label">{{ngModel.getText()}}</span>
<span class="label">{{ngModel.getText()}}</span>
</div>

View File

@ -19,7 +19,7 @@
this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information.
-->
<div class="c-object-label">
<div class="c-object-label__type-icon {{type.getCssClass()}}" ng-class="{ 'l-icon-link':location.isLink() }"></div>
<div class='c-object-label__name'>{{model.name}}</div>
<div class="t-object-label l-flex-row flex-elem grows">
<div class="t-item-icon flex-elem {{type.getCssClass()}}" ng-class="{ 'l-icon-link':location.isLink() }"></div>
<div class='t-title-label flex-elem grows'>{{model.name}}</div>
</div>

View File

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

View File

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

View File

@ -0,0 +1,45 @@
<!--
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="t-frame-inner abs t-object-type-{{ domainObject.getModel().type }}" mct-preview>
<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">
<mct-representation
key="'switcher'"
ng-model="representation"
mct-object="domainObject">
</mct-representation>
</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

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,55 @@
/*****************************************************************************
* 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 PREVIEW_TEMPLATE = '<mct-representation key="\'mct-preview\'"' +
'class="t-rep-frame holder"' +
'mct-object="domainObject">' +
'</mct-representation>';
function MCTPreviewAction($compile, $rootScope, context) {
context = context || {};
this.domainObject = context.selectedObject || context.domainObject;
this.$rootScope = $rootScope;
this.$compile = $compile;
}
MCTPreviewAction.prototype.perform = function () {
var newScope = this.$rootScope.$new();
newScope.domainObject = this.domainObject;
this.$compile(PREVIEW_TEMPLATE)(newScope);
};
MCTPreviewAction.appliesTo = function (context) {
var domainObject = (context || {}).domainObject,
status = domainObject.getCapability('status');
return !(status && status.get('editing'));
};
return MCTPreviewAction;
}
);

View File

@ -0,0 +1,51 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/**
* Module defining ContextMenuController. Created by vwoeltje on 11/17/14.
*/
define(
[],
function () {
/**
* Controller for the context menu. Maintains an up-to-date
* list of applicable actions (those from category "contextual")
*
* @memberof platform/commonUI/general
* @constructor
*/
function ContextMenuController($scope) {
// Refresh variable "menuActions" in the scope
function updateActions() {
$scope.menuActions = $scope.action ?
$scope.action.getActions({ category: 'contextual' }) :
[];
}
// Update using the action capability
$scope.$watch("action", updateActions);
}
return ContextMenuController;
}
);

View File

@ -0,0 +1,64 @@
/*****************************************************************************
* 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', '../services/Overlay'], function ($, Overlay) {
function MCTPreview($document) {
function link($scope, $element) {
var actions = $scope.domainObject.getCapability('action'),
notebookAction = actions.getActions({key: 'notebook-new-entry'})[0];
var notebookButton = notebookAction ?
[
{
class: 'icon-notebook new-notebook-entry',
title: 'New Notebook Entry',
clickHandler: function (event) {
event.stopPropagation();
notebookAction.perform();
}
}
] : [];
var overlayService = new Overlay({
$document: $document,
$element: $element[0],
$scope: $scope,
browseBarButtons: notebookButton
});
overlayService.toggleOverlay();
$scope.$on('$destroy', function () {
$element.remove();
});
}
return {
restrict: 'A',
link: link
};
}
return MCTPreview;
});

View File

@ -54,7 +54,6 @@ define(
if (isDestroyed) {
return;
}
var removeSelectable = openmct.selection.selectable(
element[0],
scope.$eval(attrs.mctSelectable),

View File

@ -82,7 +82,7 @@ define(
}
var searchPath = "?" + arr.join('&'),
newTabPath =
"#" + this.urlForLocation(mode, domainObject) +
"index.html#" + this.urlForLocation(mode, domainObject) +
searchPath;
return newTabPath;
};

View File

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

View File

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

View File

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

View File

@ -0,0 +1,60 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
["../../src/controllers/ContextMenuController"],
function (ContextMenuController) {
describe("The context menu controller", function () {
var mockScope,
mockActions,
controller;
beforeEach(function () {
mockActions = jasmine.createSpyObj("action", ["getActions"]);
mockScope = jasmine.createSpyObj("$scope", ["$watch"]);
controller = new ContextMenuController(mockScope);
});
it("watches scope that may change applicable actions", function () {
// The action capability
expect(mockScope.$watch).toHaveBeenCalledWith(
"action",
jasmine.any(Function)
);
});
it("populates the scope with grouped and ungrouped actions", function () {
mockScope.action = mockActions;
mockScope.parameters = { category: "test" };
mockActions.getActions.and.returnValue(["a", "b", "c"]);
// Call the watch
mockScope.$watch.calls.mostRecent().args[1]();
// Should have grouped and ungrouped actions in scope now
expect(mockScope.menuActions.length).toEqual(3);
});
});
}
);

View File

@ -1,8 +1,8 @@
<!-- DO NOT ADD SPACES BETWEEN THE SPANS - IT ADDS WHITE SPACE!! -->
<div ng-show="notifications.length > 0" class="c-indicator c-indicator--clickable s-status-{{highest.severity}} icon-bell"
<div ng-show="notifications.length > 0" class="ls-indicator s-status-{{highest.severity}} icon-bell"
ng-controller="NotificationIndicatorController">
<span class="label c-indicator__label">
<button ng-click="showNotificationsList()">
{{notifications.length}} Notification<span ng-show="notifications.length > 1">s</span></button>
</span><span class="c-indicator__count">{{notifications.length}}</span>
<span class="label">
<a ng-click="showNotificationsList()">
{{notifications.length}} Notification<span ng-show="notifications.length > 1">s</span></a>
</span><span class="count">{{notifications.length}}</span>
</div>

View File

@ -43,25 +43,12 @@ define(
* Launch a dialog showing a list of current notifications.
*/
$scope.showNotificationsList = function () {
let notificationsList = openmct.notifications.notifications.map(notification => {
if (notification.model.severity === 'alert' || notification.model.severity === 'info') {
notification.model.primaryOption = {
label: 'Dismiss',
callback: () => {
let currentIndex = notificationsList.indexOf(notification);
notification.dismiss();
notificationsList.splice(currentIndex, 1);
}
}
}
return notification;
})
dialogService.getDialogResponse('overlay-message-list', {
dialog: {
title: "Messages",
//Launch the message list dialog with the models
// from the notifications
messages: notificationsList
messages: openmct.notifications.notifications
}
});

View File

@ -43,10 +43,23 @@ define([], function () {
var mutationTopic = topic('mutation');
mutationTopic.listen(function (domainObject) {
var persistence = domainObject.getCapability('persistence');
var wasActive = transactionService.isActive();
cacheService.put(domainObject.getId(), domainObject.getModel());
if (hasChanged(domainObject)) {
persistence.persist();
if (!wasActive) {
transactionService.startTransaction();
}
transactionService.addToTransaction(
persistence.persist.bind(persistence),
persistence.refresh.bind(persistence)
);
if (!wasActive) {
transactionService.commit();
}
}
});
}

View File

@ -24,27 +24,22 @@ define(
["../../src/runs/TransactingMutationListener"],
function (TransactingMutationListener) {
describe("TransactingMutationListener", function () {
xdescribe("TransactingMutationListener", function () {
var mockTopic,
mockMutationTopic,
mockCacheService,
mockTransactionService,
mockDomainObject,
mockModel,
mockPersistence;
beforeEach(function () {
mockTopic = jasmine.createSpy('topic');
mockMutationTopic =
jasmine.createSpyObj('mutation', ['listen']);
mockCacheService =
jasmine.createSpyObj('cacheService', [
'put'
]);
mockTransactionService =
jasmine.createSpyObj('transactionService', [
'isActive',
'startTransaction',
'addToTransaction',
'commit'
]);
mockDomainObject = jasmine.createSpyObj(
@ -57,24 +52,18 @@ define(
);
mockTopic.and.callFake(function (t) {
expect(t).toBe('mutation');
return mockMutationTopic;
return (t === 'mutation') && mockMutationTopic;
});
mockDomainObject.getId.and.returnValue('mockId');
mockDomainObject.getCapability.and.callFake(function (c) {
expect(c).toBe('persistence');
return mockPersistence;
return (c === 'persistence') && mockPersistence;
});
mockModel = {};
mockDomainObject.getModel.and.returnValue(mockModel);
mockPersistence.persisted.and.returnValue(true);
return new TransactingMutationListener(
mockTopic,
mockTransactionService,
mockCacheService
mockTransactionService
);
});
@ -83,27 +72,48 @@ define(
.toHaveBeenCalledWith(jasmine.any(Function));
});
it("calls persist if the model has changed", function () {
mockModel.persisted = Date.now();
[false, true].forEach(function (isActive) {
var verb = isActive ? "is" : "isn't";
//Mark the model dirty by setting the mutated date later than the last persisted date.
mockModel.modified = mockModel.persisted + 1;
function onlyWhenInactive(expectation) {
return isActive ? expectation.not : expectation;
}
mockMutationTopic.listen.calls.mostRecent()
.args[0](mockDomainObject);
describe("when a transaction " + verb + " active", function () {
var innerVerb = isActive ? "does" : "doesn't";
expect(mockPersistence.persist).toHaveBeenCalled();
});
beforeEach(function () {
mockTransactionService.isActive.and.returnValue(isActive);
});
it("does not call persist if the model has not changed", function () {
mockModel.persisted = Date.now();
describe("and mutation occurs", function () {
beforeEach(function () {
mockMutationTopic.listen.calls.mostRecent()
.args[0](mockDomainObject);
});
mockModel.modified = mockModel.persisted;
mockMutationTopic.listen.calls.mostRecent()
.args[0](mockDomainObject);
it(innerVerb + " start a new transaction", function () {
onlyWhenInactive(
expect(mockTransactionService.startTransaction)
).toHaveBeenCalled();
});
expect(mockPersistence.persist).not.toHaveBeenCalled();
it("adds to the active transaction", function () {
expect(mockTransactionService.addToTransaction)
.toHaveBeenCalledWith(
jasmine.any(Function),
jasmine.any(Function)
);
});
it(innerVerb + " immediately commit", function () {
onlyWhenInactive(
expect(mockTransactionService.commit)
).toHaveBeenCalled();
});
});
});
});
});
}

View File

@ -24,6 +24,7 @@ define([
"./src/actions/MoveAction",
"./src/actions/CopyAction",
"./src/actions/LinkAction",
"./src/actions/GoToOriginalAction",
"./src/actions/SetPrimaryLocationAction",
"./src/services/LocatingCreationDecorator",
"./src/services/LocatingObjectDecorator",
@ -40,6 +41,7 @@ define([
MoveAction,
CopyAction,
LinkAction,
GoToOriginalAction,
SetPrimaryLocationAction,
LocatingCreationDecorator,
LocatingObjectDecorator,
@ -102,6 +104,14 @@ define([
"linkService"
]
},
{
"key": "follow",
"name": "Go To Original",
"description": "Go to the original, un-linked instance of this object.",
"cssClass": "",
"category": "contextual",
"implementation": GoToOriginalAction
},
{
"key": "locate",
"name": "Set Primary Location",

View File

@ -0,0 +1,60 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
function () {
/**
* Implements the "Go To Original" action, which follows a link back
* to an original instance of an object.
*
* @implements {Action}
* @constructor
* @private
* @memberof platform/entanglement
* @param {ActionContext} context the context in which the action
* will be performed
*/
function GoToOriginalAction(context) {
this.domainObject = context.domainObject;
}
GoToOriginalAction.prototype.perform = function () {
return this.domainObject.getCapability("location").getOriginal()
.then(function (originalObject) {
var actionCapability =
originalObject.getCapability("action");
return actionCapability &&
actionCapability.perform("navigate");
});
};
GoToOriginalAction.appliesTo = function (context) {
var domainObject = context.domainObject;
return domainObject && domainObject.hasCapability("location") &&
domainObject.getCapability("location").isLink();
};
return GoToOriginalAction;
}
);

View File

@ -0,0 +1,93 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
[
'../../src/actions/GoToOriginalAction',
'../DomainObjectFactory',
'../ControlledPromise'
],
function (GoToOriginalAction, domainObjectFactory, ControlledPromise) {
describe("The 'go to original' action", function () {
var testContext,
originalDomainObject,
mockLocationCapability,
mockOriginalActionCapability,
originalPromise,
action;
beforeEach(function () {
mockLocationCapability = jasmine.createSpyObj(
'location',
['isLink', 'isOriginal', 'getOriginal']
);
mockOriginalActionCapability = jasmine.createSpyObj(
'action',
['perform', 'getActions']
);
originalPromise = new ControlledPromise();
mockLocationCapability.getOriginal.and.returnValue(originalPromise);
mockLocationCapability.isLink.and.returnValue(true);
mockLocationCapability.isOriginal.and.callFake(function () {
return !mockLocationCapability.isLink();
});
testContext = {
domainObject: domainObjectFactory({
capabilities: {
location: mockLocationCapability
}
})
};
originalDomainObject = domainObjectFactory({
capabilities: {
action: mockOriginalActionCapability
}
});
action = new GoToOriginalAction(testContext);
});
it("is applicable to links", function () {
expect(GoToOriginalAction.appliesTo(testContext))
.toBeTruthy();
});
it("is not applicable to originals", function () {
mockLocationCapability.isLink.and.returnValue(false);
expect(GoToOriginalAction.appliesTo(testContext))
.toBeFalsy();
});
it("navigates to original objects when performed", function () {
expect(mockOriginalActionCapability.perform)
.not.toHaveBeenCalled();
action.perform();
originalPromise.resolve(originalDomainObject);
expect(mockOriginalActionCapability.perform)
.toHaveBeenCalledWith('navigate');
});
});
}
);

View File

@ -19,14 +19,16 @@
this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information.
-->
<div class="c-clock l-time-display" ng-controller="ClockController as clock">
<div class="c-clock__timezone">
{{clock.zone()}}
</div>
<div class="c-clock__value">
{{clock.text()}}
</div>
<div class="c-clock__ampm">
{{clock.ampm()}}
<div class="l-time-display l-digital l-clock s-clock" ng-controller="ClockController as clock">
<div class="l-elem-wrapper">
<span class="l-elem timezone">
{{clock.zone()}}
</span>
<span class="l-elem value active">
{{clock.text()}}
</span>
<span class="l-elem ampm">
{{clock.ampm()}}
</span>
</div>
</div>

View File

@ -19,19 +19,21 @@
this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information.
-->
<div class="c-timer is-{{timer.timerState}}" ng-controller="TimerController as timer">
<div class="c-timer__controls">
<button ng-click="timer.clickStopButton()"
ng-hide="timer.timerState == 'stopped'"
title="Reset"
class="c-timer__ctrl-reset c-icon-button c-icon-button--major icon-reset"></button>
<button ng-click="timer.clickButton()"
title="{{timer.buttonText()}}"
class="c-timer__ctrl-pause-play c-icon-button c-icon-button--major {{timer.buttonCssClass()}}"></button>
</div>
<div class="c-timer__direction {{timer.signClass()}}"
ng-hide="!timer.signClass()"></div>
<div class="c-timer__value">{{timer.text() || "--:--:--"}}
<div class="l-time-display l-digital l-timer s-timer s-state-{{timer.timerState}}" ng-controller="TimerController as timer">
<div class="l-elem-wrapper l-flex-row">
<div class="l-elem-wrapper l-flex-row controls">
<a ng-click="timer.clickStopButton()"
title="Stop"
class="flex-elem s-icon-button t-btn-stop icon-box"></a>
<a ng-click="timer.clickButton()"
title="{{timer.buttonText()}}"
class="flex-elem s-icon-button t-btn-pauseplay {{timer.buttonCssClass()}}"></a>
</div>
<span class="flex-elem l-value {{timer.signClass()}}">
<span class="value"
ng-class="{ active:timer.text() }">{{timer.text() || "--:--:--"}}
</span>
</span>
<span ng-controller="RefreshingController"></span>
</div>
<span class="c-timer__ng-controller u-contents" ng-controller="RefreshingController"></span>
</div>

View File

@ -49,7 +49,7 @@ define(
};
ClockIndicator.prototype.getCssClass = function () {
return "t-indicator-clock icon-clock no-minify c-indicator--not-clickable";
return "t-indicator-clock icon-clock no-collapse float-right";
};
ClockIndicator.prototype.getText = function () {

View File

@ -0,0 +1,475 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define([
"./src/FixedController",
"./templates/fixed.html",
"./templates/frame.html",
"./templates/elements/telemetry.html",
"./templates/elements/box.html",
"./templates/elements/line.html",
"./templates/elements/text.html",
"./templates/elements/image.html",
"legacyRegistry"
], function (
FixedController,
fixedTemplate,
frameTemplate,
telemetryTemplate,
boxTemplate,
lineTemplate,
textTemplate,
imageTemplate,
legacyRegistry
) {
return function() {
return function (openmct) {
openmct.legacyRegistry.register("platform/features/fixed", {
"name": "Fixed position components.",
"description": "Plug in adding Fixed Position object type.",
"extensions": {
"views": [
{
"key": "fixed-display",
"name": "Fixed Position Display",
"cssClass": "icon-box-with-dashed-lines",
"type": "telemetry.fixed",
"template": fixedTemplate,
"uses": ["composition"],
"editable": true
}
],
"templates": [
{
"key": "fixed.telemetry",
"template": telemetryTemplate
},
{
"key": "fixed.box",
"template": boxTemplate
},
{
"key": "fixed.line",
"template": lineTemplate
},
{
"key": "fixed.text",
"template": textTemplate
},
{
"key": "fixed.image",
"template": imageTemplate
}
],
"controllers": [
{
"key": "FixedController",
"implementation": FixedController,
"depends": [
"$scope",
"$q",
"dialogService",
"openmct",
"$element"
]
}
],
"toolbars": [
{
name: "Fixed Position Toolbar",
key: "fixed.position",
description: "Toolbar for the selected element inside a fixed position display.",
forSelection: function (selection) {
if (!selection) {
return;
}
return (openmct.editor.isEditing() &&
selection[0] && selection[0].context.elementProxy &&
((selection[1] && selection[1].context.item.type === 'telemetry.fixed') ||
(selection[0] && selection[0].context.item && selection[0].context.item.type === 'telemetry.fixed')));
},
toolbar: function (selection) {
var imageProperties = ["add", "remove", "order", "stroke", "useGrid", "x", "y", "height", "width", "url"];
var boxProperties = ["add", "remove", "order", "stroke", "useGrid", "x", "y", "height", "width", "fill"];
var textProperties = ["add", "remove", "order", "stroke", "useGrid", "x", "y", "height", "width", "fill", "color", "size", "text"];
var lineProperties = ["add", "remove", "order", "stroke", "useGrid", "x", "y", "x2", "y2"];
var telemetryProperties = ["add", "remove", "order", "stroke", "useGrid", "x", "y", "height", "width", "fill", "color", "size", "titled"];
var fixedPageProperties = ["add"];
var properties = [],
fixedItem = selection[0] && selection[0].context.item,
elementProxy = selection[0] && selection[0].context.elementProxy,
domainObject = selection[1] && selection[1].context.item,
path;
if (elementProxy) {
var type = elementProxy.element.type;
path = "configuration['fixed-display'].elements[" + elementProxy.index + "]";
properties =
type === 'fixed.image' ? imageProperties :
type === 'fixed.text' ? textProperties :
type === 'fixed.box' ? boxProperties :
type === 'fixed.line' ? lineProperties :
type === 'fixed.telemetry' ? telemetryProperties : [];
} else if (fixedItem) {
properties = domainObject && domainObject.type === 'layout' ? [] : fixedPageProperties;
}
return [
{
control: "menu",
domainObject: domainObject || selection[0].context.item,
method: function (option) {
selection[0].context.fixedController.add(option.key);
},
key: "add",
icon: "icon-plus",
label: "Add",
options: [
{
"name": "Box",
"class": "icon-box",
"key": "fixed.box"
},
{
"name": "Line",
"class": "icon-line-horz",
"key": "fixed.line"
},
{
"name": "Text",
"class": "icon-T",
"key": "fixed.text"
},
{
"name": "Image",
"class": "icon-image",
"key": "fixed.image"
}
]
},
{
control: "menu",
domainObject: domainObject,
method: function (option) {
console.log('option', option)
selection[0].context.fixedController.order(
selection[0].context.elementProxy,
option.key
);
},
key: "order",
icon: "icon-layers",
title: "Move the selected object above or below other objects",
options: [
{
"name": "Move to Top",
"class": "icon-arrow-double-up",
"key": "top"
},
{
"name": "Move Up",
"class": "icon-arrow-up",
"key": "up"
},
{
"name": "Move Down",
"class": "icon-arrow-down",
"key": "down"
},
{
"name": "Move to Bottom",
"class": "icon-arrow-double-down",
"key": "bottom"
}
]
},
{
control: "color-picker",
domainObject: domainObject,
property: path + ".fill",
icon: "icon-paint-bucket",
title: "Set fill color",
key: 'fill'
},
{
control: "color-picker",
domainObject: domainObject,
property: path + ".stroke",
icon: "icon-line-horz",
title: "Set border color",
key: 'stroke'
},
{
control: "button",
domainObject: domainObject,
property: path + ".url",
icon: "icon-image",
title: "Edit image properties",
key: 'url',
dialog: {
control: "input",
type: "text",
name: "Image URL",
class: "l-input-lg",
required: true
}
},
{
control: "color-picker",
domainObject: domainObject,
property: path + ".color",
icon: "icon-T",
mandatory: true,
title: "Set text color",
key: 'color'
},
{
control: "select-menu",
domainObject: domainObject,
property: path + ".size",
title: "Set text size",
key: 'size',
options: [9, 10, 11, 12, 13, 14, 15, 16, 20, 24, 30, 36, 48, 72, 96].map(function (size) {
return { "value": size + "px"};
})
},
{
control: "input",
type: "number",
domainObject: domainObject,
property: path + ".x",
label: "X",
title: "X position",
key: "x",
class: "l-input-sm",
min: "0"
},
{
control: "input",
type: "number",
domainObject: domainObject,
property: path + ".y",
label: "Y",
title: "Y position",
key: "y",
class: "l-input-sm",
min: "0"
},
{
control: "input",
type: "number",
domainObject: domainObject,
property: path + ".x",
label: "X1",
title: "X1 position",
key: "x1",
class: "l-input-sm",
min: "0"
},
{
control: "input",
type: "number",
domainObject: domainObject,
property: path + ".y",
label: "Y1",
title: "Y1 position",
key: "y1",
class: "l-input-sm",
min: "0"
},
{
control: "input",
type: "number",
domainObject: domainObject,
property: path + ".x2",
label: "X2",
title: "X2 position",
key: "x2",
class: "l-input-sm",
min: "0"
},
{
control: "input",
type: "number",
domainObject: domainObject,
property: path + ".y2",
label: "Y2",
title: "Y2 position",
key: "y2",
class: "l-input-sm",
min: "0"
},
{
control: "input",
type: "number",
domainObject: domainObject,
property: path + ".height",
label: "H",
title: "Resize object height",
key: "height",
class: "l-input-sm",
min: "1"
},
{
control: "input",
type: "number",
domainObject: domainObject,
property: path + ".width",
label: "W",
title: "Resize object width",
key: "width",
class: "l-input-sm",
min: "1"
},
{
control: "toggle-button",
domainObject: domainObject,
property: path + ".useGrid",
key: "useGrid",
options: [
{
value: true,
icon: 'icon-grid-snap-to',
title: 'Snap to grid'
},
{
value: false,
icon: 'icon-grid-snap-no',
title: "Do not snap to grid"
}
]
},
{
control: "button",
domainObject: domainObject,
property: path + ".text",
icon: "icon-gear",
title: "Edit text properties",
key: "text",
dialog: {
control: "input",
type: "text",
name: "Text",
required: true
}
},
{
control: "toggle-button",
domainObject: domainObject,
property: path + ".titled",
key: "titled",
options: [
{
value: true,
icon: 'icon-two-parts-both',
title: 'Show label'
},
{
value: false,
icon: 'icon-two-parts-one-only',
title: "Hide label"
}
]
},
{
control: "button",
domainObject: domainObject,
method: function () {
selection[0].context.fixedController.remove(
selection[0].context.elementProxy
);
},
key: "remove",
icon: "icon-trash"
}
].filter(function (item) {
var filtered;
properties.forEach(function (property) {
if (item.property && item.key === property ||
item.method && item.key === property) {
filtered = item;
}
});
return filtered;
});
}
}
],
"types": [
{
"key": "telemetry.fixed",
"name": "Fixed Position Display",
"cssClass": "icon-box-with-dashed-lines",
"description": "Collect and display telemetry elements in " +
"alphanumeric format in a simple canvas workspace. " +
"Elements can be positioned and sized. " +
"Lines, boxes and images can be added as well.",
"priority": 899,
"delegates": [
"telemetry"
],
"features": "creation",
"contains": [
{
"has": "telemetry"
}
],
"model": {
"layoutGrid": [64, 16],
"composition": []
},
"properties": [
{
"name": "Layout Grid",
"control": "composite",
"items": [
{
"name": "Horizontal grid (px)",
"control": "textfield",
"cssClass": "l-input-sm l-numeric"
},
{
"name": "Vertical grid (px)",
"control": "textfield",
"cssClass": "l-input-sm l-numeric"
}
],
"pattern": "^(\\d*[1-9]\\d*)?$",
"property": "layoutGrid",
"conversion": "number[]"
}
],
"views": [
"fixed-display"
]
}
]
}
});
openmct.legacyRegistry.enable("platform/features/fixed");
}
}
});

View File

@ -0,0 +1,720 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
[
'lodash',
'./FixedProxy',
'./elements/ElementProxies',
'./FixedDragHandle',
'../../../../src/api/objects/object-utils'
],
function (
_,
FixedProxy,
ElementProxies,
FixedDragHandle,
objectUtils
) {
var DEFAULT_DIMENSIONS = [2, 1];
// Convert from element x/y/width/height to an
// appropriate ng-style argument, to position elements.
function convertPosition(elementProxy) {
if (elementProxy.getStyle) {
return elementProxy.getStyle();
}
var gridSize = elementProxy.getGridSize();
// Multiply position/dimensions by grid size
return {
left: (gridSize[0] * elementProxy.element.x) + 'px',
top: (gridSize[1] * elementProxy.element.y) + 'px',
width: (gridSize[0] * elementProxy.element.width) + 'px',
height: (gridSize[1] * elementProxy.element.height) + 'px'
};
}
/**
* The FixedController is responsible for supporting the
* Fixed Position view. It arranges frames according to saved
* configuration and provides methods for updating these based on
* mouse movement.
* @memberof platform/features/layout
* @constructor
* @param {Scope} $scope the controller's Angular scope
*/
function FixedController($scope, $q, dialogService, openmct, $element) {
this.names = {}; // Cache names by ID
this.values = {}; // Cache values by ID
this.elementProxiesById = {};
this.telemetryObjects = {};
this.subscriptions = {};
this.openmct = openmct;
this.$element = $element;
this.$scope = $scope;
this.dialogService = dialogService;
this.$q = $q;
this.newDomainObject = $scope.domainObject.useCapability('adapter');
this.fixedViewSelectable = false;
var self = this;
[
'digest',
'fetchHistoricalData',
'getTelemetry',
'setDisplayedValue',
'subscribeToObject',
'unsubscribe',
'updateView'
].forEach(function (name) {
self[name] = self[name].bind(self);
});
// Decorate an element for display
function makeProxyElement(element, index, elements) {
var ElementProxy = ElementProxies[element.type],
e = ElementProxy && new ElementProxy(element, index, elements, self.gridSize);
if (e) {
// Provide a displayable position (convert from grid to px)
e.style = convertPosition(e);
// Template names are same as type names, presently
e.template = element.type;
}
return e;
}
// Decorate elements in the current configuration
function refreshElements() {
var elements = (((self.newDomainObject.configuration || {})['fixed-display'] || {}).elements || []);
// Create the new proxies...
self.elementProxies = elements.map(makeProxyElement);
if (self.selectedElementProxy) {
// If selection is not in array, select parent.
// Otherwise, set the element to select after refresh.
var index = elements.indexOf(self.selectedElementProxy.element);
if (index === -1) {
self.$element[0].click();
} else if (!self.elementToSelectAfterRefresh) {
self.elementToSelectAfterRefresh = self.elementProxies[index].element;
}
}
// Finally, rebuild lists of elements by id to
// facilitate faster update when new telemetry comes in.
self.elementProxiesById = {};
self.elementProxies.forEach(function (elementProxy) {
var id = elementProxy.id;
if (elementProxy.element.type === 'fixed.telemetry') {
// Provide it a cached name/value to avoid flashing
elementProxy.name = self.names[id];
elementProxy.value = self.values[id];
self.elementProxiesById[id] = self.elementProxiesById[id] || [];
self.elementProxiesById[id].push(elementProxy);
}
});
}
// Trigger a new query for telemetry data
function updateDisplayBounds(bounds, isTick) {
if (!isTick) {
//Reset values
self.values = {};
refreshElements();
//Fetch new data
Object.values(self.telemetryObjects).forEach(function (object) {
self.fetchHistoricalData(object);
});
}
}
// Add an element to this view
function addElement(element) {
var index;
var elements = (((self.newDomainObject.configuration || {})['fixed-display'] || {}).elements || []);
elements.push(element);
if (self.selectedElementProxy) {
index = elements.indexOf(self.selectedElementProxy.element);
}
self.mutate("configuration['fixed-display'].elements", elements);
elements = (self.newDomainObject.configuration)['fixed-display'].elements || [];
self.elementToSelectAfterRefresh = elements[elements.length - 1];
if (self.selectedElementProxy) {
// Update the selected element with the new
// value since newDomainOject is mutated.
self.selectedElementProxy.element = elements[index];
}
refreshElements();
}
// Position a panel after a drop event
function handleDrop(e, id, position) {
// Don't handle this event if it has already been handled
if (e.defaultPrevented) {
return;
}
e.preventDefault();
// Store the position of this element.
// color is set to "" to let the CSS theme determine the default color
addElement({
type: "fixed.telemetry",
x: Math.floor(position.x / self.gridSize[0]),
y: Math.floor(position.y / self.gridSize[1]),
id: id,
stroke: "transparent",
color: "",
titled: true,
width: DEFAULT_DIMENSIONS[0],
height: DEFAULT_DIMENSIONS[1],
useGrid: true
});
// Subscribe to the new object to get telemetry
self.openmct.objects.get(id).then(function (object) {
self.getTelemetry(object);
});
}
this.elementProxies = [];
this.addElement = addElement;
this.refreshElements = refreshElements;
this.fixedProxy = new FixedProxy(this.addElement, this.$q, this.dialogService);
this.composition = this.openmct.composition.get(this.newDomainObject);
this.composition.on('add', this.onCompositionAdd, this);
this.composition.on('remove', this.onCompositionRemove, this);
this.composition.load();
// Position panes where they are dropped
$scope.$on("mctDrop", handleDrop);
$scope.$on("$destroy", this.destroy.bind(this));
// Respond to external bounds changes
this.openmct.time.on("bounds", updateDisplayBounds);
this.openmct.selection.on('change', this.setSelection.bind(this));
this.openmct.editor.on('isEditing', this.handleEditing.bind(this));
this.$element.on('click', this.bypassSelection.bind(this));
this.unlisten = this.openmct.objects.observe(this.newDomainObject, '*', function (obj) {
this.newDomainObject = JSON.parse(JSON.stringify(obj));
this.updateElementPositions(this.newDomainObject.layoutGrid);
}.bind(this));
this.updateElementPositions(this.newDomainObject.layoutGrid);
refreshElements();
}
FixedController.prototype.updateElementPositions = function (layoutGrid) {
this.gridSize = layoutGrid;
this.elementProxies.forEach(function (elementProxy) {
elementProxy.setGridSize(this.gridSize);
elementProxy.style = convertPosition(elementProxy);
}.bind(this));
};
FixedController.prototype.onCompositionAdd = function (object) {
this.getTelemetry(object);
};
FixedController.prototype.onCompositionRemove = function (identifier) {
// Defer mutation of newDomainObject to prevent mutating an
// outdated version since this is triggered by a composition change.
setTimeout(function () {
var id = objectUtils.makeKeyString(identifier);
var elements = this.newDomainObject.configuration['fixed-display'].elements || [];
var newElements = elements.filter(function (proxy) {
return proxy.id !== id;
});
this.mutate("configuration['fixed-display'].elements", newElements);
if (this.subscriptions[id]) {
this.subscriptions[id]();
delete this.subscriptions[id];
}
delete this.telemetryObjects[id];
this.refreshElements();
}.bind(this));
};
/**
* Removes an element from the view.
*
* @param {Object} elementProxy the element proxy to remove.
*/
FixedController.prototype.remove = function (elementProxy) {
var element = elementProxy.element;
var elements = this.newDomainObject.configuration['fixed-display'].elements || [];
elements.splice(elements.indexOf(element), 1);
if (element.type === 'fixed.telemetry') {
this.newDomainObject.composition = this.newDomainObject.composition.filter(function (identifier) {
return objectUtils.makeKeyString(identifier) !== element.id;
});
}
this.mutate("configuration['fixed-display'].elements", elements);
this.refreshElements();
};
/**
* Adds a new element to the view.
*
* @param {string} type the type of element to add. Supported types are:
* `fixed.image`
* `fixed.box`
* `fixed.text`
* `fixed.line`
*/
FixedController.prototype.add = function (type) {
this.fixedProxy.add(type);
};
/**
* Change the display order of the element proxy.
*/
FixedController.prototype.order = function (elementProxy, position) {
var elements = elementProxy.order(position);
// Find the selected element index in the updated array.
var selectedElemenetIndex = elements.indexOf(this.selectedElementProxy.element);
this.mutate("configuration['fixed-display'].elements", elements);
elements = (this.newDomainObject.configuration)['fixed-display'].elements || [];
// Update the selected element with the new
// value since newDomainOject is mutated.
this.selectedElementProxy.element = elements[selectedElemenetIndex];
this.refreshElements();
};
FixedController.prototype.generateDragHandle = function (elementProxy, elementHandle) {
var index = this.elementProxies.indexOf(elementProxy);
if (elementHandle) {
elementHandle.element = elementProxy.element;
elementProxy = elementHandle;
}
return new FixedDragHandle(
elementProxy,
"configuration['fixed-display'].elements[" + index + "]",
this
);
};
FixedController.prototype.generateDragHandles = function (elementProxy) {
return elementProxy.handles().map(function (handle) {
return this.generateDragHandle(elementProxy, handle);
}, this);
};
FixedController.prototype.updateSelectionStyle = function () {
this.selectedElementProxy.style = convertPosition(this.selectedElementProxy);
};
FixedController.prototype.setSelection = function (selectable) {
var selection = selectable[0];
if (this.selectionListeners) {
this.selectionListeners.forEach(function (l) {
l();
});
}
this.selectionListeners = [];
if (!selection) {
return;
}
if (selection.context.elementProxy) {
this.selectedElementProxy = selection.context.elementProxy;
this.attachSelectionListeners();
this.mvHandle = this.generateDragHandle(this.selectedElementProxy);
this.resizeHandles = this.generateDragHandles(this.selectedElementProxy);
} else {
// Make fixed view selectable if it's not already.
if (!this.fixedViewSelectable && selectable.length === 1) {
this.fixedViewSelectable = true;
selection.context.fixedController = this;
this.openmct.selection.select(selection);
}
this.resizeHandles = [];
this.mvHandle = undefined;
this.selectedElementProxy = undefined;
}
};
FixedController.prototype.attachSelectionListeners = function () {
var index = this.elementProxies.indexOf(this.selectedElementProxy);
var path = "configuration['fixed-display'].elements[" + index + "]";
this.selectionListeners.push(this.openmct.objects.observe(this.newDomainObject, path + ".useGrid", function (newValue) {
if (this.selectedElementProxy.useGrid() !== newValue) {
this.selectedElementProxy.useGrid(newValue);
this.updateSelectionStyle();
this.openmct.objects.mutate(this.newDomainObject, path, this.selectedElementProxy.element);
}
}.bind(this)));
[
"width",
"height",
"stroke",
"fill",
"x",
"y",
"x1",
"y1",
"x2",
"y2",
"color",
"size",
"text",
"titled"
].forEach(function (property) {
this.selectionListeners.push(this.openmct.objects.observe(this.newDomainObject, path + "." + property, function (newValue) {
this.selectedElementProxy.element[property] = newValue;
this.updateSelectionStyle();
}.bind(this)));
}.bind(this));
};
FixedController.prototype.destroy = function () {
this.unsubscribe();
this.unlisten();
this.openmct.time.off("bounds", this.updateDisplayBounds);
this.openmct.selection.off("change", this.setSelection);
this.composition.off('add', this.onCompositionAdd, this);
this.composition.off('remove', this.onCompositionRemove, this);
this.openmct.editor.off('isEditing', this.handleEditing, this);
};
/**
* A rate-limited digest function. Caps digests at 60Hz
* @private
*/
FixedController.prototype.digest = function () {
var self = this;
if (!this.digesting) {
this.digesting = true;
requestAnimationFrame(function () {
self.$scope.$digest();
self.digesting = false;
});
}
};
/**
* Unsubscribe all listeners
* @private
*/
FixedController.prototype.unsubscribe = function () {
Object.values(this.subscriptions).forEach(function (unsubscribeFunc) {
unsubscribeFunc();
});
this.subscriptions = {};
this.telemetryObjects = {};
};
/**
* Subscribe to the given domain object
* @private
* @param {object} object Domain object to subscribe to
* @returns {object} The provided object, for chaining.
*/
FixedController.prototype.subscribeToObject = function (object) {
var self = this;
var timeAPI = this.openmct.time;
var id = objectUtils.makeKeyString(object.identifier);
this.subscriptions[id] = self.openmct.telemetry.subscribe(object, function (datum) {
if (timeAPI.clock() !== undefined) {
self.updateView(object, datum);
}
}, {});
return object;
};
/**
* Print the values from the given datum against the provided object in the view.
* @private
* @param {object} telemetryObject The domain object associated with the given telemetry data
* @param {object} datum The telemetry datum containing the values to print
*/
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 limitEvaluator = this.openmct.telemetry.limitEvaluator(telemetryObject);
var alarm = limitEvaluator && limitEvaluator.evaluate(datum, valueMetadata);
this.setDisplayedValue(
telemetryObject,
formattedTelemetryValue,
alarm && alarm.cssClass
);
this.digest();
};
/**
* @private
*/
FixedController.prototype.getFormattedTelemetryValueForKey = function (valueMetadata, datum) {
var formatter = this.openmct.telemetry.getValueFormatter(valueMetadata);
return formatter.format(datum);
};
/**
* @private
*/
FixedController.prototype.chooseValueMetadataToDisplay = function (metadata) {
// If there is a range value, show that preferentially
var valueMetadata = metadata.valuesForHints(['range'])[0];
// If no range is defined, default to the highest priority non time-domain data.
if (valueMetadata === undefined) {
var valuesOrderedByPriority = metadata.values();
valueMetadata = valuesOrderedByPriority.filter(function (values) {
return !(values.hints.domain);
})[0];
}
return valueMetadata;
};
/**
* Request the last historical data point for the given domain object
* @param {object} object
* @returns {object} the provided object for chaining.
*/
FixedController.prototype.fetchHistoricalData = function (object) {
var bounds = this.openmct.time.bounds();
var self = this;
self.openmct.telemetry.request(object, {start: bounds.start, end: bounds.end, size: 1})
.then(function (data) {
if (data.length > 0) {
self.updateView(object, data[data.length - 1]);
}
});
return object;
};
/**
* Print a value to the onscreen element associated with a given telemetry object.
* @private
* @param {object} telemetryObject The telemetry object associated with the value
* @param {string | number} value The value to print to screen
* @param {string} [cssClass] an optional CSS class to apply to the onscreen element.
*/
FixedController.prototype.setDisplayedValue = function (telemetryObject, value, cssClass) {
var id = objectUtils.makeKeyString(telemetryObject.identifier);
var self = this;
(self.elementProxiesById[id] || []).forEach(function (element) {
self.names[id] = telemetryObject.name;
self.values[id] = value;
element.name = self.names[id];
element.value = self.values[id];
element.cssClass = cssClass;
});
};
FixedController.prototype.getTelemetry = function (domainObject) {
var id = objectUtils.makeKeyString(domainObject.identifier);
if (this.subscriptions[id]) {
this.subscriptions[id]();
delete this.subscriptions[id];
}
delete this.telemetryObjects[id];
if (!this.openmct.telemetry.isTelemetryObject(domainObject)) {
return;
}
// Initialize display
this.telemetryObjects[id] = domainObject;
this.setDisplayedValue(domainObject, "");
return Promise.resolve(domainObject)
.then(this.fetchHistoricalData)
.then(this.subscribeToObject);
};
/**
* Get the size of the grid, in pixels. The returned array
* is in the form `[x, y]`.
* @returns {number[]} the grid size
* @memberof platform/features/layout.FixedController#
*/
FixedController.prototype.getGridSize = function () {
return this.gridSize;
};
/**
* Get an array of elements in this panel; these are
* decorated proxies for both selection and display.
* @returns {Array} elements in this panel
*/
FixedController.prototype.getElements = function () {
return this.elementProxies;
};
/**
* Checks if the element should be selected or not.
*
* @param elementProxy the element to check
* @returns {boolean} true if the element should be selected.
*/
FixedController.prototype.shouldSelect = function (elementProxy) {
if (elementProxy.element === this.elementToSelectAfterRefresh) {
delete this.elementToSelectAfterRefresh;
return true;
} else {
return false;
}
};
/**
* Checks if an element is currently selected.
*
* @returns {boolean} true if an element is selected.
*/
FixedController.prototype.isElementSelected = function () {
return (this.selectedElementProxy) ? true : false;
};
/**
* Gets the style for the selected element.
*
* @returns {string} element style
*/
FixedController.prototype.getSelectedElementStyle = function () {
return (this.selectedElementProxy) ? this.selectedElementProxy.style : undefined;
};
/**
* Gets the selected element.
*
* @returns the selected element
*/
FixedController.prototype.getSelectedElement = function () {
return this.selectedElementProxy;
};
/**
* Prevents the event from bubbling up if drag is in progress.
*/
FixedController.prototype.bypassSelection = function ($event) {
if (this.dragInProgress) {
if ($event) {
$event.stopPropagation();
}
return;
}
};
/**
* Get drag handles.
* @returns {platform/features/layout.FixedDragHandle[]}
* drag handles for the current selection
*/
FixedController.prototype.handles = function () {
return this.resizeHandles;
};
/**
* Get the handle to handle dragging to reposition an element.
* @returns {platform/features/layout.FixedDragHandle} the drag handle
*/
FixedController.prototype.moveHandle = function () {
return this.mvHandle;
};
/**
* Gets the selection context.
*
* @param elementProxy the element proxy
* @returns {object} the context object which includes elementProxy
*/
FixedController.prototype.getContext = function (elementProxy) {
return {
elementProxy: elementProxy,
fixedController: this
};
};
/**
* End drag.
*
* @param handle the resize handle
*/
FixedController.prototype.endDrag = function (handle) {
this.dragInProgress = true;
setTimeout(function () {
this.dragInProgress = false;
}.bind(this), 0);
if (handle) {
handle.endDrag();
} else {
this.moveHandle().endDrag();
}
};
FixedController.prototype.mutate = function (path, value) {
this.openmct.objects.mutate(this.newDomainObject, path, value);
};
FixedController.prototype.handleEditing = function (isEditing) {
// Listen for edit mode changes and update selection if necessary.
// Mainly to ensure fixedController is on the selection context when editing.
this.setSelection(this.openmct.selection.get());
}
return FixedController;
}
);

View File

@ -0,0 +1,110 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
[],
function () {
// Drag handle dimensions
var DRAG_HANDLE_SIZE = [6, 6];
/**
* Template-displayable drag handle for an element in fixed
* position mode.
*
* @param elementHandle the element handle
* @param configPath the configuration path of an element
* @param {Object} fixedControl the fixed controller
* @memberof platform/features/layout
* @constructor
*/
function FixedDragHandle(elementHandle, configPath, fixedControl) {
this.elementHandle = elementHandle;
this.configPath = configPath;
this.fixedControl = fixedControl;
}
/**
* Get a CSS style to position this drag handle.
*
* @returns CSS style object (for `ng-style`)
* @memberof platform/features/layout.FixedDragHandle#
*/
FixedDragHandle.prototype.style = function () {
var gridSize = this.elementHandle.getGridSize();
// Adjust from grid to pixel coordinates
var x = this.elementHandle.x() * gridSize[0],
y = this.elementHandle.y() * gridSize[1];
// Convert to a CSS style centered on that point
return {
left: (x - DRAG_HANDLE_SIZE[0] / 2) + 'px',
top: (y - DRAG_HANDLE_SIZE[1] / 2) + 'px',
width: DRAG_HANDLE_SIZE[0] + 'px',
height: DRAG_HANDLE_SIZE[1] + 'px'
};
};
/**
* Start a drag gesture. This should be called when a drag
* begins to track initial state.
*/
FixedDragHandle.prototype.startDrag = function () {
// Cache initial x/y positions
this.dragging = {
x: this.elementHandle.x(),
y: this.elementHandle.y()
};
};
/**
* Continue a drag gesture; update x/y positions.
*
* @param {number[]} delta x/y pixel difference since drag started
*/
FixedDragHandle.prototype.continueDrag = function (delta) {
var gridSize = this.elementHandle.getGridSize();
if (this.dragging) {
// Update x/y positions (snapping to grid)
var newX = this.dragging.x + Math.round(delta[0] / gridSize[0]);
var newY = this.dragging.y + Math.round(delta[1] / gridSize[1]);
this.elementHandle.x(Math.max(0, newX));
this.elementHandle.y(Math.max(0, newY));
this.fixedControl.updateSelectionStyle();
}
};
/**
* End a drag gesture. This should be called when a drag
* concludes to trigger commit of changes.
*/
FixedDragHandle.prototype.endDrag = function () {
this.dragging = undefined;
this.fixedControl.mutate(this.configPath, this.elementHandle.element);
};
return FixedDragHandle;
}
);

View File

@ -0,0 +1,76 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
['./elements/ElementFactory'],
function (ElementFactory) {
/**
* Proxy for configuring a fixed position view via the toolbar.
* @memberof platform/features/layout
* @constructor
* @param {Function} addElementCallback callback to invoke when
* elements are created
* @param $q Angular's $q, for promise-handling
* @param {DialogService} dialogService dialog service to use
* when adding a new element will require user input
*/
function FixedProxy(addElementCallback, $q, dialogService) {
this.factory = new ElementFactory(dialogService);
this.$q = $q;
this.addElementCallback = addElementCallback;
}
/**
* Add a new visual element to this view. Supported types are:
*
* * `fixed.image`
* * `fixed.box`
* * `fixed.text`
* * `fixed.line`
*
* @param {string} type the type of element to add
*/
FixedProxy.prototype.add = function (type) {
var addElementCallback = this.addElementCallback;
// Place a configured element into the view configuration
function addElement(element) {
// Configure common properties of the element
element.x = element.x || 0;
element.y = element.y || 0;
element.width = element.width || 1;
element.height = element.height || 1;
element.type = type;
element.useGrid = true;
// Finally, add it to the view's configuration
addElementCallback(element);
}
// Defer creation to the factory
this.$q.when(this.factory.createElement(type)).then(addElement);
};
return FixedProxy;
}
);

View File

@ -0,0 +1,115 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
[],
function () {
/**
* Handles drag interactions on frames in layouts. This will
* provides new positions/dimensions for frames based on
* relative pixel positions provided; these will take into account
* the grid size (in a snap-to sense) and will enforce some minimums
* on both position and dimensions.
*
* The provided position and dimensions factors will determine
* whether this is a move or a resize, and what type of resize it
* will be. For instance, a position factor of [1, 1]
* will move a frame along with the mouse as the drag
* proceeds, while a dimension factor of [0, 0] will leave
* dimensions unchanged. Combining these in different
* ways results in different handles; a position factor of
* [1, 0] and a dimensions factor of [-1, 0] will implement
* a left-edge resize, as the horizontal position will move
* with the mouse while the horizontal dimensions shrink in
* kind (and vertical properties remain unmodified.)
*
* @param {object} rawPosition the initial position/dimensions
* of the frame being interacted with
* @param {number[]} posFactor the position factor
* @param {number[]} dimFactor the dimensions factor
* @param {number[]} the size of each grid element, in pixels
* @constructor
* @memberof platform/features/layout
*/
function LayoutDrag(rawPosition, posFactor, dimFactor, gridSize) {
this.rawPosition = rawPosition;
this.posFactor = posFactor;
this.dimFactor = dimFactor;
this.gridSize = gridSize;
}
// Convert a delta from pixel coordinates to grid coordinates,
// rounding to whole-number grid coordinates.
function toGridDelta(gridSize, pixelDelta) {
return pixelDelta.map(function (v, i) {
return Math.round(v / gridSize[i]);
});
}
// Utility function to perform element-by-element multiplication
function multiply(array, factors) {
return array.map(function (v, i) {
return v * factors[i];
});
}
// Utility function to perform element-by-element addition
function add(array, other) {
return array.map(function (v, i) {
return v + other[i];
});
}
// Utility function to perform element-by-element max-choosing
function max(array, other) {
return array.map(function (v, i) {
return Math.max(v, other[i]);
});
}
/**
* Get a new position object in grid coordinates, with
* position and dimensions both offset appropriately
* according to the factors supplied in the constructor.
* @param {number[]} pixelDelta the offset from the
* original position, in pixels
*/
LayoutDrag.prototype.getAdjustedPosition = function (pixelDelta) {
var gridDelta = toGridDelta(this.gridSize, pixelDelta);
return {
position: max(add(
this.rawPosition.position,
multiply(gridDelta, this.posFactor)
), [0, 0]),
dimensions: max(add(
this.rawPosition.dimensions,
multiply(gridDelta, this.dimFactor)
), [1, 1])
};
};
return LayoutDrag;
}
);

View File

@ -0,0 +1,105 @@
/*****************************************************************************
* 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',
'../../../commonUI/general/src/services/Overlay'
], function (
$,
Overlay
) {
/**
* 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 MCTTriggerModal($document) {
function link($scope, $element) {
var actions = $scope.domainObject.getCapability('action'),
notebookAction = actions.getActions({key: 'notebook-new-entry'})[0];
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;
var notebookButton = notebookAction ?
[
{
class: 'icon-notebook new-notebook-entry',
title: 'New Notebook Entry',
clickHandler: function (event) {
event.stopPropagation();
notebookAction.perform();
}
}
] : [];
var overlayService = new Overlay ({
$document: $document,
$scope: $scope,
$element: frame,
overlayWillMount: function () {
$(frame).removeClass('frame frame-template');
layoutContainer.removeChild(frame);
},
overlayDidUnmount: function () {
$(frame).addClass('frame frame-template');
layoutContainer.appendChild(frame);
},
browseBarButtons: notebookButton
});
$element.on('click', overlayService.toggleOverlay);
$scope.$on('$destroy', function () {
$element.off('click', overlayService.toggleOverlay);
});
}
return {
restrict: 'A',
link: link
};
}
return MCTTriggerModal;
});

View File

@ -0,0 +1,58 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
[],
function () {
/**
* Utility function for creating getter-setter functions,
* since these are frequently useful for element proxies.
*
* An optional third argument may be supplied in order to
* constrain or modify arguments when using as a setter;
* this argument is a function which takes two arguments
* (the current value for the property, and the requested
* new value.) This is useful when values need to be kept
* in certain ranges; specifically, to keep x/y positions
* non-negative in a fixed position view.
*
* @memberof platform/features/layout
* @constructor
* @param {Object} object the object to get/set values upon
* @param {string} key the property to get/set
* @param {function} [updater] function used to process updates
*/
function AccessorMutator(object, key, updater) {
return function (value) {
if (arguments.length > 0) {
object[key] = updater ?
updater(value, object[key]) :
value;
}
return object[key];
};
}
return AccessorMutator;
}
);

View File

@ -0,0 +1,61 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
['./ElementProxy', './AccessorMutator'],
function (ElementProxy, AccessorMutator) {
/**
* Selection proxy for Box elements in a fixed position view.
* Also serves as a superclass for Text elements, since those
* elements have a superset of Box properties.
*
* Note that arguments here are meant to match those expected
* by `Array.prototype.map`
*
* @memberof platform/features/layout
* @constructor
* @param element the fixed position element, as stored in its
* configuration
* @param index the element's index within its array
* @param {number[]} gridSize the current layout grid size in [x,y] from
* @param {Array} elements the full array of elements
*/
function BoxProxy(element, index, elements, gridSize) {
var proxy = new ElementProxy(element, index, elements, gridSize);
/**
* Get/set this element's fill color. (Omitting the
* argument makes this act as a getter.)
* @method
* @param {string} fill the new fill color
* @returns {string} the fill color
* @memberof platform/features/layout.BoxProxy#
*/
proxy.fill = new AccessorMutator(element, 'fill');
return proxy;
}
return BoxProxy;
}
);

View File

@ -0,0 +1,115 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
[],
function () {
var INITIAL_STATES = {
"fixed.image": {
stroke: "transparent"
},
"fixed.box": {
fill: "#717171",
border: "transparent",
stroke: "transparent"
},
"fixed.line": {
x: 5,
y: 9,
x2: 6,
y2: 6,
stroke: "#717171"
},
"fixed.text": {
fill: "transparent",
stroke: "transparent",
size: "13px"
}
},
DIALOGS = {
"fixed.image": {
name: "Image Properties",
sections: [
{
rows: [
{
key: "url",
control: "textfield",
name: "Image URL",
"cssClass": "l-input-lg",
required: true
}
]
}
]
},
"fixed.text": {
name: "Text Element Properties",
sections: [
{
rows: [
{
key: "text",
control: "textfield",
name: "Text",
required: true
}
]
}
]
}
};
/**
* The ElementFactory creates new instances of elements for the
* fixed position view, prompting for user input where necessary.
* @param {DialogService} dialogService service to request user input
* @memberof platform/features/layout
* @constructor
*/
function ElementFactory(dialogService) {
this.dialogService = dialogService;
}
/**
* Create a new element for the fixed position view.
* @param {string} type the type of element to create
* @returns {Promise|object} the created element, or a promise
* for that element
*/
ElementFactory.prototype.createElement = function (type) {
var initialState = INITIAL_STATES[type] || {};
// Clone that state
initialState = JSON.parse(JSON.stringify(initialState));
// Show a dialog to configure initial state, if appropriate
return DIALOGS[type] ? this.dialogService.getUserInput(
DIALOGS[type],
initialState
) : initialState;
};
return ElementFactory;
}
);

View File

@ -0,0 +1,35 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
['./TelemetryProxy', './ImageProxy', './LineProxy', './BoxProxy', './TextProxy'],
function (TelemetryProxy, ImageProxy, LineProxy, BoxProxy, TextProxy) {
return {
"fixed.telemetry": TelemetryProxy,
"fixed.line": LineProxy,
"fixed.box": BoxProxy,
"fixed.image": ImageProxy,
"fixed.text": TextProxy
};
}
);

View File

@ -0,0 +1,209 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
['./AccessorMutator', './ResizeHandle', './UnitAccessorMutator'],
function (AccessorMutator, ResizeHandle, UnitAccessorMutator) {
// Index deltas for changes in order
var ORDERS = {
top: Number.POSITIVE_INFINITY,
up: 1,
down: -1,
bottom: Number.NEGATIVE_INFINITY
};
// Mininmum pixel height and width for objects
var MIN_WIDTH = 10;
var MIN_HEIGHT = 10;
// Ensure a value is non-negative (for x/y setters)
function clamp(value) {
return Math.max(value, 0);
}
/**
* Abstract superclass for other classes which provide useful
* interfaces upon an elements in a fixed position view.
* This handles the generic operations (e.g. remove) so that
* subclasses only need to implement element-specific behaviors.
*
* Note that arguments here are meant to match those expected
* by `Array.prototype.map`
*
* @memberof platform/features/layout
* @constructor
* @param element the fixed position element, as stored in its
* configuration
* @param index the element's index within its array
* @param {Array} elements the full array of elements
* @param {number[]} gridSize the current layout grid size in [x,y] from
*/
function ElementProxy(element, index, elements, gridSize) {
/**
* The element as stored in the view configuration.
* @memberof platform/features/layout.ElementProxy#
*/
this.element = element;
/**
* The current grid size of the layout.
* @memberof platform/features/layout.ElementProxy#
*/
this.gridSize = gridSize || [1,1]; //Ensure a reasonable default
/**
* Get and/or set the x position of this element.
* Units are in fixed position grid space.
* @param {number} [x] the new x position (if setting)
* @returns {number} the x position
* @memberof platform/features/layout.ElementProxy#
*/
this.x = new AccessorMutator(element, 'x', clamp);
/**
* Get and/or set the y position of this element.
* Units are in fixed position grid space.
* @param {number} [y] the new y position (if setting)
* @returns {number} the y position
* @memberof platform/features/layout.ElementProxy#
*/
this.y = new AccessorMutator(element, 'y', clamp);
/**
* Get and/or set the stroke color of this element.
* @param {string} [stroke] the new stroke color (if setting)
* @returns {string} the stroke color
* @memberof platform/features/layout.ElementProxy#
*/
this.stroke = new AccessorMutator(element, 'stroke');
/**
* Get and/or set the width of this element.
* Units are in fixed position grid space.
* @param {number} [w] the new width (if setting)
* @returns {number} the width
* @memberof platform/features/layout.ElementProxy#
*/
this.width = new AccessorMutator(element, 'width');
/**
* Get and/or set the height of this element.
* Units are in fixed position grid space.
* @param {number} [h] the new height (if setting)
* @returns {number} the height
* @memberof platform/features/layout.ElementProxy#
*/
this.height = new AccessorMutator(element, 'height');
this.useGrid = new UnitAccessorMutator(this);
this.index = index;
this.elements = elements;
this.resizeHandles = [new ResizeHandle(this, this.element)];
}
/**
* Change the display order of this element.
* @param {string} o where to move this element;
* one of "top", "up", "down", or "bottom"
* @return {Array} the full array of elements
*/
ElementProxy.prototype.order = function (o) {
var index = this.index,
elements = this.elements,
element = this.element,
delta = ORDERS[o] || 0,
desired = Math.max(
Math.min(index + delta, elements.length - 1),
0
);
// Move to the desired index, if this is a change
if ((desired !== index) && (elements[index] === element)) {
// Splice out the current element
elements.splice(index, 1);
// Splice it back in at the correct index
elements.splice(desired, 0, element);
// Track change in index (proxy should be recreated
// anyway, but be consistent)
this.index = desired;
}
return elements;
};
/**
* Get handles to control specific features of this element,
* e.g. corner size.
* @return {platform/features/layout.ElementHandle[]} handles
* for moving/resizing this element
*/
ElementProxy.prototype.handles = function () {
return this.resizeHandles;
};
/**
* Returns which grid size the element is currently using.
* @return {number[]} The current grid size in [x,y] form if the element
* is currently using the grid, [1,1] if it is using
* pixels.
*/
ElementProxy.prototype.getGridSize = function () {
var gridSize;
// Default to using the grid if useGrid was not defined
if (typeof this.element.useGrid === 'undefined') {
this.element.useGrid = true;
}
if (this.element.useGrid) {
gridSize = this.gridSize;
} else {
gridSize = [1,1];
}
return gridSize;
};
/**
* Set the current grid size stored by this element proxy
* @param {number[]} gridSize The current layout grid size in [x,y] form
*/
ElementProxy.prototype.setGridSize = function (gridSize) {
this.gridSize = gridSize;
};
/**
* Get the current minimum element width in grid units
* @return {number} The current minimum element width
*/
ElementProxy.prototype.getMinWidth = function () {
return Math.ceil(MIN_WIDTH / this.getGridSize()[0]);
};
/**
* Get the current minimum element height in grid units
* @return {number} The current minimum element height
*/
ElementProxy.prototype.getMinHeight = function () {
return Math.ceil(MIN_HEIGHT / this.getGridSize()[1]);
};
return ElementProxy;
}
);

View File

@ -0,0 +1,58 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
['./ElementProxy', './AccessorMutator'],
function (ElementProxy, AccessorMutator) {
/**
* Selection proxy for Image elements in a fixed position view.
*
* Note that arguments here are meant to match those expected
* by `Array.prototype.map`
*
* @memberof platform/features/layout
* @constructor
* @param element the fixed position element, as stored in its
* configuration
* @param index the element's index within its array
* @param {Array} elements the full array of elements
* @param {number[]} gridSize the current layout grid size in [x,y] from
* @augments {platform/features/layout.ElementProxy}
*/
function ImageProxy(element, index, elements, gridSize) {
var proxy = new ElementProxy(element, index, elements, gridSize);
/**
* Get and/or set the displayed text of this element.
* @param {string} [text] the new text (if setting)
* @returns {string} the text
* @memberof platform/features/layout.ImageProxy#
*/
proxy.url = new AccessorMutator(element, 'url');
return proxy;
}
return ImageProxy;
}
);

View File

@ -0,0 +1,94 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
[],
function () {
/**
* Handle for changing x/y position of a line's end point.
* This is used to support drag handles for line elements
* in a fixed position view. Field names for opposite ends
* are provided to avoid zero-length lines.
* @memberof platform/features/layout
* @constructor
* @param element the line element
* @param {string} xProperty field which stores x position
* @param {string} yProperty field which stores y position
* @param {string} xOther field which stores x of other end
* @param {string} yOther field which stores y of other end
* @implements {platform/features/layout.ElementHandle}
*/
function LineHandle(element, elementProxy, xProperty, yProperty, xOther, yOther) {
this.elementProxy = elementProxy;
this.element = element;
this.xProperty = xProperty;
this.yProperty = yProperty;
this.xOther = xOther;
this.yOther = yOther;
}
LineHandle.prototype.x = function (value) {
var element = this.element,
xProperty = this.xProperty,
yProperty = this.yProperty,
xOther = this.xOther,
yOther = this.yOther;
if (arguments.length > 0) {
// Ensure we stay in view
value = Math.max(value, 0);
// Make sure end points will still be different
if (element[yOther] !== element[yProperty] ||
element[xOther] !== value) {
element[xProperty] = value;
}
}
return element[xProperty];
};
LineHandle.prototype.y = function (value) {
var element = this.element,
xProperty = this.xProperty,
yProperty = this.yProperty,
xOther = this.xOther,
yOther = this.yOther;
if (arguments.length > 0) {
// Ensure we stay in view
value = Math.max(value, 0);
// Make sure end points will still be different
if (element[xOther] !== element[xProperty] ||
element[yOther] !== value) {
element[yProperty] = value;
}
}
return element[yProperty];
};
LineHandle.prototype.getGridSize = function () {
return this.elementProxy.getGridSize();
};
return LineHandle;
}
);

View File

@ -0,0 +1,171 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
['./ElementProxy', './LineHandle', './AccessorMutator'],
function (ElementProxy, LineHandle, AccessorMutator) {
/**
* Selection/diplay proxy for line elements of a fixed position
* view.
* @memberof platform/features/layout
* @constructor
* @param element the fixed position element, as stored in its
* configuration
* @param index the element's index within its array
* @param {Array} elements the full array of elements
* @param {number[]} gridSize the current layout grid size in [x,y] from
* @augments {platform/features/layout.ElementProxy}
*/
function LineProxy(element, index, elements, gridSize) {
var proxy = new ElementProxy(element, index, elements, gridSize),
handles = [
new LineHandle(element, proxy, 'x', 'y', 'x2', 'y2'),
new LineHandle(element, proxy, 'x2', 'y2', 'x', 'y')
];
/**
* Gets style specific to line proxy.
*/
proxy.getStyle = function () {
var layoutGridSize = proxy.getGridSize();
return {
left: (layoutGridSize[0] * proxy.x()) + 'px',
top: (layoutGridSize[1] * proxy.y()) + 'px',
width: (layoutGridSize[0] * proxy.width()) + 'px',
height: (layoutGridSize[1] * proxy.height()) + 'px'
};
};
/**
* Get the top-left x coordinate, in grid space, of
* this line's bounding box.
* @returns {number} the x coordinate
* @memberof platform/features/layout.LineProxy#
*/
proxy.x = function (v) {
var x = Math.min(element.x, element.x2),
delta = Math.max(v, 0) - x;
if (arguments.length > 0 && delta) {
element.x += delta;
element.x2 += delta;
}
return x;
};
/**
* Get the top-left y coordinate, in grid space, of
* this line's bounding box.
* @returns {number} the y coordinate
* @memberof platform/features/layout.LineProxy#
*/
proxy.y = function (v) {
var y = Math.min(element.y, element.y2),
delta = Math.max(v, 0) - y;
if (arguments.length > 0 && delta) {
element.y += delta;
element.y2 += delta;
}
return y;
};
/**
* Get the width, in grid space, of
* this line's bounding box.
* @returns {number} the width
* @memberof platform/features/layout.LineProxy#
*/
proxy.width = function () {
return Math.max(Math.abs(element.x - element.x2), 1);
};
/**
* Get the height, in grid space, of
* this line's bounding box.
* @returns {number} the height
* @memberof platform/features/layout.LineProxy#
*/
proxy.height = function () {
return Math.max(Math.abs(element.y - element.y2), 1);
};
/**
* Get the x position, in grid units relative to
* the top-left corner, of the first point in this line
* segment.
* @returns {number} the x position of the first point
* @memberof platform/features/layout.LineProxy#
*/
proxy.x1 = function () {
return element.x - proxy.x();
};
/**
* Get the y position, in grid units relative to
* the top-left corner, of the first point in this line
* segment.
* @returns {number} the y position of the first point
* @memberof platform/features/layout.LineProxy#
*/
proxy.y1 = function () {
return element.y - proxy.y();
};
/**
* Get the x position, in grid units relative to
* the top-left corner, of the second point in this line
* segment.
* @returns {number} the x position of the second point
* @memberof platform/features/layout.LineProxy#
*/
proxy.x2 = function () {
return element.x2 - proxy.x();
};
/**
* Get the y position, in grid units relative to
* the top-left corner, of the second point in this line
* segment.
* @returns {number} the y position of the second point
* @memberof platform/features/layout.LineProxy#
*/
proxy.y2 = function () {
return element.y2 - proxy.y();
};
/**
* Get element handles for changing the position of end
* points of this line.
* @returns {LineHandle[]} line handles for both end points
* @memberof platform/features/layout.LineProxy#
*/
proxy.handles = function () {
return handles;
};
return proxy;
}
return LineProxy;
}
);

View File

@ -0,0 +1,72 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
[],
function () {
/**
* @interface platform/features/layout.ElementHandle
* @private
*/
/**
* Handle for changing width/height properties of an element.
* This is used to support drag handles for different
* element types in a fixed position view.
* @memberof platform/features/layout
* @constructor
*/
function ResizeHandle(elementProxy, element) {
this.elementProxy = elementProxy;
this.element = element;
}
ResizeHandle.prototype.x = function (value) {
var element = this.element;
if (arguments.length > 0) {
element.width = Math.max(
this.elementProxy.getMinWidth(),
value - element.x
);
}
return element.x + element.width;
};
ResizeHandle.prototype.y = function (value) {
var element = this.element;
if (arguments.length > 0) {
element.height = Math.max(
this.elementProxy.getMinHeight(),
value - element.y
);
}
return element.y + element.height;
};
ResizeHandle.prototype.getGridSize = function () {
return this.elementProxy.getGridSize();
};
return ResizeHandle;
}
);

View File

@ -20,34 +20,37 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
import Migrations from './Migrations.js'
define(
['./TextProxy'],
function (TextProxy) {
export default function () {
return function (openmct) {
let migrations = Migrations(openmct);
/**
* Selection proxy for telemetry elements in a fixed position view.
*
* Note that arguments here are meant to match those expected
* by `Array.prototype.map`
*
* @memberof platform/features/layout
* @constructor
* @param element the fixed position element, as stored in its
* configuration
* @param index the element's index within its array
* @param {Array} elements the full array of elements
* @param {number[]} gridSize the current layout grid size in [x,y] form
* @augments {platform/features/layout.ElementProxy}
*/
function TelemetryProxy(element, index, elements, gridSize) {
var proxy = new TextProxy(element, index, elements, gridSize);
function needsMigration(domainObject) {
return migrations.some(m => m.check(domainObject));
// Expose the domain object identifier
proxy.id = element.id;
// Don't expose text configuration
delete proxy.text;
return proxy;
}
function migrateObject(domainObject) {
return migrations.filter(m => m.check(domainObject))[0]
.migrate(domainObject);
}
let wrappedFunction = openmct.objects.get;
openmct.objects.get = function migrate(identifier) {
return wrappedFunction.apply(openmct.objects, [identifier])
.then(function (object) {
if (needsMigration(object)) {
migrateObject(object)
.then(newObject => {
openmct.objects.mutate(newObject, 'persisted', Date.now());
return newObject;
});
}
return object;
});
}
};
}
return TelemetryProxy;
}
);

View File

@ -0,0 +1,75 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
['./BoxProxy', './AccessorMutator'],
function (BoxProxy, AccessorMutator) {
/**
* Selection proxy for Text elements in a fixed position view.
*
* Note that arguments here are meant to match those expected
* by `Array.prototype.map`
*
* @memberof platform/features/layout
* @constructor
* @param element the fixed position element, as stored in its
* configuration
* @param index the element's index within its array
* @param {Array} elements the full array of elements
* @param {number[]} gridSize the current layout grid size in [x,y] from
* @augments {platform/features/layout.ElementProxy}
*/
function TextProxy(element, index, elements, gridSize) {
var proxy = new BoxProxy(element, index, elements, gridSize);
/**
* Get and/or set the text color of this element.
* @param {string} [color] the new text color (if setting)
* @returns {string} the text color
* @memberof platform/features/layout.TextProxy#
*/
proxy.color = new AccessorMutator(element, 'color');
/**
* Get and/or set the displayed text of this element.
* @param {string} [text] the new text (if setting)
* @returns {string} the text
* @memberof platform/features/layout.TextProxy#
*/
proxy.text = new AccessorMutator(element, 'text');
/**
* Get and/or set the text size of this element.
*
* @param {string} [size] the new text size (if setting)
* @returns {string} the text size
* @memberof platform/features/layout.TextProxy#
*/
proxy.size = new AccessorMutator(element, 'size');
return proxy;
}
return TextProxy;
}
);

View File

@ -0,0 +1,92 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
[],
function () {
/**
* Variant of AccessorMutator to handle the specific case of updating
* useGrid, in order update the positions appropriately from within
* the scope of UnitAccessorMutator
*
* @memberof platform/features/layout
* @constructor
* @param {ElementProxy} proxy ElementProxy object to perform the update
* upon
*/
function UnitAccessorMutator(elementProxy) {
var self = this;
this.elementProxy = elementProxy;
return function (useGrid) {
var current = elementProxy.element.useGrid;
if (arguments.length > 0) {
elementProxy.element.useGrid = useGrid;
if (useGrid && !current) {
self.convertCoordsTo('grid');
} else if (!useGrid && current) {
self.convertCoordsTo('px');
}
}
return elementProxy.element.useGrid;
};
}
/**
* For the elementProxy object called upon, convert its element's
* coordinates and size from pixels to grid units, or vice-versa.
* @param {string} unit When called with 'px', converts grid units to
* pixels; when called with 'grid', snaps element
* to grid units
*/
UnitAccessorMutator.prototype.convertCoordsTo = function (unit) {
var proxy = this.elementProxy,
gridSize = proxy.gridSize,
element = proxy.element,
minWidth = proxy.getMinWidth(),
minHeight = proxy.getMinHeight();
if (unit === 'px') {
element.x = element.x * gridSize[0];
element.y = element.y * gridSize[1];
element.width = element.width * gridSize[0];
element.height = element.height * gridSize[1];
if (element.x2 && element.y2) {
element.x2 = element.x2 * gridSize[0];
element.y2 = element.y2 * gridSize[1];
}
} else if (unit === 'grid') {
element.x = Math.round(element.x / gridSize[0]);
element.y = Math.round(element.y / gridSize[1]);
element.width = Math.max(Math.round(element.width / gridSize[0]), minWidth);
element.height = Math.max(Math.round(element.height / gridSize[1]), minHeight);
if (element.x2 && element.y2) {
element.x2 = Math.round(element.x2 / gridSize[0]);
element.y2 = Math.round(element.y2 / gridSize[1]);
}
}
};
return UnitAccessorMutator;
}
);

View File

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

View File

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

View File

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

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -19,18 +19,21 @@
this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information.
-->
<span ng-controller="SnapshotPreviewController"
class='form-control shell'>
<span class='field control {{structure.cssClass}}'>
<image
class="c-ne__embed__snap-thumb"
src="{{imageUrl || structure.src}}"
ng-click="previewImage(imageUrl || structure.src)"
name="mctControl">
</image>
<br>
<a title="Annotate" class="s-button icon-pencil" ng-click="annotateImage(ngModel, field, imageUrl || structure.src)">
<span class="title-label">Annotate</span>
</a>
<div
class="l-fixed-position-text l-telemetry"
ng-style="{ background: ngModel.fill(), 'border-color': ngModel.stroke(), color: ngModel.color(), 'font-size': ngModel.size() }"
>
<span
class="l-elem l-value l-obj-val-format"
data-value="{{ngModel.value}}"
ng-class="ngModel.cssClass"
>
{{ngModel.value}}
</span>
</span>
<span
class="l-elem l-title"
ng-show="ngModel.element.titled"
>
{{ngModel.name}}
</span>
</div>

View File

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

View File

@ -0,0 +1,62 @@
<!--
Open MCT, Copyright (c) 2014-2018, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
Open MCT is licensed under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
Open MCT includes source code licensed under additional open source
licenses. See the Open Source Licenses file (LICENSES.md) included with
this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information.
-->
<div class="t-fixed-position l-fixed-position"
ng-controller="FixedController as controller">
<!-- Background grid -->
<div class="l-fixed-position__grid-holder l-grid-holder c-grid" ng-click="controller.bypassSelection($event)">
<div class="c-grid__x l-grid l-grid-x"
ng-if="!controller.getGridSize()[0] < 3"
ng-style="{ 'background-size': controller.getGridSize() [0] + 'px 100%' }"></div>
<div class="c-grid__y l-grid l-grid-y"
ng-if="!controller.getGridSize()[1] < 3"
ng-style="{ 'background-size': '100% ' + controller.getGridSize() [1] + 'px' }"></div>
</div>
<!-- Fixed position elements -->
<div ng-repeat="element in controller.getElements()"
class="l-fixed-position-item s-selectable s-moveable is-selectable is-moveable"
ng-style="element.style"
mct-selectable="controller.getContext(element)"
mct-init-select="controller.shouldSelect(element)">
<mct-include key="element.template"
parameters="{ gridSize: controller.getGridSize() }"
ng-model="element">
</mct-include>
</div>
<!-- Selection highlight, handles -->
<span class="c-frame-edit" ng-if="controller.isElementSelected()">
<div class="c-frame-edit__move"
mct-drag-down="controller.moveHandle().startDrag()"
mct-drag="controller.moveHandle().continueDrag(delta)"
mct-drag-up="controller.endDrag()"
ng-style="controller.getSelectedElementStyle()">
</div>
<div ng-repeat="handle in controller.handles()"
class="c-frame-edit__handle c-frame-edit__handle--nwse"
ng-style="handle.style()"
mct-drag-down="handle.startDrag()"
mct-drag="handle.continueDrag(delta)"
mct-drag-up="controller.endDrag(handle)">
</div>
</span>
</div>

View File

@ -0,0 +1,49 @@
<!--
Open MCT, Copyright (c) 2014-2018, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
Open MCT is licensed under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
Open MCT includes source code licensed under additional open source
licenses. See the Open Source Licenses file (LICENSES.md) included with
this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information.
-->
<div class="frame frame-template t-frame-inner abs has-local-controls t-object-type-{{ domainObject.getModel().type }}">
<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 h-local-controls local-controls-hidden">
<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-trigger-modal>
</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

@ -0,0 +1,641 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
[
"../src/FixedController",
"zepto"
],
function (
FixedController,
$
) {
describe("The Fixed Position controller", function () {
var mockScope,
mockQ,
mockDialogService,
mockFormatter,
mockDomainObject,
mockEvent,
testGrid,
testModel,
testConfiguration,
mockOpenMCT,
mockTelemetryAPI,
mockCompositionAPI,
mockCompositionCollection,
mockChildren,
mockConductor,
mockMetadata,
mockTimeSystem,
mockLimitEvaluator,
mockSelection,
mockObjects,
mockNewDomainObject,
unlistenFunc,
$element = [],
selectable = [],
controller;
// Utility function; find a $on calls for a given expression.
function findOn(expr) {
var on;
mockScope.$on.calls.all().forEach(function (call) {
if (call.args[0] === expr) {
on = call.args[1];
}
});
return on;
}
function makeMockDomainObject(id) {
return {
identifier: {
key: "domainObject-" + id,
namespace: ""
},
name: "Point " + id
};
}
beforeEach(function () {
mockScope = jasmine.createSpyObj(
'$scope',
["$on", "$watch", "$digest", "commit"]
);
mockQ = jasmine.createSpyObj('$q', ['when']);
mockDialogService = jasmine.createSpyObj(
'dialogService',
['getUserInput']
);
mockFormatter = jasmine.createSpyObj(
'telemetryFormatter',
['format']
);
mockFormatter.format.and.callFake(function (valueMetadata) {
return "Formatted " + valueMetadata.value;
});
mockConductor = jasmine.createSpyObj('conductor', [
'on',
'off',
'bounds',
'timeSystem',
'clock'
]);
mockConductor.bounds.and.returnValue({});
mockTimeSystem = {
metadata: {
key: 'key'
}
};
mockConductor.timeSystem.and.returnValue(mockTimeSystem);
mockEvent = jasmine.createSpyObj(
'event',
['preventDefault']
);
mockTelemetryAPI = jasmine.createSpyObj('telemetry',
[
'subscribe',
'request',
'isTelemetryObject',
'getMetadata',
'limitEvaluator',
'getValueFormatter'
]
);
mockTelemetryAPI.isTelemetryObject.and.returnValue(true);
mockTelemetryAPI.request.and.returnValue(Promise.resolve([]));
testGrid = [123, 456];
testModel = {
composition: ['a', 'b', 'c'],
layoutGrid: testGrid
};
testConfiguration = { elements: [
{ type: "fixed.telemetry", id: 'a', x: 1, y: 1, useGrid: true},
{ type: "fixed.telemetry", id: 'b', x: 1, y: 1, useGrid: true},
{ type: "fixed.telemetry", id: 'c', x: 1, y: 1, useGrid: true}
]};
mockChildren = testModel.composition.map(makeMockDomainObject);
mockCompositionCollection = jasmine.createSpyObj('compositionCollection', [
'load',
'on',
'off'
]);
mockCompositionAPI = jasmine.createSpyObj('composition', [
'get'
]);
mockCompositionAPI.get.and.returnValue(mockCompositionCollection);
mockCompositionCollection.load.and.returnValue(
Promise.resolve(mockChildren)
);
mockScope.model = testModel;
mockScope.configuration = testConfiguration;
mockNewDomainObject = jasmine.createSpyObj("newDomainObject", [
'layoutGrid',
'configuration',
'composition'
]);
mockNewDomainObject.layoutGrid = testGrid;
mockNewDomainObject.configuration = {
'fixed-display': testConfiguration
};
mockNewDomainObject.composition = ['a', 'b', 'c'];
mockDomainObject = jasmine.createSpyObj(
'domainObject',
['getId', 'getModel', 'getCapability', 'useCapability']
);
mockDomainObject.useCapability.and.returnValue(mockNewDomainObject);
mockScope.domainObject = mockDomainObject;
selectable[0] = {
context: {
oldItem: mockDomainObject
}
};
mockSelection = jasmine.createSpyObj("selection", [
'select',
'on',
'off',
'get'
]);
mockSelection.get.and.returnValue([]);
unlistenFunc = jasmine.createSpy("unlisten");
mockObjects = jasmine.createSpyObj('objects', [
'observe',
'get'
]);
mockObjects.observe.and.returnValue(unlistenFunc);
mockOpenMCT = {
time: mockConductor,
telemetry: mockTelemetryAPI,
composition: mockCompositionAPI,
selection: mockSelection,
objects: mockObjects
};
$element = $('<div></div>');
spyOn($element[0], 'click');
mockMetadata = jasmine.createSpyObj('mockMetadata', [
'valuesForHints',
'value',
'values'
]);
mockMetadata.value.and.returnValue({
key: 'value'
});
mockMetadata.valuesForHints.and.callFake(function (hints) {
if (hints === ['domain']) {
return [{
key: 'time'
}];
} else {
return [{
key: 'value'
}];
}
});
mockLimitEvaluator = jasmine.createSpyObj('limitEvaluator', [
'evaluate'
]);
mockLimitEvaluator.evaluate.and.returnValue({});
mockTelemetryAPI.getMetadata.and.returnValue(mockMetadata);
mockTelemetryAPI.limitEvaluator.and.returnValue(mockLimitEvaluator);
mockTelemetryAPI.getValueFormatter.and.returnValue(mockFormatter);
controller = new FixedController(
mockScope,
mockQ,
mockDialogService,
mockOpenMCT,
$element
);
spyOn(controller, "mutate");
});
it("subscribes a domain object", function () {
var object = makeMockDomainObject("mock");
return controller.getTelemetry(object).then(function () {
expect(mockTelemetryAPI.subscribe).toHaveBeenCalledWith(
object,
jasmine.any(Function),
jasmine.any(Object)
);
});
});
it("releases subscription when a domain objects is removed", function () {
var unsubscribe = jasmine.createSpy('unsubscribe');
var unsubscribePromise = new Promise(function (resolve) {
unsubscribe.and.callFake(resolve);
});
var object = makeMockDomainObject("mock");
mockTelemetryAPI.subscribe.and.returnValue(unsubscribe);
return controller.getTelemetry(object).then(function () {
controller.onCompositionRemove(object.identifier);
return unsubscribePromise;
}).then(function () {
expect(unsubscribe).toHaveBeenCalled();
});
});
it("exposes visible elements based on configuration", function () {
var elements = controller.getElements();
expect(elements.length).toEqual(3);
expect(elements[0].id).toEqual('a');
expect(elements[1].id).toEqual('b');
expect(elements[2].id).toEqual('c');
});
it("allows elements to be selected", function () {
selectable[0].context.elementProxy = controller.getElements()[1];
mockOpenMCT.selection.on.calls.mostRecent().args[1](selectable);
expect(controller.isElementSelected()).toBe(true);
});
it("allows selection retrieval", function () {
var elements = controller.getElements();
selectable[0].context.elementProxy = elements[1];
mockOpenMCT.selection.on.calls.mostRecent().args[1](selectable);
expect(controller.getSelectedElement()).toEqual(elements[1]);
});
it("selects the parent view when selected element is removed", function () {
var elements = controller.getElements();
selectable[0].context.elementProxy = elements[1];
mockOpenMCT.selection.on.calls.mostRecent().args[1](selectable);
controller.remove(elements[1]);
expect($element[0].click).toHaveBeenCalled();
});
it("retains selections during refresh", function () {
// Get elements; remove one of them; trigger refresh.
// Same element (at least by index) should still be selected.
var elements = controller.getElements();
selectable[0].context.elementProxy = elements[1];
mockOpenMCT.selection.on.calls.mostRecent().args[1](selectable);
expect(controller.getSelectedElement()).toEqual(elements[1]);
controller.remove(elements[2]);
elements = controller.getElements();
// Verify removal, as test assumes this
expect(elements.length).toEqual(2);
expect(controller.shouldSelect(elements[1])).toBe(true);
});
it("Displays received values for telemetry elements", function () {
var elements;
var mockTelemetry = {
time: 100,
value: 200
};
var testElement = {};
var telemetryObject = {
identifier: {
key: '12345'
}
};
mockConductor.clock.and.returnValue({});
controller.elementProxiesById = {};
controller.elementProxiesById['12345'] = [testElement];
controller.elementProxies = [testElement];
controller.subscribeToObject(telemetryObject);
mockTelemetryAPI.subscribe.calls.mostRecent().args[1](mockTelemetry);
return new Promise(function (resolve) {
mockScope.$digest.and.callFake(resolve);
}).then(function () {
// Get elements that controller is now exposing
elements = controller.getElements();
// Formatted values should be available
expect(elements[0].value).toEqual("Formatted 200");
});
});
it("updates elements styles when grid size changes", function () {
// Grid size is initially set to testGrid which is [123, 456]
var originalLeft = controller.getElements()[0].style.left;
// Change the grid size
controller.updateElementPositions([20, 20]);
expect(controller.getElements()[0].style.left).not.toEqual(originalLeft);
});
it("listens for drop events", function () {
mockScope.domainObject = mockDomainObject;
mockScope.model = testModel;
// Layout should position panels according to
// where the user dropped them, so it needs to
// listen for drop events.
expect(mockScope.$on).toHaveBeenCalledWith(
'mctDrop',
jasmine.any(Function)
);
// Verify precondition
expect(testConfiguration.elements.length).toEqual(3);
// Notify that a drop occurred
testModel.composition.push('d');
mockObjects.get.and.returnValue(Promise.resolve([]));
findOn('mctDrop')(
mockEvent,
'd',
{ x: 300, y: 100 }
);
// Should have added an element
expect(testConfiguration.elements.length).toEqual(4);
// ...and prevented default...
expect(mockEvent.preventDefault).toHaveBeenCalled();
});
it("ignores drops when default has been prevented", function () {
// Avoids redundant drop-handling, WTD-1233
mockEvent.defaultPrevented = true;
// Notify that a drop occurred
testModel.composition.push('d');
findOn('mctDrop')(
mockEvent,
'd',
{ x: 300, y: 100 }
);
// Should NOT have added an element
expect(testConfiguration.elements.length).toEqual(3);
});
it("unsubscribes when destroyed", function () {
var unsubscribe = jasmine.createSpy('unsubscribe');
var object = makeMockDomainObject("mock");
mockTelemetryAPI.subscribe.and.returnValue(unsubscribe);
return controller.getTelemetry(object).then(function () {
expect(unsubscribe).not.toHaveBeenCalled();
// Destroy the scope
findOn('$destroy')();
expect(unsubscribe).toHaveBeenCalled();
});
});
it("exposes its grid size", function () {
expect(controller.getGridSize()).toEqual(testGrid);
});
it("exposes drag handles", function () {
var handles;
selectable[0].context.elementProxy = controller.getElements()[1];
mockOpenMCT.selection.on.calls.mostRecent().args[1](selectable);
// Should have a non-empty array of handles
handles = controller.handles();
expect(handles).toEqual(jasmine.any(Array));
expect(handles.length).not.toEqual(0);
// And they should have start/continue/end drag methods
handles.forEach(function (handle) {
expect(handle.startDrag).toEqual(jasmine.any(Function));
expect(handle.continueDrag).toEqual(jasmine.any(Function));
expect(handle.endDrag).toEqual(jasmine.any(Function));
});
});
it("exposes a move handle", function () {
selectable[0].context.elementProxy = controller.getElements()[1];
mockOpenMCT.selection.on.calls.mostRecent().args[1](selectable);
// Should have a move handle
var handle = controller.moveHandle();
// And it should have start/continue/end drag methods
expect(handle.startDrag).toEqual(jasmine.any(Function));
expect(handle.continueDrag).toEqual(jasmine.any(Function));
expect(handle.endDrag).toEqual(jasmine.any(Function));
});
it("updates selection style during drag", function () {
var oldStyle;
selectable[0].context.elementProxy = controller.getElements()[1];
mockOpenMCT.selection.on.calls.mostRecent().args[1](selectable);
// Get style
oldStyle = controller.getSelectedElementStyle();
// Start a drag gesture
controller.moveHandle().startDrag();
// Haven't moved yet; style shouldn't have updated yet
expect(controller.getSelectedElementStyle()).toEqual(oldStyle);
// Drag a little
controller.moveHandle().continueDrag([1000, 100]);
// Style should have been updated
expect(controller.getSelectedElementStyle()).not.toEqual(oldStyle);
});
it("cleans up selection on scope destroy", function () {
expect(mockScope.$on).toHaveBeenCalledWith(
'$destroy',
jasmine.any(Function)
);
mockScope.$on.calls.mostRecent().args[1]();
expect(mockOpenMCT.selection.off).toHaveBeenCalledWith(
'change',
jasmine.any(Function)
);
});
describe("on display bounds changes", function () {
var testBounds;
var boundsChangeCallback;
var objectOne;
var objectTwo;
beforeEach(function () {
testBounds = { start: 123, end: 321 };
boundsChangeCallback = mockConductor.on.calls.mostRecent().args[1];
objectOne = {};
objectTwo = {};
controller.telemetryObjects = [
objectOne,
objectTwo
];
spyOn(controller, "fetchHistoricalData");
controller.fetchHistoricalData.and.callThrough();
});
it("registers a bounds change listener", function () {
expect(mockConductor.on).toHaveBeenCalledWith("bounds", jasmine.any(Function));
});
it("requests only a single point", function () {
mockConductor.clock.and.returnValue(undefined);
boundsChangeCallback(testBounds);
expect(mockTelemetryAPI.request.calls.count()).toBe(2);
mockTelemetryAPI.request.calls.all().forEach(function (call) {
expect(call.args[1].size).toBe(1);
});
});
it("Does not fetch historical data on tick", function () {
boundsChangeCallback(testBounds, true);
expect(mockTelemetryAPI.request.calls.count()).toBe(0);
});
});
describe("on receipt of telemetry", function () {
var mockTelemetryObject;
var testValue;
var testElement;
beforeEach(function () {
mockTelemetryObject = {
identifier: {
key: '12345'
}
};
testValue = 30;
testElement = {};
controller.elementProxiesById = {};
controller.elementProxiesById['12345'] = [testElement];
controller.elementProxies = [testElement];
});
it("updates displayed values from historical telemetry", function () {
spyOn(controller, "updateView");
controller.updateView.and.callThrough();
mockTelemetryAPI.request.and.returnValue(Promise.resolve([{
time: 100,
value: testValue
}]));
controller.fetchHistoricalData(mockTelemetryObject);
return new Promise(function (resolve) {
mockScope.$digest.and.callFake(resolve);
}).then(function () {
expect(controller.updateView).toHaveBeenCalled();
expect(controller.getElements()[0].value)
.toEqual("Formatted " + testValue);
});
});
it("selects an range value to display, if available", function () {
mockMetadata.valuesForHints.and.returnValue([
{
key: 'range',
source: 'range'
}
]);
var key = controller.chooseValueMetadataToDisplay(mockMetadata).source;
expect(key).toEqual('range');
});
it("selects the first non-domain value to display, if no range available", function () {
mockMetadata.valuesForHints.and.returnValue([]);
mockMetadata.values.and.returnValue([
{
key: 'domain',
source: 'domain',
hints: {
domain: 1
}
},
{
key: 'image',
source: 'image',
hints: {
image: 1
}
}
]);
var key = controller.chooseValueMetadataToDisplay(mockMetadata).source;
expect(key).toEqual('image');
});
it("reflects limit status", function () {
mockLimitEvaluator.evaluate.and.returnValue({cssClass: "alarm-a"});
controller.updateView(mockTelemetryObject, [{
time: 100,
value: testValue
}]);
return new Promise(function (resolve) {
mockScope.$digest.and.callFake(resolve);
}).then(function () {
// Limit-based CSS classes should be available
expect(controller.getElements()[0].cssClass).toEqual("alarm-a");
});
});
it("listens for selection change events", function () {
expect(mockOpenMCT.selection.on).toHaveBeenCalledWith(
'change',
jasmine.any(Function)
);
});
});
});
}
);

View File

@ -0,0 +1,92 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
['../src/FixedDragHandle'],
function (FixedDragHandle) {
var TEST_GRID_SIZE = [13, 33];
describe("A fixed position drag handle", function () {
var mockElementHandle,
mockConfigPath,
mockFixedControl,
handle;
beforeEach(function () {
mockElementHandle = jasmine.createSpyObj(
'elementHandle',
['x', 'y','getGridSize']
);
mockElementHandle.x.and.returnValue(6);
mockElementHandle.y.and.returnValue(8);
mockElementHandle.getGridSize.and.returnValue(TEST_GRID_SIZE);
mockFixedControl = jasmine.createSpyObj(
'fixedControl',
['updateSelectionStyle', 'mutate']
);
mockFixedControl.updateSelectionStyle.and.returnValue();
mockFixedControl.mutate.and.returnValue();
mockConfigPath = jasmine.createSpy('configPath');
handle = new FixedDragHandle(
mockElementHandle,
mockConfigPath,
mockFixedControl
);
});
it("provides a style for positioning", function () {
var style = handle.style();
// 6 grid coords * 13 pixels - 3 pixels for centering
expect(style.left).toEqual('75px');
// 8 grid coords * 33 pixels - 3 pixels for centering
expect(style.top).toEqual('261px');
});
it("allows handles to be dragged", function () {
handle.startDrag();
handle.continueDrag([16, 8]);
// Should update x/y, snapped to grid
expect(mockElementHandle.x).toHaveBeenCalledWith(7);
expect(mockElementHandle.y).toHaveBeenCalledWith(8);
handle.continueDrag([-16, -35]);
// Should have interpreted relative to initial state
expect(mockElementHandle.x).toHaveBeenCalledWith(5);
expect(mockElementHandle.y).toHaveBeenCalledWith(7);
// Should have called updateSelectionStyle once per continueDrag
expect(mockFixedControl.updateSelectionStyle.calls.count()).toEqual(2);
// Finally, ending drag should mutate
handle.endDrag();
expect(mockFixedControl.mutate).toHaveBeenCalled();
});
});
}
);

View File

@ -0,0 +1,71 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
['../src/FixedProxy'],
function (FixedProxy) {
describe("Fixed Position view's selection proxy", function () {
var mockCallback,
mockQ,
mockDialogService,
mockPromise,
proxy;
beforeEach(function () {
mockCallback = jasmine.createSpy('callback');
mockQ = jasmine.createSpyObj('$q', ['when']);
mockDialogService = jasmine.createSpyObj('dialogService', ['getUserInput']);
mockPromise = jasmine.createSpyObj('promise', ['then']);
mockQ.when.and.returnValue(mockPromise);
proxy = new FixedProxy(mockCallback, mockQ, mockDialogService);
});
it("handles promised element creation", function () {
// The element factory may return promises (e.g. if
// user input is required) so make sure proxy is wrapping these
proxy.add("fixed.box");
expect(mockQ.when).toHaveBeenCalled();
});
it("notifies its callback when an element is created", function () {
proxy.add("fixed.box");
// Callback should not have been invoked yet
expect(mockCallback).not.toHaveBeenCalled();
// Resolve the promise
mockPromise.then.calls.mostRecent().args[0]({});
// Should have fired the callback
expect(mockCallback).toHaveBeenCalledWith({
type: "fixed.box",
x: 0,
y: 0,
width: 1,
height: 1,
useGrid: true
});
});
});
}
);

View File

@ -0,0 +1,81 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
["../src/LayoutDrag"],
function (LayoutDrag) {
describe("A Layout drag handler", function () {
var testPosition = {
position: [8, 11],
dimensions: [3, 2]
};
it("changes position by a supplied factor, rounding by grid size", function () {
var handler = new LayoutDrag(
testPosition,
[1, 1],
[0, 0],
[10, 20]
);
expect(handler.getAdjustedPosition([37, 84])).toEqual({
position: [12, 15],
dimensions: [3, 2]
});
expect(handler.getAdjustedPosition([-37, 84])).toEqual({
position: [4, 15],
dimensions: [3, 2]
});
});
it("changes dimensions by a supplied factor, rounding by grid size", function () {
var handler = new LayoutDrag(
testPosition,
[0, 0],
[1, 1],
[10, 20]
);
expect(handler.getAdjustedPosition([37, 84])).toEqual({
position: [8, 11],
dimensions: [7, 6]
});
});
it("allows mixing dimension and position factors", function () {
var handler = new LayoutDrag(
testPosition,
[0, 1],
[-1, 0],
[10, 20]
);
expect(handler.getAdjustedPosition([11, 84])).toEqual({
position: [8, 15],
dimensions: [2, 2]
});
});
});
}
);

View File

@ -0,0 +1,128 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2016, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define([
'../src/MCTTriggerModal'
], function (
MCTTriggerModal
) {
describe('MCTTriggerModal', function () {
var $scope,
$element,
frame,
layoutContainer,
$document,
mctTriggerModal;
function makeElement(classes, parentEl) {
var elem = jasmine.createSpyObj('element.' + classes.join('.'), [
'hasClass',
'parent'
]);
elem.hasClass.and.callFake(function (className) {
return classes.indexOf(className) !== -1;
});
elem.parent.and.returnValue(parentEl);
var div = document.createElement('div');
div.className = classes.join(' ');
parentEl[0].appendChild(div);
elem[0] = div;
return elem;
}
beforeEach(function () {
$scope = jasmine.createSpyObj('$scope', ['$on']);
$scope.domainObject = { getCapability: function () {
return { getActions: function () {
return [];
}};
}};
$element = jasmine.createSpyObj('$element', [
'parent',
'remove',
'on',
'off'
]);
layoutContainer = document.createElement('div');
frame = makeElement(['frame'], [layoutContainer]);
var child = makeElement([], frame);
for (var i = 0; i < 5; i++) {
child = makeElement([], child);
}
$element.parent.and.returnValue(child);
$document = [jasmine.createSpyObj('document', ['createElement'])];
$document[0].body = document.createElement('div');
$document[0].createElement.and.callFake(function (tag) {
return document.createElement(tag);
});
mctTriggerModal = new MCTTriggerModal($document);
});
it('is a directive definition', function () {
expect(mctTriggerModal.restrict).toBe('A');
expect(mctTriggerModal.link).toEqual(jasmine.any(Function));
});
describe('link', function () {
beforeEach(function () {
mctTriggerModal.link($scope, $element);
});
it('attaches handlers to $element', function () {
expect($element.on).toHaveBeenCalledWith(
'click',
jasmine.any(Function)
);
});
it('cleans up on $scope $destroy', function () {
expect($scope.$on).toHaveBeenCalledWith(
'$destroy',
jasmine.any(Function)
);
$scope.$on.calls.mostRecent().args[1]();
expect($element.off).toHaveBeenCalledWith(
'click',
jasmine.any(Function)
);
});
it('opens and closes overlays', function () {
[
'a.close', 'a.t-done', '.abs.blocker'
].forEach(function (selector) {
$element.on.calls.mostRecent().args[1]();
var container = $document[0].body.querySelector('.t-contents');
expect(container.children[0]).toBe(frame[0]);
expect(layoutContainer.children[0]).not.toBe(frame[0]);
$document[0].body.querySelector(selector)
.dispatchEvent(new Event('click'));
expect(container.children[0]).not.toBe(frame[0]);
expect(layoutContainer.children[0]).toBe(frame[0]);
});
});
});
});
});

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