Compare commits
48 Commits
vue-conduc
...
layout-imp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
50e994f982 | ||
|
|
e07cfc9394 | ||
|
|
32a0baa7a3 | ||
|
|
f06427cb3e | ||
|
|
9ae4e66c91 | ||
|
|
eeab6e9bde | ||
|
|
cabc410e0a | ||
|
|
2dcff00fa7 | ||
|
|
94cdce3551 | ||
|
|
a7948ce83e | ||
|
|
74faf1bd48 | ||
|
|
3e7527d55c | ||
|
|
9733674d6e | ||
|
|
e05dbadea2 | ||
|
|
bc512f3766 | ||
|
|
ae51e2e437 | ||
|
|
0e06a7b403 | ||
|
|
ff7df9ad1e | ||
|
|
1069a45cfc | ||
|
|
d13d59bfa0 | ||
|
|
55d3ab5e8a | ||
|
|
c073a21ba6 | ||
|
|
ed8137726d | ||
|
|
3ebdab5e51 | ||
|
|
35d1b894e2 | ||
|
|
7c54ec4f9f | ||
|
|
cbcfd44016 | ||
|
|
a296bc2b81 | ||
|
|
06b9e0fa97 | ||
|
|
4374a6fa28 | ||
|
|
67883519ee | ||
|
|
6f1b5b4ae3 | ||
|
|
c3b7e7869e | ||
|
|
d48cc2deee | ||
|
|
64b9d4c24a | ||
|
|
88bcb6078e | ||
|
|
5f9f3cd8e8 | ||
|
|
814b404614 | ||
|
|
ba2bb2180b | ||
|
|
72cdb352f0 | ||
|
|
cedf942c0c | ||
|
|
27506a3757 | ||
|
|
acc4e03c88 | ||
|
|
9a6090cd02 | ||
|
|
f40c9fa6f9 | ||
|
|
e7cdb334de | ||
|
|
afca6cd2e9 | ||
|
|
3c324cbea0 |
2
app.js
2
app.js
@@ -46,7 +46,7 @@ webpackConfig.plugins.push(new webpack.HotModuleReplacementPlugin());
|
||||
webpackConfig.plugins.push(function() { this.plugin('watch-run', function(watching, callback) { console.log('Begin compile at ' + new Date()); callback(); }) });
|
||||
|
||||
webpackConfig.entry.openmct = [
|
||||
'webpack-hot-middleware/client',
|
||||
'webpack-hot-middleware/client?reload=true',
|
||||
webpackConfig.entry.openmct
|
||||
];
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ define([
|
||||
{
|
||||
"key": "eventGenerator",
|
||||
"name": "Event Message Generator",
|
||||
"cssClass": "icon-folder-new",
|
||||
"cssClass": "icon-generator-events",
|
||||
"description": "For development use. Creates sample event message data that mimics a live data stream.",
|
||||
"priority": 10,
|
||||
"features": "creation",
|
||||
|
||||
@@ -38,7 +38,7 @@ define([
|
||||
openmct.types.addType("example.state-generator", {
|
||||
name: "State Generator",
|
||||
description: "For development use. Generates test enumerated telemetry by cycling through a given set of states",
|
||||
cssClass: "icon-telemetry",
|
||||
cssClass: "icon-generator-telemetry",
|
||||
creatable: true,
|
||||
form: [
|
||||
{
|
||||
@@ -66,7 +66,7 @@ define([
|
||||
openmct.types.addType("generator", {
|
||||
name: "Sine Wave Generator",
|
||||
description: "For development use. Generates example streaming telemetry data using a simple sine wave algorithm.",
|
||||
cssClass: "icon-telemetry",
|
||||
cssClass: "icon-generator-telemetry",
|
||||
creatable: true,
|
||||
form: [
|
||||
{
|
||||
|
||||
@@ -26,12 +26,16 @@ define([
|
||||
"./src/NotificationLaunchController",
|
||||
"./src/DialogLaunchIndicator",
|
||||
"./src/NotificationLaunchIndicator",
|
||||
"./res/dialog-launch.html",
|
||||
"./res/notification-launch.html",
|
||||
'legacyRegistry'
|
||||
], function (
|
||||
DialogLaunchController,
|
||||
NotificationLaunchController,
|
||||
DialogLaunchIndicator,
|
||||
NotificationLaunchIndicator,
|
||||
DialogLaunch,
|
||||
NotificationLaunch,
|
||||
legacyRegistry
|
||||
) {
|
||||
"use strict";
|
||||
@@ -41,11 +45,11 @@ define([
|
||||
"templates": [
|
||||
{
|
||||
"key": "dialogLaunchTemplate",
|
||||
"templateUrl": "dialog-launch.html"
|
||||
"template": DialogLaunch
|
||||
},
|
||||
{
|
||||
"key": "notificationLaunchTemplate",
|
||||
"templateUrl": "notification-launch.html"
|
||||
"template": NotificationLaunch
|
||||
}
|
||||
],
|
||||
"controllers": [
|
||||
|
||||
@@ -51,76 +51,26 @@ define(
|
||||
return actionTexts[Math.floor(Math.random()*3)];
|
||||
}
|
||||
|
||||
function getExampleActions() {
|
||||
var actions = [
|
||||
{
|
||||
label: "Try Again",
|
||||
callback: function () {
|
||||
$log.debug("Try Again pressed");
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "Remove",
|
||||
callback: function () {
|
||||
$log.debug("Remove pressed");
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "Cancel",
|
||||
callback: function () {
|
||||
$log.debug("Cancel pressed");
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
// Randomly remove some actions off the top; leave at least one
|
||||
actions.splice(0,Math.floor(Math.random() * actions.length));
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
function getExampleSeverity() {
|
||||
var severities = [
|
||||
"info",
|
||||
"alert",
|
||||
"error"
|
||||
];
|
||||
return severities[Math.floor(Math.random() * severities.length)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch a new notification with a severity level of 'Error'.
|
||||
*/
|
||||
$scope.newError = function(){
|
||||
|
||||
$scope.newError = function () {
|
||||
notificationService.notify({
|
||||
title: "Example error notification " + messageCounter++,
|
||||
hint: "An error has occurred",
|
||||
severity: "error",
|
||||
primaryOption: {
|
||||
label: 'Retry',
|
||||
callback: function() {
|
||||
$log.info('Retry clicked');
|
||||
}
|
||||
},
|
||||
options: getExampleActions()});
|
||||
severity: "error"
|
||||
});
|
||||
};
|
||||
/**
|
||||
* Launch a new notification with a severity of 'Alert'.
|
||||
*/
|
||||
$scope.newAlert = function(){
|
||||
|
||||
$scope.newAlert = function () {
|
||||
notificationService.notify({
|
||||
title: "Alert notification " + (messageCounter++),
|
||||
hint: "This is an alert message",
|
||||
severity: "alert",
|
||||
primaryOption: {
|
||||
label: 'Retry',
|
||||
callback: function() {
|
||||
$log.info('Retry clicked');
|
||||
}
|
||||
},
|
||||
options: getExampleActions()});
|
||||
autoDismiss: true
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -128,39 +78,42 @@ define(
|
||||
* Launch a new notification with a progress bar that is updated
|
||||
* periodically, tracking an ongoing process.
|
||||
*/
|
||||
$scope.newProgress = function(){
|
||||
|
||||
$scope.newProgress = function () {
|
||||
let progress = 0;
|
||||
var notificationModel = {
|
||||
title: "Progress notification example",
|
||||
severity: "info",
|
||||
progress: 0,
|
||||
actionText: getExampleActionText(),
|
||||
unknownProgress: false
|
||||
progress: progress,
|
||||
actionText: getExampleActionText()
|
||||
};
|
||||
let notification;
|
||||
|
||||
/**
|
||||
* Simulate an ongoing process and update the progress bar.
|
||||
* @param notification
|
||||
*/
|
||||
function incrementProgress(notificationModel) {
|
||||
notificationModel.progress = Math.min(100, Math.floor(notificationModel.progress + Math.random() * 30));
|
||||
notificationModel.progressText = ["Estimated time" +
|
||||
function incrementProgress() {
|
||||
progress = Math.min(100, Math.floor(progress + Math.random() * 30))
|
||||
let progressText = ["Estimated time" +
|
||||
" remaining:" +
|
||||
" about ", 60 - Math.floor((notificationModel.progress / 100) * 60), " seconds"].join(" ");
|
||||
if (notificationModel.progress < 100) {
|
||||
$timeout(function(){incrementProgress(notificationModel);}, 1000);
|
||||
" about ", 60 - Math.floor((progress / 100) * 60), " seconds"].join(" ");
|
||||
notification.progress(progress, progressText);
|
||||
|
||||
if (progress < 100) {
|
||||
$timeout(function () {
|
||||
incrementProgress(notificationModel);
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
notificationService.notify(notificationModel);
|
||||
incrementProgress(notificationModel);
|
||||
notification = notificationService.notify(notificationModel);
|
||||
incrementProgress();
|
||||
};
|
||||
|
||||
/**
|
||||
* Launch a new notification with severity level of INFO.
|
||||
*/
|
||||
$scope.newInfo = function(){
|
||||
|
||||
$scope.newInfo = function () {
|
||||
notificationService.info({
|
||||
title: "Example Info notification " + messageCounter++
|
||||
});
|
||||
|
||||
201
index.html
201
index.html
@@ -49,6 +49,7 @@
|
||||
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"
|
||||
}));
|
||||
@@ -76,8 +77,208 @@
|
||||
openmct.install(openmct.plugins.SummaryWidget());
|
||||
openmct.install(openmct.plugins.Notebook());
|
||||
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>
|
||||
|
||||
@@ -58,7 +58,7 @@ define([], function () {
|
||||
|
||||
function checkNavigation() {
|
||||
var navigatedObject = navigationService.getNavigation();
|
||||
if (navigatedObject.hasCapability('context')) {
|
||||
if (navigatedObject && navigatedObject.hasCapability('context')) {
|
||||
if (!navigatedObject.getCapability('editor').isEditContextRoot()) {
|
||||
preventOrphanNavigation(navigatedObject);
|
||||
}
|
||||
|
||||
@@ -2,31 +2,14 @@
|
||||
ng-class="'message-severity-' + ngModel.severity">
|
||||
<div class="w-message-contents">
|
||||
<div class="top-bar">
|
||||
<div class="title">{{ngModel.title}}</div>
|
||||
</div>
|
||||
<div class="hint" ng-hide="ngModel.hint === undefined">
|
||||
{{ngModel.hint}}
|
||||
<span ng-if="ngModel.timestamp !== undefined">[{{ngModel.timestamp}}]</span>
|
||||
<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="bottom-bar">
|
||||
<a ng-repeat="dialogOption in ngModel.options"
|
||||
class="s-button"
|
||||
ng-click="dialogOption.callback()">
|
||||
{{dialogOption.label}}
|
||||
</a>
|
||||
<a class="s-button major"
|
||||
ng-if="ngModel.primaryOption"
|
||||
ng-click="ngModel.primaryOption.callback()">
|
||||
{{ngModel.primaryOption.label}}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
define([
|
||||
"./src/controllers/EditActionController",
|
||||
"./src/controllers/EditPanesController",
|
||||
"./src/controllers/ElementsController",
|
||||
"./src/controllers/EditObjectController",
|
||||
"./src/actions/EditAndComposeAction",
|
||||
"./src/actions/EditAction",
|
||||
@@ -47,7 +46,6 @@ define([
|
||||
"./src/creation/LocatorController",
|
||||
"./src/creation/CreationPolicy",
|
||||
"./src/creation/CreateActionProvider",
|
||||
"./src/creation/AddActionProvider",
|
||||
"./src/creation/CreationService",
|
||||
"./res/templates/create/locator.html",
|
||||
"./res/templates/create/create-button.html",
|
||||
@@ -55,13 +53,11 @@ define([
|
||||
"./res/templates/library.html",
|
||||
"./res/templates/edit-object.html",
|
||||
"./res/templates/edit-action-buttons.html",
|
||||
"./res/templates/elements.html",
|
||||
"./res/templates/topbar-edit.html",
|
||||
'legacyRegistry'
|
||||
], function (
|
||||
EditActionController,
|
||||
EditPanesController,
|
||||
ElementsController,
|
||||
EditObjectController,
|
||||
EditAndComposeAction,
|
||||
EditAction,
|
||||
@@ -85,7 +81,6 @@ define([
|
||||
LocatorController,
|
||||
CreationPolicy,
|
||||
CreateActionProvider,
|
||||
AddActionProvider,
|
||||
CreationService,
|
||||
locatorTemplate,
|
||||
createButtonTemplate,
|
||||
@@ -93,7 +88,6 @@ define([
|
||||
libraryTemplate,
|
||||
editObjectTemplate,
|
||||
editActionButtonsTemplate,
|
||||
elementsTemplate,
|
||||
topbarEditTemplate,
|
||||
legacyRegistry
|
||||
) {
|
||||
@@ -115,14 +109,6 @@ define([
|
||||
"$scope"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "ElementsController",
|
||||
"implementation": ElementsController,
|
||||
"depends": [
|
||||
"$scope",
|
||||
"openmct"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "EditObjectController",
|
||||
"implementation": EditObjectController,
|
||||
@@ -225,10 +211,10 @@ define([
|
||||
"description": "Save changes made to these objects.",
|
||||
"depends": [
|
||||
"$injector",
|
||||
"policyService",
|
||||
"dialogService",
|
||||
"copyService",
|
||||
"notificationService"
|
||||
"notificationService",
|
||||
"openmct"
|
||||
],
|
||||
"priority": "mandatory"
|
||||
},
|
||||
@@ -296,13 +282,6 @@ define([
|
||||
"action"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "edit-elements",
|
||||
"template": elementsTemplate,
|
||||
"gestures": [
|
||||
"drop"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "topbar-edit",
|
||||
"template": topbarEditTemplate
|
||||
@@ -319,12 +298,6 @@ define([
|
||||
]
|
||||
}
|
||||
],
|
||||
"templates": [
|
||||
{
|
||||
key: "elementsPool",
|
||||
template: elementsTemplate
|
||||
}
|
||||
],
|
||||
"components": [
|
||||
{
|
||||
"type": "decorator",
|
||||
@@ -356,18 +329,6 @@ define([
|
||||
"policyService"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "AddActionProvider",
|
||||
"provides": "actionService",
|
||||
"type": "provider",
|
||||
"implementation": AddActionProvider,
|
||||
"depends": [
|
||||
"$q",
|
||||
"typeService",
|
||||
"dialogService",
|
||||
"policyService"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "CreationService",
|
||||
"provides": "creationService",
|
||||
@@ -405,7 +366,8 @@ define([
|
||||
"description": "Provides transactional editing capabilities",
|
||||
"implementation": EditorCapability,
|
||||
"depends": [
|
||||
"transactionService"
|
||||
"transactionService",
|
||||
"openmct"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
<!--
|
||||
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-controller="ElementsController" class="flex-elem l-flex-col holder grows">
|
||||
<mct-include key="'input-filter'"
|
||||
class="flex-elem holder"
|
||||
ng-model="filterBy">
|
||||
</mct-include>
|
||||
<div class="flex-elem grows vscroll scroll-pad">
|
||||
<ul class="tree" id="inspector-elements-tree"
|
||||
ng-if="composition.length > 0">
|
||||
<li ng-repeat="containedObject in composition | filter:searchElements">
|
||||
<span class="tree-item">
|
||||
<span class="grippy-sm"
|
||||
ng-if="composition.length > 1"
|
||||
data-id="{{ containedObject.id }}"
|
||||
mct-drag-down="dragDown($event)"
|
||||
mct-drag="drag($event)"
|
||||
mct-drag-up="dragUp($event)">
|
||||
</span>
|
||||
<mct-representation
|
||||
class="rep-object-label"
|
||||
key="'label'"
|
||||
mct-object="containedObject">
|
||||
</mct-representation>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div ng-if="composition.length === 0">No contained elements</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -69,8 +69,8 @@ define([], function () {
|
||||
}
|
||||
]
|
||||
};
|
||||
setTimeout(() => this.removeCallback(domainObject));
|
||||
|
||||
dialog = this.dialogService.showBlockingMessage(model);
|
||||
};
|
||||
|
||||
return RemoveDialog;
|
||||
|
||||
@@ -40,20 +40,20 @@ function (
|
||||
*/
|
||||
function SaveAsAction(
|
||||
$injector,
|
||||
policyService,
|
||||
dialogService,
|
||||
copyService,
|
||||
notificationService,
|
||||
openmct,
|
||||
context
|
||||
) {
|
||||
this.domainObject = (context || {}).domainObject;
|
||||
this.injectObjectService = function () {
|
||||
this.objectService = $injector.get("objectService");
|
||||
};
|
||||
this.policyService = policyService;
|
||||
this.dialogService = dialogService;
|
||||
this.copyService = copyService;
|
||||
this.notificationService = notificationService;
|
||||
this.openmct = openmct;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,7 +63,7 @@ function (
|
||||
return new CreateWizard(
|
||||
this.domainObject,
|
||||
parent,
|
||||
this.policyService
|
||||
this.openmct
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -36,9 +36,11 @@ define(
|
||||
*/
|
||||
function EditorCapability(
|
||||
transactionService,
|
||||
openmct,
|
||||
domainObject
|
||||
) {
|
||||
this.transactionService = transactionService;
|
||||
this.openmct = openmct;
|
||||
this.domainObject = domainObject;
|
||||
}
|
||||
|
||||
@@ -48,27 +50,22 @@ define(
|
||||
* or finish() are called.
|
||||
*/
|
||||
EditorCapability.prototype.edit = function () {
|
||||
this.transactionService.startTransaction();
|
||||
this.domainObject.getCapability('status').set('editing', true);
|
||||
console.warn('DEPRECATED: cannot edit via edit capability, use openmct.editor instead.');
|
||||
|
||||
if (!this.openmct.editor.isEditing()) {
|
||||
this.openmct.editor.edit();
|
||||
this.domainObject.getCapability('status').set('editing', true);
|
||||
}
|
||||
};
|
||||
|
||||
function isEditContextRoot(domainObject) {
|
||||
return domainObject.getCapability('status').get('editing');
|
||||
}
|
||||
|
||||
function isEditing(domainObject) {
|
||||
return isEditContextRoot(domainObject) ||
|
||||
domainObject.hasCapability('context') &&
|
||||
isEditing(domainObject.getCapability('context').getParent());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether this object, or any of its ancestors are
|
||||
* currently being edited.
|
||||
* @returns boolean
|
||||
*/
|
||||
EditorCapability.prototype.inEditContext = function () {
|
||||
return isEditing(this.domainObject);
|
||||
console.warn('DEPRECATION WARNING: isEditing checks must be done via openmct.editor.');
|
||||
return this.openmct.editor.isEditing();
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -77,7 +74,8 @@ define(
|
||||
* @returns {*}
|
||||
*/
|
||||
EditorCapability.prototype.isEditContextRoot = function () {
|
||||
return isEditContextRoot(this.domainObject);
|
||||
console.warn('DEPRECATION WARNING: isEditing checks must be done via openmct.editor.');
|
||||
return this.openmct.editor.isEditing();
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -86,10 +84,8 @@ define(
|
||||
* @returns {*}
|
||||
*/
|
||||
EditorCapability.prototype.save = function () {
|
||||
var transactionService = this.transactionService;
|
||||
return transactionService.commit().then(function () {
|
||||
transactionService.startTransaction();
|
||||
});
|
||||
console.warn('DEPRECATED: cannot save via edit capability, use openmct.editor instead.');
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
EditorCapability.prototype.invoke = EditorCapability.prototype.edit;
|
||||
@@ -100,16 +96,8 @@ define(
|
||||
* @returns {*}
|
||||
*/
|
||||
EditorCapability.prototype.finish = function () {
|
||||
var domainObject = this.domainObject;
|
||||
|
||||
if (this.transactionService.isActive()) {
|
||||
return this.transactionService.cancel().then(function () {
|
||||
domainObject.getCapability("status").set("editing", false);
|
||||
return domainObject;
|
||||
});
|
||||
} else {
|
||||
return Promise.resolve(domainObject);
|
||||
}
|
||||
console.warn('DEPRECATED: cannot finish via edit capability, use openmct.editor instead.');
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,197 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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(
|
||||
['zepto'],
|
||||
function ($) {
|
||||
|
||||
/**
|
||||
* The ElementsController prepares the elements view for display
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
function ElementsController($scope, openmct) {
|
||||
this.scope = $scope;
|
||||
this.scope.composition = [];
|
||||
this.openmct = openmct;
|
||||
this.dragDown = this.dragDown.bind(this);
|
||||
this.dragUp = this.dragUp.bind(this);
|
||||
|
||||
var self = this;
|
||||
|
||||
function filterBy(text) {
|
||||
if (typeof text === 'undefined') {
|
||||
return $scope.searchText;
|
||||
} else {
|
||||
$scope.searchText = text;
|
||||
}
|
||||
}
|
||||
|
||||
function searchElements(value) {
|
||||
if ($scope.searchText) {
|
||||
return value.getModel().name.toLowerCase().search(
|
||||
$scope.searchText.toLowerCase()) !== -1;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function setSelection(selection) {
|
||||
if (!selection[0]) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.mutationListener) {
|
||||
self.mutationListener();
|
||||
delete self.mutationListener;
|
||||
}
|
||||
|
||||
var domainObject = selection[0].context.oldItem;
|
||||
self.refreshComposition(domainObject);
|
||||
|
||||
if (domainObject) {
|
||||
|
||||
self.mutationListener = domainObject.getCapability('mutation')
|
||||
.listen(self.refreshComposition.bind(self, domainObject));
|
||||
}
|
||||
}
|
||||
|
||||
$scope.filterBy = filterBy;
|
||||
$scope.searchElements = searchElements;
|
||||
|
||||
openmct.selection.on('change', setSelection);
|
||||
setSelection(openmct.selection.get());
|
||||
|
||||
$scope.dragDown = this.dragDown;
|
||||
$scope.drag = this.drag;
|
||||
$scope.dragUp = this.dragUp;
|
||||
|
||||
$scope.$on("$destroy", function () {
|
||||
openmct.selection.off("change", setSelection);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked on DragStart - Adds reordering class to parent UL element
|
||||
* Sets selected object ID, to be used on Drag End
|
||||
*
|
||||
* @param {object} event | Mouse Event
|
||||
*/
|
||||
ElementsController.prototype.dragDown = function (event) {
|
||||
if (!this.parentUL) {
|
||||
this.parentUL = $(document).find('#inspector-elements-tree');
|
||||
}
|
||||
|
||||
this.selectedTreeItem = $(event.target).parent();
|
||||
this.selectedObjectId = event.target.getAttribute('data-id');
|
||||
|
||||
this.parentUL.addClass('reordering');
|
||||
this.selectedTreeItem.addClass('reorder-actor');
|
||||
};
|
||||
|
||||
/**
|
||||
* Invoked on dragEnd - Removes selected object from position in composition
|
||||
* and replaces it at the target position. Composition is then updated with current
|
||||
* scope
|
||||
*
|
||||
* @param {object} event - Mouse Event
|
||||
*/
|
||||
ElementsController.prototype.dragUp = function (event) {
|
||||
this.targetObjectId = event.target.getAttribute('data-id');
|
||||
|
||||
if (this.targetObjectId && this.selectedObjectId) {
|
||||
var selectedObjectPosition,
|
||||
targetObjectPosition;
|
||||
|
||||
selectedObjectPosition = findObjectInCompositionFromId(this.selectedObjectId, this.scope.composition);
|
||||
targetObjectPosition = findObjectInCompositionFromId(this.targetObjectId, this.scope.composition);
|
||||
|
||||
if ((selectedObjectPosition !== -1) && (targetObjectPosition !== -1)) {
|
||||
var selectedObject = this.scope.composition.splice(selectedObjectPosition, 1),
|
||||
selection = this.openmct.selection.get(),
|
||||
domainObject = selection ? selection[0].context.oldItem : undefined;
|
||||
|
||||
this.scope.composition.splice(targetObjectPosition, 0, selectedObject[0]);
|
||||
|
||||
if (domainObject) {
|
||||
domainObject.getCapability('mutation').mutate(function (model) {
|
||||
model.composition = this.scope.composition.map(function (dObject) {
|
||||
return dObject.id;
|
||||
});
|
||||
}.bind(this));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.parentUL) {
|
||||
this.parentUL.removeClass('reordering');
|
||||
}
|
||||
|
||||
if (this.selectedTreeItem) {
|
||||
this.selectedTreeItem.removeClass('reorder-actor');
|
||||
}
|
||||
};
|
||||
|
||||
ElementsController.prototype.drag = function (event) {
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the composition for the selected object and populates the scope with it.
|
||||
*
|
||||
* @param domainObject the selected object
|
||||
* @private
|
||||
*/
|
||||
ElementsController.prototype.refreshComposition = function (domainObject) {
|
||||
var refreshTracker = {};
|
||||
this.currentRefresh = refreshTracker;
|
||||
|
||||
var selectedObjectComposition = domainObject && domainObject.useCapability('composition');
|
||||
if (selectedObjectComposition) {
|
||||
selectedObjectComposition.then(function (composition) {
|
||||
if (this.currentRefresh === refreshTracker) {
|
||||
this.scope.composition = composition;
|
||||
}
|
||||
}.bind(this));
|
||||
} else {
|
||||
this.scope.composition = [];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Finds position of object with given ID in Composition
|
||||
*
|
||||
* @param {String} id
|
||||
* @param {Array} composition
|
||||
* @private
|
||||
*/
|
||||
function findObjectInCompositionFromId(id, composition) {
|
||||
var mapped = composition.map(function (element) {
|
||||
return element.id;
|
||||
});
|
||||
|
||||
return mapped.indexOf(id);
|
||||
}
|
||||
|
||||
return ElementsController;
|
||||
}
|
||||
);
|
||||
@@ -1,133 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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 AddAction. Created by ahenry on 01/21/16.
|
||||
*/
|
||||
define(
|
||||
[
|
||||
'./CreateWizard'
|
||||
],
|
||||
function (CreateWizard) {
|
||||
|
||||
/**
|
||||
* The Add Action is performed to create new instances of
|
||||
* domain objects of a specific type that are subobjects of an
|
||||
* object being edited. This is the action that is performed when a
|
||||
* user uses the Add menu option.
|
||||
*
|
||||
* @memberof platform/commonUI/browse
|
||||
* @implements {Action}
|
||||
* @constructor
|
||||
*
|
||||
* @param {Type} type the type of domain object to create
|
||||
* @param {DomainObject} parent the domain object that should
|
||||
* act as a container for the newly-created object
|
||||
* (note that the user will have an opportunity to
|
||||
* override this)
|
||||
* @param {ActionContext} context the context in which the
|
||||
* action is being performed
|
||||
* @param {DialogService} dialogService
|
||||
*/
|
||||
function AddAction(type, parent, context, $q, dialogService, policyService) {
|
||||
this.metadata = {
|
||||
key: 'add',
|
||||
cssClass: type.getCssClass(),
|
||||
name: type.getName(),
|
||||
type: type.getKey(),
|
||||
description: type.getDescription(),
|
||||
context: context
|
||||
};
|
||||
|
||||
this.type = type;
|
||||
this.parent = parent;
|
||||
this.$q = $q;
|
||||
this.dialogService = dialogService;
|
||||
this.policyService = policyService;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Create a new object of the given type.
|
||||
* This will prompt for user input first.
|
||||
*
|
||||
* @returns {Promise} that will be resolved with the object that the
|
||||
* action was originally invoked on (ie. the 'parent')
|
||||
*/
|
||||
AddAction.prototype.perform = function () {
|
||||
var newModel = this.type.getInitialModel(),
|
||||
newObject,
|
||||
parentObject = this.parent,
|
||||
wizard;
|
||||
|
||||
newModel.type = this.type.getKey();
|
||||
newObject = parentObject.getCapability('instantiation').instantiate(newModel);
|
||||
newObject.useCapability('mutation', function (model) {
|
||||
model.location = parentObject.getId();
|
||||
});
|
||||
|
||||
wizard = new CreateWizard(newObject, this.parent, this.policyService);
|
||||
|
||||
function populateObjectFromInput(formValue) {
|
||||
return wizard.populateObjectFromInput(formValue, newObject);
|
||||
}
|
||||
|
||||
function persistAndReturn(domainObject) {
|
||||
return domainObject.getCapability('persistence')
|
||||
.persist()
|
||||
.then(function () {
|
||||
return domainObject;
|
||||
});
|
||||
}
|
||||
|
||||
function addToParent(populatedObject) {
|
||||
parentObject.getCapability('composition').add(populatedObject);
|
||||
return persistAndReturn(parentObject);
|
||||
}
|
||||
|
||||
return this.dialogService
|
||||
.getUserInput(wizard.getFormStructure(false), wizard.getInitialFormValue())
|
||||
.then(populateObjectFromInput)
|
||||
.then(persistAndReturn)
|
||||
.then(addToParent);
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Metadata associated with a Add action.
|
||||
* @typedef {ActionMetadata} AddActionMetadata
|
||||
* @property {string} type the key for the type of domain object
|
||||
* to be created
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get metadata about this action.
|
||||
* @returns {AddActionMetadata} metadata about this action
|
||||
*/
|
||||
AddAction.prototype.getMetadata = function () {
|
||||
return this.metadata;
|
||||
};
|
||||
|
||||
return AddAction;
|
||||
}
|
||||
);
|
||||
@@ -1,82 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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 AddActionProvider.js. Created by ahenry on 01/21/16.
|
||||
*/
|
||||
define(
|
||||
["./AddAction"],
|
||||
function (AddAction) {
|
||||
|
||||
/**
|
||||
* The AddActionProvider is an ActionProvider which introduces
|
||||
* an Add action for creating sub objects.
|
||||
*
|
||||
* @memberof platform/commonUI/browse
|
||||
* @constructor
|
||||
* @implements {ActionService}
|
||||
*
|
||||
* @param {TypeService} typeService the type service, used to discover
|
||||
* available types
|
||||
* @param {DialogService} dialogService the dialog service, used by
|
||||
* specific Create actions to get user input to populate the
|
||||
* model of the newly-created domain object.
|
||||
* @param {CreationService} creationService the creation service (also
|
||||
* introduced in this bundle), responsible for handling actual
|
||||
* object creation.
|
||||
*/
|
||||
function AddActionProvider($q, typeService, dialogService, policyService) {
|
||||
this.typeService = typeService;
|
||||
this.dialogService = dialogService;
|
||||
this.$q = $q;
|
||||
this.policyService = policyService;
|
||||
}
|
||||
|
||||
AddActionProvider.prototype.getActions = function (actionContext) {
|
||||
var context = actionContext || {},
|
||||
key = context.key,
|
||||
destination = context.domainObject;
|
||||
|
||||
// We only provide Add actions, and we need a
|
||||
// domain object to serve as the container for the
|
||||
// newly-created object (although the user may later
|
||||
// make a different selection)
|
||||
if (key !== 'add' || !destination) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Introduce one create action per type
|
||||
return ['timeline', 'activity'].map(function (type) {
|
||||
return new AddAction(
|
||||
this.typeService.getType(type),
|
||||
destination,
|
||||
context,
|
||||
this.$q,
|
||||
this.dialogService,
|
||||
this.policyService
|
||||
);
|
||||
}, this);
|
||||
};
|
||||
|
||||
return AddActionProvider;
|
||||
}
|
||||
);
|
||||
@@ -44,7 +44,7 @@ define(
|
||||
* @param {ActionContext} context the context in which the
|
||||
* action is being performed
|
||||
*/
|
||||
function CreateAction(type, parent, context) {
|
||||
function CreateAction(type, parent, context, openmct) {
|
||||
this.metadata = {
|
||||
key: 'create',
|
||||
cssClass: type.getCssClass(),
|
||||
@@ -55,6 +55,7 @@ define(
|
||||
};
|
||||
this.type = type;
|
||||
this.parent = parent;
|
||||
this.openmct = openmct;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,37 +64,28 @@ define(
|
||||
*/
|
||||
CreateAction.prototype.perform = function () {
|
||||
var newModel = this.type.getInitialModel(),
|
||||
openmct = this.openmct,
|
||||
newObject,
|
||||
editAction,
|
||||
editorCapability;
|
||||
|
||||
function closeEditor() {
|
||||
return editorCapability.finish();
|
||||
}
|
||||
editAction;
|
||||
|
||||
function onSave() {
|
||||
return editorCapability.save()
|
||||
.then(closeEditor);
|
||||
openmct.editor.save();
|
||||
}
|
||||
|
||||
function onCancel() {
|
||||
return closeEditor();
|
||||
openmct.editor.cancel();
|
||||
}
|
||||
|
||||
newModel.type = this.type.getKey();
|
||||
newModel.location = this.parent.getId();
|
||||
newObject = this.parent.useCapability('instantiation', newModel);
|
||||
editorCapability = newObject.hasCapability('editor') && newObject.getCapability("editor");
|
||||
|
||||
openmct.editor.edit();
|
||||
editAction = newObject.getCapability("action").getActions("edit")[0];
|
||||
//If an edit action is available, perform it
|
||||
if (editAction) {
|
||||
return editAction.perform();
|
||||
} else if (editorCapability) {
|
||||
//otherwise, use the save as action
|
||||
editorCapability.edit();
|
||||
return newObject.getCapability("action").perform("save-as").then(onSave, onCancel);
|
||||
}
|
||||
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.
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -34,13 +34,13 @@ define(
|
||||
* @memberof platform/commonUI/browse
|
||||
* @constructor
|
||||
*/
|
||||
function CreateWizard(domainObject, parent, policyService) {
|
||||
function CreateWizard(domainObject, parent, openmct) {
|
||||
this.type = domainObject.getCapability('type');
|
||||
this.model = domainObject.getModel();
|
||||
this.domainObject = domainObject;
|
||||
this.properties = this.type.getProperties();
|
||||
this.parent = parent;
|
||||
this.policyService = policyService;
|
||||
this.openmct = openmct;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -56,15 +56,10 @@ define(
|
||||
*/
|
||||
CreateWizard.prototype.getFormStructure = function (includeLocation) {
|
||||
var sections = [],
|
||||
domainObject = this.domainObject,
|
||||
policyService = this.policyService;
|
||||
domainObject = this.domainObject;
|
||||
|
||||
function validateLocation(parent) {
|
||||
return parent && policyService.allow(
|
||||
"composition",
|
||||
parent,
|
||||
domainObject
|
||||
);
|
||||
return parent && this.openmct.composition.checkPolicy(parent.useCapability('adapter'), domainObject.useCapability('adapter'));
|
||||
}
|
||||
|
||||
sections.push({
|
||||
@@ -93,7 +88,7 @@ define(
|
||||
rows: [{
|
||||
name: "Save In",
|
||||
control: "locator",
|
||||
validate: validateLocation,
|
||||
validate: validateLocation.bind(this),
|
||||
key: "createParent"
|
||||
}]
|
||||
});
|
||||
|
||||
@@ -55,16 +55,19 @@ define(
|
||||
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;
|
||||
}
|
||||
// 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;
|
||||
|
||||
@@ -1,254 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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/api/objects/object-utils',
|
||||
'lodash'
|
||||
],
|
||||
function (
|
||||
objectUtils,
|
||||
_
|
||||
) {
|
||||
|
||||
/**
|
||||
* Provides initial structure and state (as suitable for provision
|
||||
* to the `mct-toolbar` directive) for a view's toolbar, based on
|
||||
* that view's declaration of what belongs in its toolbar and on
|
||||
* the current selection.
|
||||
*
|
||||
* @param $scope the Angular scope
|
||||
* @param {Object} openmct the openmct object
|
||||
* @param structure the toolbar structure
|
||||
* @memberof platform/commonUI/edit
|
||||
* @constructor
|
||||
*/
|
||||
function EditToolbar($scope, openmct, structure) {
|
||||
this.toolbarStructure = [];
|
||||
this.properties = [];
|
||||
this.toolbarState = [];
|
||||
this.openmct = openmct;
|
||||
this.domainObjectsById = {};
|
||||
this.unobserveObjects = [];
|
||||
this.stateTracker = [];
|
||||
|
||||
$scope.$watchCollection(this.getState.bind(this), this.handleStateChanges.bind(this));
|
||||
$scope.$on("$destroy", this.destroy.bind(this));
|
||||
|
||||
this.updateToolbar(structure);
|
||||
this.registerListeners(structure);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the toolbar with a new structure.
|
||||
*
|
||||
* @param {Array} structure the toolbar structure
|
||||
*/
|
||||
EditToolbar.prototype.updateToolbar = function (structure) {
|
||||
var self = this;
|
||||
|
||||
function addKey(item) {
|
||||
self.stateTracker.push({
|
||||
id: objectUtils.makeKeyString(item.domainObject.identifier),
|
||||
domainObject: item.domainObject,
|
||||
property: item.property
|
||||
});
|
||||
self.properties.push(item.property);
|
||||
|
||||
return self.properties.length - 1; // Return index of property
|
||||
}
|
||||
|
||||
function convertItem(item) {
|
||||
var converted = Object.create(item || {});
|
||||
|
||||
if (item.property) {
|
||||
converted.key = addKey(item);
|
||||
}
|
||||
|
||||
if (item.method) {
|
||||
converted.click = function (value) {
|
||||
item.method(value);
|
||||
};
|
||||
}
|
||||
|
||||
return converted;
|
||||
}
|
||||
|
||||
// Get initial value for a given property
|
||||
function initializeState(property) {
|
||||
var result;
|
||||
structure.forEach(function (item) {
|
||||
if (item.property === property) {
|
||||
result = _.get(item.domainObject, item.property);
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Tracks the domain object and property for every element in the state array
|
||||
this.stateTracker = [];
|
||||
this.toolbarStructure = structure.map(convertItem);
|
||||
this.toolbarState = this.properties.map(initializeState);
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the structure of the toolbar, as appropriate to
|
||||
* pass to `mct-toolbar`.
|
||||
*
|
||||
* @returns {Array} the toolbar structure
|
||||
*/
|
||||
EditToolbar.prototype.getStructure = function () {
|
||||
return this.toolbarStructure;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the current state of the toolbar, as appropriate
|
||||
* to two-way bind to the state handled by `mct-toolbar`.
|
||||
*
|
||||
* @returns {Array} state of the toolbar
|
||||
*/
|
||||
EditToolbar.prototype.getState = function () {
|
||||
return this.toolbarState;
|
||||
};
|
||||
|
||||
/**
|
||||
* Mutates the domain object's property with a new value.
|
||||
*
|
||||
* @param {Object} dominObject the domain object
|
||||
* @param {string} property the domain object's property to update
|
||||
* @param value the property's new value
|
||||
*/
|
||||
EditToolbar.prototype.updateDomainObject = function (domainObject, property, value) {
|
||||
this.openmct.objects.mutate(domainObject, property, value);
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates state with the new value.
|
||||
*
|
||||
* @param {number} index the index of the corresponding
|
||||
* element in the state array
|
||||
* @param value the new value to update the state array with
|
||||
*/
|
||||
EditToolbar.prototype.updateState = function (index, value) {
|
||||
this.toolbarState[index] = value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Register listeners for domain objects to watch for updates.
|
||||
*
|
||||
* @param {Array} the toolbar structure
|
||||
*/
|
||||
EditToolbar.prototype.registerListeners = function (structure) {
|
||||
var self = this;
|
||||
|
||||
function observeObject(domainObject, id) {
|
||||
var unobserveObject = self.openmct.objects.observe(domainObject, '*', function (newObject) {
|
||||
self.domainObjectsById[id].newObject = JSON.parse(JSON.stringify(newObject));
|
||||
self.scheduleStateUpdate();
|
||||
});
|
||||
self.unobserveObjects.push(unobserveObject);
|
||||
}
|
||||
|
||||
structure.forEach(function (item) {
|
||||
var domainObject = item.domainObject;
|
||||
var id = objectUtils.makeKeyString(domainObject.identifier);
|
||||
|
||||
if (!self.domainObjectsById[id]) {
|
||||
self.domainObjectsById[id] = {
|
||||
domainObject: domainObject,
|
||||
properties: []
|
||||
};
|
||||
observeObject(domainObject, id);
|
||||
}
|
||||
|
||||
self.domainObjectsById[id].properties.push(item.property);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Delays updating the state.
|
||||
*/
|
||||
EditToolbar.prototype.scheduleStateUpdate = function () {
|
||||
if (this.stateUpdateScheduled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.stateUpdateScheduled = true;
|
||||
setTimeout(this.updateStateAfterMutation.bind(this));
|
||||
};
|
||||
|
||||
EditToolbar.prototype.updateStateAfterMutation = function () {
|
||||
this.stateTracker.forEach(function (state, index) {
|
||||
if (!this.domainObjectsById[state.id].newObject) {
|
||||
return;
|
||||
}
|
||||
|
||||
var domainObject = this.domainObjectsById[state.id].domainObject;
|
||||
var newObject = this.domainObjectsById[state.id].newObject;
|
||||
var currentValue = _.get(domainObject, state.property);
|
||||
var newValue = _.get(newObject, state.property);
|
||||
|
||||
state.domainObject = newObject;
|
||||
|
||||
if (currentValue !== newValue) {
|
||||
this.updateState(index, newValue);
|
||||
}
|
||||
}, this);
|
||||
|
||||
Object.values(this.domainObjectsById).forEach(function (tracker) {
|
||||
if (tracker.newObject) {
|
||||
tracker.domainObject = tracker.newObject;
|
||||
}
|
||||
delete tracker.newObject;
|
||||
});
|
||||
this.stateUpdateScheduled = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes the listeners.
|
||||
*/
|
||||
EditToolbar.prototype.deregisterListeners = function () {
|
||||
this.unobserveObjects.forEach(function (unobserveObject) {
|
||||
unobserveObject();
|
||||
});
|
||||
this.unobserveObjects = [];
|
||||
};
|
||||
|
||||
EditToolbar.prototype.handleStateChanges = function (state) {
|
||||
(state || []).map(function (newValue, index) {
|
||||
var domainObject = this.stateTracker[index].domainObject;
|
||||
var property = this.stateTracker[index].property;
|
||||
var currentValue = _.get(domainObject, property);
|
||||
|
||||
if (currentValue !== newValue) {
|
||||
this.updateDomainObject(domainObject, property, newValue);
|
||||
}
|
||||
}, this);
|
||||
};
|
||||
|
||||
EditToolbar.prototype.destroy = function () {
|
||||
this.deregisterListeners();
|
||||
};
|
||||
|
||||
return EditToolbar;
|
||||
}
|
||||
);
|
||||
@@ -1,184 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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/controllers/ElementsController"],
|
||||
function (ElementsController) {
|
||||
|
||||
describe("The Elements Pane controller", function () {
|
||||
var mockScope,
|
||||
mockOpenMCT,
|
||||
mockSelection,
|
||||
mockDomainObject,
|
||||
mockMutationCapability,
|
||||
mockCompositionCapability,
|
||||
mockCompositionObjects,
|
||||
mockComposition,
|
||||
mockUnlisten,
|
||||
selectable = [],
|
||||
controller;
|
||||
|
||||
function mockPromise(value) {
|
||||
return {
|
||||
then: function (thenFunc) {
|
||||
return mockPromise(thenFunc(value));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function createDomainObject() {
|
||||
return {
|
||||
useCapability: function () {
|
||||
return mockCompositionCapability;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
mockComposition = ["a", "b"];
|
||||
mockCompositionObjects = mockComposition.map(createDomainObject);
|
||||
mockCompositionCapability = mockPromise(mockCompositionObjects);
|
||||
|
||||
mockUnlisten = jasmine.createSpy('unlisten');
|
||||
mockMutationCapability = jasmine.createSpyObj("mutationCapability", [
|
||||
"listen"
|
||||
]);
|
||||
mockMutationCapability.listen.and.returnValue(mockUnlisten);
|
||||
mockDomainObject = jasmine.createSpyObj("domainObject", [
|
||||
"getCapability",
|
||||
"useCapability"
|
||||
]);
|
||||
mockDomainObject.useCapability.and.returnValue(mockCompositionCapability);
|
||||
mockDomainObject.getCapability.and.returnValue(mockMutationCapability);
|
||||
|
||||
mockScope = jasmine.createSpyObj("$scope", ['$on']);
|
||||
mockSelection = jasmine.createSpyObj("selection", [
|
||||
'on',
|
||||
'off',
|
||||
'get'
|
||||
]);
|
||||
mockSelection.get.and.returnValue([]);
|
||||
mockOpenMCT = {
|
||||
selection: mockSelection
|
||||
};
|
||||
|
||||
selectable[0] = {
|
||||
context: {
|
||||
oldItem: mockDomainObject
|
||||
}
|
||||
};
|
||||
|
||||
spyOn(ElementsController.prototype, 'refreshComposition').and.callThrough();
|
||||
|
||||
controller = new ElementsController(mockScope, mockOpenMCT);
|
||||
});
|
||||
|
||||
function getModel(model) {
|
||||
return function () {
|
||||
return model;
|
||||
};
|
||||
}
|
||||
|
||||
it("filters objects in elements pool based on input text and" +
|
||||
" object name", function () {
|
||||
var objects = [
|
||||
{
|
||||
getModel: getModel({name: "first element"})
|
||||
},
|
||||
{
|
||||
getModel: getModel({name: "second element"})
|
||||
},
|
||||
{
|
||||
getModel: getModel({name: "third element"})
|
||||
},
|
||||
{
|
||||
getModel: getModel({name: "THIRD Element 1"})
|
||||
}
|
||||
];
|
||||
|
||||
mockScope.filterBy("third element");
|
||||
expect(objects.filter(mockScope.searchElements).length).toBe(2);
|
||||
mockScope.filterBy("element");
|
||||
expect(objects.filter(mockScope.searchElements).length).toBe(4);
|
||||
});
|
||||
|
||||
it("refreshes composition on selection", function () {
|
||||
mockOpenMCT.selection.on.calls.mostRecent().args[1](selectable);
|
||||
|
||||
expect(ElementsController.prototype.refreshComposition).toHaveBeenCalledWith(mockDomainObject);
|
||||
});
|
||||
|
||||
it("listens on mutation and refreshes composition", function () {
|
||||
mockOpenMCT.selection.on.calls.mostRecent().args[1](selectable);
|
||||
|
||||
expect(mockDomainObject.getCapability).toHaveBeenCalledWith('mutation');
|
||||
expect(mockMutationCapability.listen).toHaveBeenCalled();
|
||||
expect(ElementsController.prototype.refreshComposition.calls.count()).toBe(1);
|
||||
|
||||
mockMutationCapability.listen.calls.mostRecent().args[0](mockDomainObject);
|
||||
|
||||
expect(ElementsController.prototype.refreshComposition.calls.count()).toBe(2);
|
||||
});
|
||||
|
||||
it("cleans up mutation listener when selection changes", function () {
|
||||
mockOpenMCT.selection.on.calls.mostRecent().args[1](selectable);
|
||||
|
||||
expect(mockMutationCapability.listen).toHaveBeenCalled();
|
||||
|
||||
mockOpenMCT.selection.on.calls.mostRecent().args[1](selectable);
|
||||
|
||||
expect(mockUnlisten).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does not listen on mutation for element proxy selectable", function () {
|
||||
selectable[0] = {
|
||||
context: {
|
||||
elementProxy: {}
|
||||
}
|
||||
};
|
||||
mockOpenMCT.selection.on.calls.mostRecent().args[1](selectable);
|
||||
|
||||
expect(mockDomainObject.getCapability).not.toHaveBeenCalledWith('mutation');
|
||||
});
|
||||
|
||||
it("checks concurrent changes to composition", function () {
|
||||
var secondMockComposition = ["a", "b", "c"],
|
||||
secondMockCompositionObjects = secondMockComposition.map(createDomainObject),
|
||||
firstCompositionCallback,
|
||||
secondCompositionCallback;
|
||||
|
||||
spyOn(mockCompositionCapability, "then").and.callThrough();
|
||||
|
||||
controller.refreshComposition(mockDomainObject);
|
||||
controller.refreshComposition(mockDomainObject);
|
||||
|
||||
firstCompositionCallback = mockCompositionCapability.then.calls.all()[0].args[0];
|
||||
secondCompositionCallback = mockCompositionCapability.then.calls.all()[1].args[0];
|
||||
secondCompositionCallback(secondMockCompositionObjects);
|
||||
firstCompositionCallback(mockCompositionObjects);
|
||||
|
||||
expect(mockScope.composition).toBe(secondMockCompositionObjects);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -1,105 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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 ahenry on 01/21/14.
|
||||
*/
|
||||
define(
|
||||
["../../src/creation/AddActionProvider"],
|
||||
function (AddActionProvider) {
|
||||
|
||||
describe("The add action provider", function () {
|
||||
var mockTypeService,
|
||||
mockDialogService,
|
||||
mockPolicyService,
|
||||
mockTypeMap,
|
||||
mockTypes,
|
||||
mockDomainObject,
|
||||
mockQ,
|
||||
provider;
|
||||
|
||||
function createMockType(name) {
|
||||
var mockType = jasmine.createSpyObj(
|
||||
"type" + name,
|
||||
[
|
||||
"getKey",
|
||||
"getGlyph",
|
||||
"getCssClass",
|
||||
"getName",
|
||||
"getDescription",
|
||||
"getProperties",
|
||||
"getInitialModel",
|
||||
"hasFeature"
|
||||
]
|
||||
);
|
||||
mockType.hasFeature.and.returnValue(true);
|
||||
mockType.getName.and.returnValue(name);
|
||||
mockType.getKey.and.returnValue(name);
|
||||
return mockType;
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
mockTypeService = jasmine.createSpyObj(
|
||||
"typeService",
|
||||
["getType"]
|
||||
);
|
||||
mockDialogService = {};
|
||||
mockPolicyService = {};
|
||||
mockDomainObject = {};
|
||||
|
||||
mockTypes = [
|
||||
"timeline",
|
||||
"activity",
|
||||
"other"
|
||||
].map(createMockType);
|
||||
mockTypeMap = {};
|
||||
|
||||
mockTypes.forEach(function (type) {
|
||||
mockTypeMap[type.getKey()] = type;
|
||||
});
|
||||
|
||||
mockTypeService.getType.and.callFake(function (key) {
|
||||
return mockTypeMap[key];
|
||||
});
|
||||
|
||||
provider = new AddActionProvider(
|
||||
mockQ,
|
||||
mockTypeService,
|
||||
mockDialogService,
|
||||
mockPolicyService
|
||||
);
|
||||
});
|
||||
|
||||
it("provides actions for timeline and activity", function () {
|
||||
var actions = provider.getActions({
|
||||
key: "add",
|
||||
domainObject: mockDomainObject
|
||||
});
|
||||
expect(actions.length).toBe(2);
|
||||
expect(actions[0].metadata.type).toBe('timeline');
|
||||
expect(actions[1].metadata.type).toBe('activity');
|
||||
|
||||
// Make sure it was creation which was used to check
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -1,75 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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/representers/EditToolbar'],
|
||||
function (EditToolbar) {
|
||||
|
||||
describe("An Edit mode toolbar", function () {
|
||||
var mockOpenMCT,
|
||||
mockScope,
|
||||
mockObjects,
|
||||
mockDomainObject,
|
||||
testStructure,
|
||||
toolbar;
|
||||
|
||||
beforeEach(function () {
|
||||
mockOpenMCT = jasmine.createSpy('openmct', ['objects']);
|
||||
mockObjects = jasmine.createSpyObj('objects', ['observe']);
|
||||
mockObjects.observe.and.returnValue();
|
||||
mockOpenMCT.objects = mockObjects;
|
||||
mockScope = jasmine.createSpyObj("$scope", [
|
||||
"$watchCollection",
|
||||
"$on"
|
||||
]);
|
||||
mockScope.$watchCollection.and.returnValue();
|
||||
mockDomainObject = jasmine.createSpyObj("domainObject", [
|
||||
'identifier'
|
||||
]);
|
||||
|
||||
testStructure = [
|
||||
{ name: "A", property: "a", domainObject: mockDomainObject },
|
||||
{ name: "B", property: "b", domainObject: mockDomainObject },
|
||||
{ name: "C", property: "c", domainObject: mockDomainObject },
|
||||
{ name: "X", property: "x", domainObject: mockDomainObject },
|
||||
{ name: "Y", property: "y", domainObject: mockDomainObject },
|
||||
{ name: "Z", property: "z", domainObject: mockDomainObject },
|
||||
{ name: "M", method: "m", domainObject: mockDomainObject }
|
||||
];
|
||||
|
||||
toolbar = new EditToolbar(mockScope, mockOpenMCT, testStructure);
|
||||
});
|
||||
|
||||
it("adds click functions when a method is specified", function () {
|
||||
var structure = toolbar.getStructure();
|
||||
expect(structure[6].click).toBeDefined();
|
||||
});
|
||||
|
||||
it("adds key for controls that define a property", function () {
|
||||
var structure = toolbar.getStructure();
|
||||
expect(structure[0].key).toEqual(0);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<span class="l-progress-bar s-progress-bar"
|
||||
ng-class="{ indeterminate:ngModel.unknownProgress }">
|
||||
ng-class="{ indeterminate:ngModel.progressPerc === 'unknown' }">
|
||||
<span class="progress-amt-holder">
|
||||
<span class="progress-amt" style="width: {{ngModel.progress}}%"></span>
|
||||
<span class="progress-amt" style="width: {{ngModel.progressPerc === 'unknown' ? 100 : ngModel.progressPerc}}%"></span>
|
||||
</span>
|
||||
</span>
|
||||
<div class="progress-info hint" ng-hide="ngModel.progressText === undefined">
|
||||
<span class="progress-amt-text" ng-show="ngModel.progress > 0">{{ngModel.progress}}% complete. </span>
|
||||
<span class="progress-amt-text" ng-show="ngModel.progressPerc !== 'unknown' && ngModel.progressPerc > 0">{{ngModel.progressPerc}}% complete. </span>
|
||||
{{ngModel.progressText}}
|
||||
</div>
|
||||
|
||||
@@ -50,7 +50,7 @@ define(
|
||||
};
|
||||
$scope.dismiss = function (notification, $event) {
|
||||
$event.stopPropagation();
|
||||
notification.dismissOrMinimize();
|
||||
notification.dismiss();
|
||||
};
|
||||
$scope.maximize = function (notification) {
|
||||
if (notification.model.severity !== "info") {
|
||||
@@ -60,7 +60,7 @@ define(
|
||||
};
|
||||
//If the notification is dismissed by the user, close
|
||||
// the dialog.
|
||||
notification.onDismiss(function () {
|
||||
notification.on('dismiss', function () {
|
||||
dialog.dismiss();
|
||||
});
|
||||
|
||||
|
||||
@@ -36,20 +36,6 @@ define([
|
||||
|
||||
legacyRegistry.register("platform/commonUI/notification", {
|
||||
"extensions": {
|
||||
"constants": [
|
||||
{
|
||||
"key": "DEFAULT_AUTO_DISMISS",
|
||||
"value": 3000
|
||||
},
|
||||
{
|
||||
"key": "FORCE_AUTO_DISMISS",
|
||||
"value": 1000
|
||||
},
|
||||
{
|
||||
"key": "MINIMIZE_TIMEOUT",
|
||||
"value": 300
|
||||
}
|
||||
],
|
||||
"templates": [
|
||||
{
|
||||
"key": "notificationIndicatorTemplate",
|
||||
@@ -62,7 +48,7 @@ define([
|
||||
"implementation": NotificationIndicatorController,
|
||||
"depends": [
|
||||
"$scope",
|
||||
"notificationService",
|
||||
"openmct",
|
||||
"dialogService"
|
||||
]
|
||||
}
|
||||
@@ -76,12 +62,11 @@ define([
|
||||
"services": [
|
||||
{
|
||||
"key": "notificationService",
|
||||
"implementation": NotificationService,
|
||||
"implementation": function (openmct) {
|
||||
return new NotificationService.default(openmct);
|
||||
},
|
||||
"depends": [
|
||||
"$timeout",
|
||||
"topic",
|
||||
"DEFAULT_AUTO_DISMISS",
|
||||
"MINIMIZE_TIMEOUT"
|
||||
"openmct"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -35,9 +35,9 @@ define(
|
||||
* @param dialogService
|
||||
* @constructor
|
||||
*/
|
||||
function NotificationIndicatorController($scope, notificationService, dialogService) {
|
||||
$scope.notifications = notificationService.notifications;
|
||||
$scope.highest = notificationService.highest;
|
||||
function NotificationIndicatorController($scope, openmct, dialogService) {
|
||||
$scope.notifications = openmct.notifications.notifications;
|
||||
$scope.highest = openmct.notifications.highest;
|
||||
|
||||
/**
|
||||
* Launch a dialog showing a list of current notifications.
|
||||
@@ -48,7 +48,7 @@ define(
|
||||
title: "Messages",
|
||||
//Launch the message list dialog with the models
|
||||
// from the notifications
|
||||
messages: notificationService.notifications
|
||||
messages: openmct.notifications.notifications
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -19,419 +19,46 @@
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* This bundle implements the notification service, which can be used to
|
||||
* show banner notifications to the user. Banner notifications
|
||||
* are used to inform users of events in a non-intrusive way. As
|
||||
* much as possible, notifications share a model with blocking
|
||||
* dialogs so that the same information can be provided in a dialog
|
||||
* and then minimized to a banner notification if needed.
|
||||
*
|
||||
* @namespace platform/commonUI/notification
|
||||
*/
|
||||
define(
|
||||
['moment'],
|
||||
function (moment) {
|
||||
|
||||
/**
|
||||
* A representation of a user action. Options are provided to
|
||||
* dialogs and notifications and are shown as buttons.
|
||||
*
|
||||
* @typedef {object} NotificationOption
|
||||
* @property {string} label the label to appear on the button for
|
||||
* this action
|
||||
* @property {function} callback a callback function to be invoked
|
||||
* when the button is clicked
|
||||
*/
|
||||
|
||||
/**
|
||||
* A representation of a banner notification. Banner notifications
|
||||
* are used to inform users of events in a non-intrusive way. As
|
||||
* much as possible, notifications share a model with blocking
|
||||
* dialogs so that the same information can be provided in a dialog
|
||||
* and then minimized to a banner notification if needed, or vice-versa.
|
||||
*
|
||||
* @typedef {object} NotificationModel
|
||||
* @property {string} title The title of the message
|
||||
* @property {string} severity The importance of the message (one of
|
||||
* 'info', 'alert', or 'error' where info < alert <error)
|
||||
* @property {number} [progress] The completion status of a task
|
||||
* represented numerically
|
||||
* @property {boolean} [unknownProgress] a boolean indicating that the
|
||||
* progress of the underlying task is unknown. This will result in a
|
||||
* visually distinct progress bar.
|
||||
* @property {boolean} [autoDismiss] If truthy, dialog will
|
||||
* be automatically minimized or dismissed (depending on severity).
|
||||
* Additionally, if the provided value is a number, it will be used
|
||||
* as the delay period before being dismissed.
|
||||
* @property {boolean} [dismissable=true] If true, notification will
|
||||
* include an option to dismiss it completely.
|
||||
* @property {NotificationOption} [primaryOption] the default user
|
||||
* response to
|
||||
* this message. Will be represented as a button with the provided
|
||||
* label and action. May be used by banner notifications to display
|
||||
* only the most important option to users.
|
||||
* @property {NotificationOption[]} [options] any additional
|
||||
* actions the user can take. Will be represented as additional buttons
|
||||
* that may or may not be available from a banner.
|
||||
* @see DialogModel
|
||||
*/
|
||||
|
||||
/**
|
||||
* A wrapper object that is returned as a handle to a newly created
|
||||
* notification. Wraps the notifications model and decorates with
|
||||
* functions to dismiss or minimize the notification.
|
||||
*
|
||||
* @typedef {object} Notification
|
||||
* @property {function} dismiss This method is added to the object
|
||||
* returned by {@link NotificationService#notify} and can be used to
|
||||
* dismiss this notification. Dismissing a notification will remove
|
||||
* it completely and it will not appear in the notification indicator
|
||||
* @property {function} minimize This method is added to the object
|
||||
* returned by {@link NotificationService#notify} and can be used to
|
||||
* minimize this notification. Minimizing a notification will send
|
||||
* it to the notification indicator
|
||||
* @property {function} dismissOrMinimize This method is added to the
|
||||
* object returned by {@link NotificationService#notify}. It will
|
||||
* hide the notification by either dismissing or minimizing it,
|
||||
* depending on severity. Typically this is the method that should
|
||||
* be used for dismissing a notification. If more control is
|
||||
* required, then the minimize or dismiss functions can be called
|
||||
* individually.
|
||||
* @property {function} onDismiss Allows listening for on dismiss
|
||||
* events. This allows cleanup etc. when the notification is dismissed.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The notification service is responsible for informing the user of
|
||||
* events via the use of banner notifications.
|
||||
* @memberof platform/commonUI/notification
|
||||
* @constructor
|
||||
* @param $timeout the Angular $timeout service
|
||||
* @param defaultAutoDismissTimeout The period of time that an
|
||||
* auto-dismissed message will be displayed for.
|
||||
* @param minimizeAnimationTimeout When notifications are minimized, a brief
|
||||
* animation is shown. This animation requires some time to execute,
|
||||
* so a timeout is required before the notification is hidden
|
||||
*/
|
||||
function NotificationService($timeout, topic, defaultAutoDismissTimeout, minimizeAnimationTimeout) {
|
||||
this.notifications = [];
|
||||
this.$timeout = $timeout;
|
||||
this.highest = { severity: "info" };
|
||||
this.AUTO_DISMISS_TIMEOUT = defaultAutoDismissTimeout;
|
||||
this.MINIMIZE_ANIMATION_TIMEOUT = minimizeAnimationTimeout;
|
||||
this.topic = topic;
|
||||
|
||||
/*
|
||||
* A context in which to hold the active notification and a
|
||||
* handle to its timeout.
|
||||
*/
|
||||
this.active = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Minimize a notification. The notification will still be available
|
||||
* from the notification list. Typically notifications with a
|
||||
* severity of 'info' should not be minimized, but rather
|
||||
* dismissed. If you're not sure which is appropriate,
|
||||
* use {@link Notification#dismissOrMinimize}
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
NotificationService.prototype.minimize = function (service, notification) {
|
||||
//Check this is a known notification
|
||||
var index = service.notifications.indexOf(notification);
|
||||
|
||||
if (service.active.timeout) {
|
||||
/*
|
||||
Method can be called manually (clicking dismiss) or
|
||||
automatically from an auto-timeout. this.active.timeout
|
||||
acts as a semaphore to prevent race conditions. Cancel any
|
||||
timeout in progress (for the case where a manual dismiss
|
||||
has shortcut an active auto-dismiss), and clear the
|
||||
semaphore.
|
||||
*/
|
||||
service.$timeout.cancel(service.active.timeout);
|
||||
delete service.active.timeout;
|
||||
}
|
||||
|
||||
if (index >= 0) {
|
||||
notification.model.minimized = true;
|
||||
//Add a brief timeout before showing the next notification
|
||||
// in order to allow the minimize animation to run through.
|
||||
service.$timeout(function () {
|
||||
service.setActiveNotification(service.selectNextNotification());
|
||||
}, service.MINIMIZE_ANIMATION_TIMEOUT);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Completely removes a notification. This will dismiss it from the
|
||||
* message banner and remove it from the list of notifications.
|
||||
* Typically only notifications with a severity of info should be
|
||||
* dismissed. If you're not sure whether to dismiss or minimize a
|
||||
* notification, use {@link Notification#dismissOrMinimize}.
|
||||
* dismiss
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
NotificationService.prototype.dismiss = function (service, notification) {
|
||||
//Check this is a known notification
|
||||
var index = service.notifications.indexOf(notification);
|
||||
|
||||
if (service.active.timeout) {
|
||||
/* Method can be called manually (clicking dismiss) or
|
||||
* automatically from an auto-timeout. this.active.timeout
|
||||
* acts as a semaphore to prevent race conditions. Cancel any
|
||||
* timeout in progress (for the case where a manual dismiss
|
||||
* has shortcut an active auto-dismiss), and clear the
|
||||
* semaphore.
|
||||
*/
|
||||
|
||||
service.$timeout.cancel(service.active.timeout);
|
||||
delete service.active.timeout;
|
||||
}
|
||||
|
||||
if (index >= 0) {
|
||||
service.notifications.splice(index, 1);
|
||||
}
|
||||
service.setActiveNotification(service.selectNextNotification());
|
||||
|
||||
this.setHighestSeverity();
|
||||
};
|
||||
|
||||
/**
|
||||
* Depending on the severity of the notification will selectively
|
||||
* dismiss or minimize where appropriate.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
NotificationService.prototype.dismissOrMinimize = function (notification) {
|
||||
var model = notification.model;
|
||||
if (model.severity === "info") {
|
||||
if (model.autoDismiss === false) {
|
||||
notification.minimize();
|
||||
} else {
|
||||
notification.dismiss();
|
||||
}
|
||||
} else {
|
||||
notification.minimize();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the notification that is currently visible in the banner area
|
||||
* @returns {Notification}
|
||||
*/
|
||||
NotificationService.prototype.getActiveNotification = function () {
|
||||
return this.active.notification;
|
||||
};
|
||||
|
||||
/**
|
||||
* A convenience method for info notifications. Notifications
|
||||
* created via this method will be auto-dismissed after a default
|
||||
* wait period unless explicitly forbidden by the caller through
|
||||
* the {autoDismiss} property on the {NotificationModel}, in which
|
||||
* case the notification will be minimized after the wait.
|
||||
* @param {NotificationModel | string} message either a string for
|
||||
* the title of the notification message, or a {@link NotificationModel}
|
||||
* defining the options notification to display
|
||||
* @returns {Notification} the provided notification decorated with
|
||||
* functions to dismiss or minimize
|
||||
*/
|
||||
NotificationService.prototype.info = function (message) {
|
||||
var notificationModel = typeof message === "string" ? {title: message} : message;
|
||||
notificationModel.severity = "info";
|
||||
return this.notify(notificationModel);
|
||||
};
|
||||
|
||||
/**
|
||||
* A convenience method for alert notifications. Notifications
|
||||
* created via this method will will have severity of "alert" enforced
|
||||
* @param {NotificationModel | string} message either a string for
|
||||
* the title of the alert message with default options, or a
|
||||
* {@link NotificationModel} defining the options notification to
|
||||
* display
|
||||
* @returns {Notification} the provided notification decorated with
|
||||
* functions to dismiss or minimize
|
||||
*/
|
||||
NotificationService.prototype.alert = function (message) {
|
||||
var notificationModel = typeof message === "string" ? {title: message} : message;
|
||||
notificationModel.severity = "alert";
|
||||
return this.notify(notificationModel);
|
||||
};
|
||||
|
||||
/**
|
||||
* A convenience method for error notifications. Notifications
|
||||
* created via this method will will have severity of "error" enforced
|
||||
* @param {NotificationModel | string} message either a string for
|
||||
* the title of the error message with default options, or a
|
||||
* {@link NotificationModel} defining the options notification to
|
||||
* display
|
||||
* @returns {Notification} the provided notification decorated with
|
||||
* functions to dismiss or minimize
|
||||
*/
|
||||
NotificationService.prototype.error = function (message) {
|
||||
var notificationModel = typeof message === "string" ? {title: message} : message;
|
||||
notificationModel.severity = "error";
|
||||
return this.notify(notificationModel);
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
NotificationService.prototype.setHighestSeverity = function () {
|
||||
var severity = {
|
||||
"info": 1,
|
||||
"alert": 2,
|
||||
"error": 3
|
||||
};
|
||||
this.highest.severity = this.notifications.reduce(function (previous, notification) {
|
||||
if (severity[notification.model.severity] > severity[previous]) {
|
||||
return notification.model.severity;
|
||||
} else {
|
||||
return previous;
|
||||
}
|
||||
}, "info");
|
||||
};
|
||||
|
||||
/**
|
||||
* Notifies the user of an event. If there is a banner notification
|
||||
* already active, then it will be dismissed or minimized automatically,
|
||||
* and the provided notification displayed in its place.
|
||||
*
|
||||
* @param {NotificationModel} notificationModel The notification to
|
||||
* display
|
||||
* @returns {Notification} the provided notification decorated with
|
||||
* functions to {@link Notification#dismiss} or {@link Notification#minimize}
|
||||
*/
|
||||
NotificationService.prototype.notify = function (notificationModel) {
|
||||
var self = this,
|
||||
notification,
|
||||
activeNotification = self.active.notification,
|
||||
topic = this.topic();
|
||||
|
||||
notificationModel.severity = notificationModel.severity || "info";
|
||||
notificationModel.timestamp = moment.utc().format('YYYY-MM-DD hh:mm:ss.ms');
|
||||
|
||||
notification = {
|
||||
model: notificationModel,
|
||||
|
||||
minimize: function () {
|
||||
self.minimize(self, notification);
|
||||
},
|
||||
|
||||
dismiss: function () {
|
||||
self.dismiss(self, notification);
|
||||
topic.notify();
|
||||
},
|
||||
|
||||
dismissOrMinimize: function () {
|
||||
self.dismissOrMinimize(notification);
|
||||
},
|
||||
|
||||
onDismiss: function (callback) {
|
||||
topic.listen(callback);
|
||||
}
|
||||
};
|
||||
|
||||
//Notifications support a 'dismissable' attribute. This is a
|
||||
// convenience to support adding a 'dismiss' option to the
|
||||
// notification for the common case of dismissing a
|
||||
// notification. Could also be done manually by specifying an
|
||||
// option on the model
|
||||
if (notificationModel.dismissable !== false) {
|
||||
notificationModel.options = notificationModel.options || [];
|
||||
notificationModel.options.unshift({
|
||||
label: "Dismiss",
|
||||
callback: function () {
|
||||
notification.dismiss();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.notifications.push(notification);
|
||||
|
||||
this.setHighestSeverity();
|
||||
|
||||
/*
|
||||
Check if there is already an active (ie. visible) notification
|
||||
*/
|
||||
if (!this.active.notification) {
|
||||
this.setActiveNotification(notification);
|
||||
|
||||
} else if (!this.active.timeout) {
|
||||
/*
|
||||
If there is already an active notification, time it out. If it's
|
||||
already got a timeout in progress (either because it has had
|
||||
timeout forced because of a queue of messages, or it had an
|
||||
autodismiss specified), leave it to run. Otherwise force a
|
||||
timeout.
|
||||
|
||||
This notification has been added to queue and will be
|
||||
serviced as soon as possible.
|
||||
*/
|
||||
this.active.timeout = this.$timeout(function () {
|
||||
activeNotification.dismissOrMinimize();
|
||||
}, this.AUTO_DISMISS_TIMEOUT);
|
||||
}
|
||||
|
||||
return notification;
|
||||
};
|
||||
|
||||
/**
|
||||
* Used internally by the NotificationService
|
||||
* @private
|
||||
*/
|
||||
NotificationService.prototype.setActiveNotification = function (notification) {
|
||||
var shouldAutoDismiss;
|
||||
this.active.notification = notification;
|
||||
|
||||
if (!notification) {
|
||||
delete this.active.timeout;
|
||||
return;
|
||||
}
|
||||
|
||||
if (notification.model.severity === "info") {
|
||||
shouldAutoDismiss = true;
|
||||
} else {
|
||||
shouldAutoDismiss = notification.model.autoDismiss;
|
||||
}
|
||||
|
||||
if (shouldAutoDismiss || this.selectNextNotification()) {
|
||||
this.active.timeout = this.$timeout(function () {
|
||||
notification.dismissOrMinimize();
|
||||
}, this.AUTO_DISMISS_TIMEOUT);
|
||||
} else {
|
||||
delete this.active.timeout;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Used internally by the NotificationService
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
NotificationService.prototype.selectNextNotification = function () {
|
||||
var notification,
|
||||
i = 0;
|
||||
|
||||
/*
|
||||
Loop through the notifications queue and find the first one that
|
||||
has not already been minimized (manually or otherwise).
|
||||
*/
|
||||
for (; i < this.notifications.length; i++) {
|
||||
notification = this.notifications[i];
|
||||
|
||||
if (!notification.model.minimized &&
|
||||
notification !== this.active.notification) {
|
||||
|
||||
return notification;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return NotificationService;
|
||||
export default class NotificationService {
|
||||
constructor(openmct) {
|
||||
this.openmct = openmct;
|
||||
}
|
||||
);
|
||||
info(message) {
|
||||
if (typeof message === 'string') {
|
||||
return this.openmct.notifications.info(message);
|
||||
} else {
|
||||
if (message.hasOwnProperty('progress')) {
|
||||
return this.openmct.notifications.progress(message.title, message.progress, message.progressText);
|
||||
} else {
|
||||
return this.openmct.notifications.info(message.title);
|
||||
}
|
||||
}
|
||||
}
|
||||
alert(message) {
|
||||
if (typeof message === 'string') {
|
||||
return this.openmct.notifications.alert(message);
|
||||
} else {
|
||||
return this.openmct.notifications.alert(message.title);
|
||||
}
|
||||
}
|
||||
error(message) {
|
||||
if (typeof message === 'string') {
|
||||
return this.openmct.notifications.error(message);
|
||||
} else {
|
||||
return this.openmct.notifications.error(message.title);
|
||||
}
|
||||
}
|
||||
notify(options) {
|
||||
switch (options.severity) {
|
||||
case 'info':
|
||||
return this.info(options);
|
||||
case 'alert':
|
||||
return this.alert(options);
|
||||
case 'error':
|
||||
return this.error(options);
|
||||
}
|
||||
}
|
||||
getAllNotifications() {
|
||||
return this.openmct.notifications.notifications;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,275 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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/NotificationService'],
|
||||
function (NotificationService) {
|
||||
|
||||
describe("The notification service ", function () {
|
||||
var notificationService,
|
||||
mockTimeout,
|
||||
mockAutoDismiss,
|
||||
mockMinimizeTimeout,
|
||||
mockTopicFunction,
|
||||
mockTopicObject,
|
||||
infoModel,
|
||||
alertModel,
|
||||
errorModel;
|
||||
|
||||
function elapseTimeout() {
|
||||
mockTimeout.calls.mostRecent().args[0]();
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
mockTimeout = jasmine.createSpy("$timeout");
|
||||
mockTopicFunction = jasmine.createSpy("topic");
|
||||
mockTopicObject = jasmine.createSpyObj("topicObject", ["listen", "notify"]);
|
||||
mockTopicFunction.and.returnValue(mockTopicObject);
|
||||
|
||||
mockAutoDismiss = mockMinimizeTimeout = 1000;
|
||||
notificationService = new NotificationService(mockTimeout, mockTopicFunction, mockAutoDismiss, mockMinimizeTimeout);
|
||||
|
||||
infoModel = {
|
||||
title: "Mock Info Notification",
|
||||
severity: "info"
|
||||
};
|
||||
|
||||
alertModel = {
|
||||
title: "Mock Alert Notification",
|
||||
severity: "alert"
|
||||
};
|
||||
|
||||
errorModel = {
|
||||
title: "Mock Error Notification",
|
||||
severity: "error"
|
||||
};
|
||||
});
|
||||
|
||||
it("notifies listeners on dismissal of notification", function () {
|
||||
var dismissListener = jasmine.createSpy("ondismiss");
|
||||
var notification = notificationService.notify(infoModel);
|
||||
notification.onDismiss(dismissListener);
|
||||
expect(mockTopicObject.listen).toHaveBeenCalled();
|
||||
notification.dismiss();
|
||||
expect(mockTopicObject.notify).toHaveBeenCalled();
|
||||
mockTopicObject.listen.calls.mostRecent().args[0]();
|
||||
expect(dismissListener).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("dismisses a notification when the notification's dismiss method is used", function () {
|
||||
var notification = notificationService.info(infoModel);
|
||||
notification.dismiss();
|
||||
expect(notificationService.getActiveNotification()).toBeUndefined();
|
||||
expect(notificationService.notifications.length).toEqual(0);
|
||||
});
|
||||
|
||||
it("minimizes a notification when the notification's minimize method is used", function () {
|
||||
var notification = notificationService.info(infoModel);
|
||||
notification.minimize();
|
||||
elapseTimeout(); // needed for the minimize animation timeout
|
||||
expect(notificationService.getActiveNotification()).toBeUndefined();
|
||||
expect(notificationService.notifications.length).toEqual(1);
|
||||
expect(notificationService.notifications[0]).toEqual(notification);
|
||||
});
|
||||
|
||||
describe("when receiving info notifications", function () {
|
||||
it("minimizes info notifications if the caller disables auto-dismiss", function () {
|
||||
infoModel.autoDismiss = false;
|
||||
var notification = notificationService.info(infoModel);
|
||||
elapseTimeout();
|
||||
// 2nd elapse for the minimize animation timeout
|
||||
elapseTimeout();
|
||||
expect(notificationService.getActiveNotification()).toBeUndefined();
|
||||
expect(notificationService.notifications.length).toEqual(1);
|
||||
expect(notificationService.notifications[0]).toEqual(notification);
|
||||
});
|
||||
|
||||
it("dismisses info notifications if the caller ignores auto-dismiss", function () {
|
||||
notificationService.info(infoModel);
|
||||
elapseTimeout();
|
||||
expect(notificationService.getActiveNotification()).toBeUndefined();
|
||||
expect(notificationService.notifications.length).toEqual(0);
|
||||
});
|
||||
|
||||
it("dismisses info notifications if the caller requests auto-dismiss", function () {
|
||||
infoModel.autoDismiss = true;
|
||||
notificationService.info(infoModel);
|
||||
elapseTimeout();
|
||||
expect(notificationService.getActiveNotification()).toBeUndefined();
|
||||
expect(notificationService.notifications.length).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when receiving alert notifications", function () {
|
||||
it("minimizes alert notifications if the caller enables auto-dismiss", function () {
|
||||
alertModel.autoDismiss = true;
|
||||
var notification = notificationService.alert(alertModel);
|
||||
elapseTimeout();
|
||||
elapseTimeout();
|
||||
expect(notificationService.getActiveNotification()).toBeUndefined();
|
||||
expect(notificationService.notifications.length).toEqual(1);
|
||||
expect(notificationService.notifications[0]).toEqual(notification);
|
||||
});
|
||||
|
||||
it("keeps alert notifications active if the caller disables auto-dismiss", function () {
|
||||
mockTimeout.and.callFake(function (callback, time) {
|
||||
callback();
|
||||
});
|
||||
alertModel.autoDismiss = false;
|
||||
var notification = notificationService.alert(alertModel);
|
||||
expect(notificationService.getActiveNotification()).toEqual(notification);
|
||||
expect(notificationService.notifications.length).toEqual(1);
|
||||
expect(notificationService.notifications[0]).toEqual(notification);
|
||||
});
|
||||
|
||||
it("keeps alert notifications active if the caller ignores auto-dismiss", function () {
|
||||
mockTimeout.and.callFake(function (callback, time) {
|
||||
callback();
|
||||
});
|
||||
var notification = notificationService.alert(alertModel);
|
||||
expect(notificationService.getActiveNotification()).toEqual(notification);
|
||||
expect(notificationService.notifications.length).toEqual(1);
|
||||
expect(notificationService.notifications[0]).toEqual(notification);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when receiving error notifications", function () {
|
||||
it("minimizes error notifications if the caller enables auto-dismiss", function () {
|
||||
errorModel.autoDismiss = true;
|
||||
var notification = notificationService.error(errorModel);
|
||||
elapseTimeout();
|
||||
elapseTimeout();
|
||||
expect(notificationService.getActiveNotification()).toBeUndefined();
|
||||
expect(notificationService.notifications.length).toEqual(1);
|
||||
expect(notificationService.notifications[0]).toEqual(notification);
|
||||
});
|
||||
|
||||
it("keeps error notifications active if the caller disables auto-dismiss", function () {
|
||||
mockTimeout.and.callFake(function (callback, time) {
|
||||
callback();
|
||||
});
|
||||
errorModel.autoDismiss = false;
|
||||
var notification = notificationService.error(errorModel);
|
||||
expect(notificationService.getActiveNotification()).toEqual(notification);
|
||||
expect(notificationService.notifications.length).toEqual(1);
|
||||
expect(notificationService.notifications[0]).toEqual(notification);
|
||||
});
|
||||
|
||||
it("keeps error notifications active if the caller ignores auto-dismiss", function () {
|
||||
mockTimeout.and.callFake(function (callback, time) {
|
||||
callback();
|
||||
});
|
||||
var notification = notificationService.error(errorModel);
|
||||
expect(notificationService.getActiveNotification()).toEqual(notification);
|
||||
expect(notificationService.notifications.length).toEqual(1);
|
||||
expect(notificationService.notifications[0]).toEqual(notification);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when called with multiple notifications", function () {
|
||||
it("auto-dismisses the previously active notification, making the new notification active", function () {
|
||||
var activeNotification;
|
||||
infoModel.autoDismiss = false;
|
||||
//First pre-load with a info message
|
||||
notificationService.notify(infoModel);
|
||||
activeNotification = notificationService.getActiveNotification();
|
||||
//Initially expect the active notification to be info
|
||||
expect(activeNotification.model).toBe(infoModel);
|
||||
//Then notify of an error
|
||||
notificationService.notify(errorModel);
|
||||
//But it should be auto-dismissed and replaced with the
|
||||
// error notification
|
||||
elapseTimeout();
|
||||
//Two timeouts, one is to force minimization after
|
||||
// displaying the message for a minimum period, the
|
||||
// second is to allow minimization animation to take place.
|
||||
elapseTimeout();
|
||||
activeNotification = notificationService.getActiveNotification();
|
||||
expect(activeNotification.model).toBe(errorModel);
|
||||
});
|
||||
|
||||
it("auto-minimizes an active error notification", function () {
|
||||
var activeNotification;
|
||||
//First pre-load with an error message
|
||||
notificationService.notify(errorModel);
|
||||
//Then notify of info
|
||||
notificationService.notify(infoModel);
|
||||
expect(notificationService.notifications.length).toEqual(2);
|
||||
//Mock the auto-minimize
|
||||
elapseTimeout();
|
||||
//Two timeouts, one is to force minimization after
|
||||
// displaying the message for a minimum period, the
|
||||
// second is to allow minimization animation to take place.
|
||||
elapseTimeout();
|
||||
//Previous error message should be minimized, not
|
||||
// dismissed
|
||||
expect(notificationService.notifications.length).toEqual(2);
|
||||
activeNotification = notificationService.getActiveNotification();
|
||||
expect(activeNotification.model).toBe(infoModel);
|
||||
expect(errorModel.minimized).toEqual(true);
|
||||
});
|
||||
|
||||
it("auto-minimizes errors when a number of them arrive in short succession", function () {
|
||||
var activeNotification,
|
||||
error2 = {
|
||||
title: "Second Mock Error Notification",
|
||||
severity: "error"
|
||||
},
|
||||
error3 = {
|
||||
title: "Third Mock Error Notification",
|
||||
severity: "error"
|
||||
};
|
||||
|
||||
//First pre-load with a info message
|
||||
notificationService.notify(errorModel);
|
||||
//Then notify of a third error
|
||||
notificationService.notify(error2);
|
||||
notificationService.notify(error3);
|
||||
expect(notificationService.notifications.length).toEqual(3);
|
||||
//Mock the auto-minimize
|
||||
elapseTimeout();
|
||||
//Two timeouts, one is to force minimization after
|
||||
// displaying the message for a minimum period, the
|
||||
// second is to allow minimization animation to take place.
|
||||
elapseTimeout();
|
||||
//Previous error message should be minimized, not
|
||||
// dismissed
|
||||
expect(notificationService.notifications.length).toEqual(3);
|
||||
activeNotification = notificationService.getActiveNotification();
|
||||
expect(activeNotification.model).toBe(error2);
|
||||
expect(errorModel.minimized).toEqual(true);
|
||||
|
||||
//Mock the second auto-minimize
|
||||
elapseTimeout();
|
||||
//Two timeouts, one is to force minimization after
|
||||
// displaying the message for a minimum period, the
|
||||
// second is to allow minimization animation to take place.
|
||||
elapseTimeout();
|
||||
activeNotification = notificationService.getActiveNotification();
|
||||
expect(activeNotification.model).toBe(error3);
|
||||
expect(error2.minimized).toEqual(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -58,7 +58,8 @@ define([
|
||||
"category": "action",
|
||||
"implementation": ComposeActionPolicy,
|
||||
"depends": [
|
||||
"$injector"
|
||||
"$injector",
|
||||
"openmct"
|
||||
],
|
||||
"message": "Objects of this type cannot contain objects of that type."
|
||||
},
|
||||
|
||||
@@ -36,10 +36,11 @@ define(
|
||||
* @memberof platform/containment
|
||||
* @implements {Policy.<Action, ActionContext>}
|
||||
*/
|
||||
function ComposeActionPolicy($injector) {
|
||||
function ComposeActionPolicy($injector, openmct) {
|
||||
this.getPolicyService = function () {
|
||||
return $injector.get('policyService');
|
||||
};
|
||||
this.openmct = openmct;
|
||||
}
|
||||
|
||||
ComposeActionPolicy.prototype.allowComposition = function (containerObject, selectedObject) {
|
||||
@@ -49,11 +50,8 @@ define(
|
||||
|
||||
// ...and delegate to the composition policy
|
||||
return containerObject.getId() !== selectedObject.getId() &&
|
||||
this.policyService.allow(
|
||||
'composition',
|
||||
containerObject,
|
||||
selectedObject
|
||||
);
|
||||
this.openmct.composition.checkPolicy(containerObject.useCapability('adapter'),
|
||||
selectedObject.useCapability('adapter'));
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -170,7 +170,7 @@ define([
|
||||
"description": "Provides a service for moving objects",
|
||||
"implementation": MoveService,
|
||||
"depends": [
|
||||
"policyService",
|
||||
"openmct",
|
||||
"linkService",
|
||||
"$q"
|
||||
]
|
||||
@@ -181,7 +181,7 @@ define([
|
||||
"description": "Provides a service for linking objects",
|
||||
"implementation": LinkService,
|
||||
"depends": [
|
||||
"policyService"
|
||||
"openmct"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -192,7 +192,7 @@ define([
|
||||
"depends": [
|
||||
"$q",
|
||||
"policyService",
|
||||
"now"
|
||||
"openmct"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -33,9 +33,10 @@ define(
|
||||
* @memberof platform/entanglement
|
||||
* @implements {platform/entanglement.AbstractComposeService}
|
||||
*/
|
||||
function CopyService($q, policyService) {
|
||||
function CopyService($q, policyService, openmct) {
|
||||
this.$q = $q;
|
||||
this.policyService = policyService;
|
||||
this.openmct = openmct;
|
||||
}
|
||||
|
||||
CopyService.prototype.validate = function (object, parentCandidate) {
|
||||
@@ -45,11 +46,7 @@ define(
|
||||
if (parentCandidate.getId() === object.getId()) {
|
||||
return false;
|
||||
}
|
||||
return this.policyService.allow(
|
||||
"composition",
|
||||
parentCandidate,
|
||||
object
|
||||
);
|
||||
return this.openmct.composition.checkPolicy(parentCandidate.useCapability('adapter'), object.useCapability('adapter'));
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -32,8 +32,8 @@ define(
|
||||
* @memberof platform/entanglement
|
||||
* @implements {platform/entanglement.AbstractComposeService}
|
||||
*/
|
||||
function LinkService(policyService) {
|
||||
this.policyService = policyService;
|
||||
function LinkService(openmct) {
|
||||
this.openmct = openmct;
|
||||
}
|
||||
|
||||
LinkService.prototype.validate = function (object, parentCandidate) {
|
||||
@@ -49,11 +49,7 @@ define(
|
||||
if (parentCandidate.getModel().composition.indexOf(object.getId()) !== -1) {
|
||||
return false;
|
||||
}
|
||||
return this.policyService.allow(
|
||||
"composition",
|
||||
parentCandidate,
|
||||
object
|
||||
);
|
||||
return this.openmct.composition.checkPolicy(parentCandidate.useCapability('adapter'), object.useCapability('adapter'));
|
||||
};
|
||||
|
||||
LinkService.prototype.perform = function (object, parentObject) {
|
||||
|
||||
@@ -31,8 +31,8 @@ define(
|
||||
* @memberof platform/entanglement
|
||||
* @implements {platform/entanglement.AbstractComposeService}
|
||||
*/
|
||||
function MoveService(policyService, linkService) {
|
||||
this.policyService = policyService;
|
||||
function MoveService(openmct, linkService) {
|
||||
this.openmct = openmct;
|
||||
this.linkService = linkService;
|
||||
}
|
||||
|
||||
@@ -53,10 +53,9 @@ define(
|
||||
if (parentCandidate.getModel().composition.indexOf(object.getId()) !== -1) {
|
||||
return false;
|
||||
}
|
||||
return this.policyService.allow(
|
||||
"composition",
|
||||
parentCandidate,
|
||||
object
|
||||
return this.openmct.composition.checkPolicy(
|
||||
parentCandidate.useCapability('adapter'),
|
||||
object.useCapability('adapter')
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ define([
|
||||
{
|
||||
key: "exportService",
|
||||
implementation: function () {
|
||||
return new ExportService(saveAs);
|
||||
return new ExportService(saveAs.saveAs);
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
@@ -1,399 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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([
|
||||
"../layout/res/templates/fixed.html",
|
||||
'legacyRegistry'
|
||||
], function (
|
||||
fixedTemplate,
|
||||
legacyRegistry
|
||||
) {
|
||||
|
||||
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": [],
|
||||
"editable": true
|
||||
}
|
||||
],
|
||||
"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 (
|
||||
selection[0] && selection[0].context.elementProxy &&
|
||||
selection[1] && selection[1].context.item.type === 'telemetry.fixed' ||
|
||||
selection[0] && 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-button",
|
||||
domainObject: domainObject || selection[0].context.item,
|
||||
method: function (value) {
|
||||
selection[0].context.fixedController.add(value);
|
||||
},
|
||||
key: "add",
|
||||
cssClass: "icon-plus",
|
||||
text: "Add",
|
||||
options: [
|
||||
{
|
||||
"name": "Box",
|
||||
"cssClass": "icon-box",
|
||||
"key": "fixed.box"
|
||||
},
|
||||
{
|
||||
"name": "Line",
|
||||
"cssClass": "icon-line-horz",
|
||||
"key": "fixed.line"
|
||||
},
|
||||
{
|
||||
"name": "Text",
|
||||
"cssClass": "icon-T",
|
||||
"key": "fixed.text"
|
||||
},
|
||||
{
|
||||
"name": "Image",
|
||||
"cssClass": "icon-image",
|
||||
"key": "fixed.image"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
control: "menu-button",
|
||||
domainObject: domainObject,
|
||||
method: function (value) {
|
||||
selection[0].context.fixedController.order(
|
||||
selection[0].context.elementProxy,
|
||||
value
|
||||
);
|
||||
},
|
||||
key: "order",
|
||||
cssClass: "icon-layers",
|
||||
title: "Layering",
|
||||
description: "Move the selected object above or below other objects",
|
||||
options: [
|
||||
{
|
||||
"name": "Move to Top",
|
||||
"cssClass": "icon-arrow-double-up",
|
||||
"key": "top"
|
||||
},
|
||||
{
|
||||
"name": "Move Up",
|
||||
"cssClass": "icon-arrow-up",
|
||||
"key": "up"
|
||||
},
|
||||
{
|
||||
"name": "Move Down",
|
||||
"cssClass": "icon-arrow-down",
|
||||
"key": "down"
|
||||
},
|
||||
{
|
||||
"name": "Move to Bottom",
|
||||
"cssClass": "icon-arrow-double-down",
|
||||
"key": "bottom"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
control: "color",
|
||||
domainObject: domainObject,
|
||||
property: path + ".fill",
|
||||
cssClass: "icon-paint-bucket",
|
||||
title: "Fill color",
|
||||
description: "Set fill color",
|
||||
key: 'fill'
|
||||
},
|
||||
{
|
||||
control: "color",
|
||||
domainObject: domainObject,
|
||||
property: path + ".stroke",
|
||||
cssClass: "icon-line-horz",
|
||||
title: "Border color",
|
||||
description: "Set border color",
|
||||
key: 'stroke'
|
||||
},
|
||||
{
|
||||
control: "dialog-button",
|
||||
domainObject: domainObject,
|
||||
property: path + ".url",
|
||||
cssClass: "icon-image",
|
||||
title: "Image Properties",
|
||||
description: "Edit image properties",
|
||||
key: 'url',
|
||||
dialog: {
|
||||
control: "textfield",
|
||||
name: "Image URL",
|
||||
cssClass: "l-input-lg",
|
||||
required: true
|
||||
}
|
||||
},
|
||||
{
|
||||
control: "color",
|
||||
domainObject: domainObject,
|
||||
property: path + ".color",
|
||||
cssClass: "icon-T",
|
||||
title: "Text color",
|
||||
mandatory: true,
|
||||
description: "Set text color",
|
||||
key: 'color'
|
||||
},
|
||||
{
|
||||
control: "select",
|
||||
domainObject: domainObject,
|
||||
property: path + ".size",
|
||||
title: "Text size",
|
||||
description: "Set text size",
|
||||
"options": [9, 10, 11, 12, 13, 14, 15, 16, 20, 24, 30, 36, 48, 72, 96].map(function (size) {
|
||||
return { "name": size + " px", "value": size + "px" };
|
||||
}),
|
||||
key: 'size'
|
||||
},
|
||||
{
|
||||
control: "numberfield",
|
||||
domainObject: domainObject,
|
||||
property: path + ".x",
|
||||
text: "X",
|
||||
name: "X",
|
||||
key: "x",
|
||||
cssClass: "l-input-sm",
|
||||
min: "0"
|
||||
},
|
||||
{
|
||||
control: "numberfield",
|
||||
domainObject: domainObject,
|
||||
property: path + ".y",
|
||||
text: "Y",
|
||||
name: "Y",
|
||||
key: "y",
|
||||
cssClass: "l-input-sm",
|
||||
min: "0"
|
||||
},
|
||||
{
|
||||
control: "numberfield",
|
||||
domainObject: domainObject,
|
||||
property: path + ".x",
|
||||
text: "X1",
|
||||
name: "X1",
|
||||
key: "x1",
|
||||
cssClass: "l-input-sm",
|
||||
min: "0"
|
||||
},
|
||||
{
|
||||
control: "numberfield",
|
||||
domainObject: domainObject,
|
||||
property: path + ".y",
|
||||
text: "Y1",
|
||||
name: "Y1",
|
||||
key: "y1",
|
||||
cssClass: "l-input-sm",
|
||||
min: "0"
|
||||
},
|
||||
{
|
||||
control: "numberfield",
|
||||
domainObject: domainObject,
|
||||
property: path + ".x2",
|
||||
text: "X2",
|
||||
name: "X2",
|
||||
key: "x2",
|
||||
cssClass: "l-input-sm",
|
||||
min: "0"
|
||||
},
|
||||
{
|
||||
control: "numberfield",
|
||||
domainObject: domainObject,
|
||||
property: path + ".y2",
|
||||
text: "Y2",
|
||||
name: "Y2",
|
||||
key: "y2",
|
||||
cssClass: "l-input-sm",
|
||||
min: "0"
|
||||
},
|
||||
{
|
||||
control: "numberfield",
|
||||
domainObject: domainObject,
|
||||
property: path + ".height",
|
||||
text: "H",
|
||||
name: "H",
|
||||
key: "height",
|
||||
cssClass: "l-input-sm",
|
||||
description: "Resize object height",
|
||||
min: "1"
|
||||
},
|
||||
{
|
||||
control: "numberfield",
|
||||
domainObject: domainObject,
|
||||
property: path + ".width",
|
||||
text: "W",
|
||||
name: "W",
|
||||
key: "width",
|
||||
cssClass: "l-input-sm",
|
||||
description: "Resize object width",
|
||||
min: "1"
|
||||
},
|
||||
{
|
||||
control: "checkbox",
|
||||
domainObject: domainObject,
|
||||
property: path + ".useGrid",
|
||||
name: "Snap to Grid",
|
||||
key: "useGrid"
|
||||
},
|
||||
{
|
||||
control: "dialog-button",
|
||||
domainObject: domainObject,
|
||||
property: path + ".text",
|
||||
cssClass: "icon-gear",
|
||||
title: "Text Properties",
|
||||
description: "Edit text properties",
|
||||
key: "text",
|
||||
dialog: {
|
||||
control: "textfield",
|
||||
name: "Text",
|
||||
required: true
|
||||
}
|
||||
},
|
||||
{
|
||||
control: "checkbox",
|
||||
domainObject: domainObject,
|
||||
property: path + ".titled",
|
||||
name: "Show Title",
|
||||
key: "titled"
|
||||
},
|
||||
{
|
||||
control: "button",
|
||||
domainObject: domainObject,
|
||||
method: function () {
|
||||
selection[0].context.fixedController.remove(
|
||||
selection[0].context.elementProxy
|
||||
);
|
||||
},
|
||||
key: "remove",
|
||||
cssClass: "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"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
});
|
||||
475
platform/features/fixed/plugin.js
Normal file
475
platform/features/fixed/plugin.js
Normal 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");
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -225,6 +225,8 @@ define(
|
||||
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));
|
||||
@@ -421,6 +423,7 @@ define(
|
||||
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);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -706,6 +709,12 @@ define(
|
||||
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;
|
||||
}
|
||||
);
|
||||
@@ -42,7 +42,8 @@ define(
|
||||
},
|
||||
"fixed.text": {
|
||||
fill: "transparent",
|
||||
stroke: "transparent"
|
||||
stroke: "transparent",
|
||||
size: "13px"
|
||||
}
|
||||
},
|
||||
DIALOGS = {
|
||||
@@ -67,10 +67,6 @@ define(
|
||||
*/
|
||||
proxy.size = new AccessorMutator(element, 'size');
|
||||
|
||||
if (proxy.size() === undefined) {
|
||||
proxy.size("13px");
|
||||
}
|
||||
|
||||
return proxy;
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
@@ -23,18 +23,18 @@
|
||||
ng-controller="FixedController as controller">
|
||||
|
||||
<!-- Background grid -->
|
||||
<div class="l-grid-holder" ng-click="controller.bypassSelection($event)">
|
||||
<div class="l-grid l-grid-x"
|
||||
<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="l-grid l-grid-y"
|
||||
<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 s-hover-border"
|
||||
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)">
|
||||
@@ -44,15 +44,15 @@
|
||||
</mct-include>
|
||||
</div>
|
||||
<!-- Selection highlight, handles -->
|
||||
<span class="s-selected s-moveable" ng-if="controller.isElementSelected()">
|
||||
<div class="l-fixed-position-item t-edit-handle-holder"
|
||||
<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="l-fixed-position-item-handle edit-corner"
|
||||
class="c-frame-edit__handle c-frame-edit__handle--nwse"
|
||||
ng-style="handle.style()"
|
||||
mct-drag-down="handle.startDrag()"
|
||||
mct-drag="handle.continueDrag(delta)"
|
||||
@@ -19,10 +19,10 @@
|
||||
this source code distribution or the Licensing information page available
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
<a class="l-hyperlink s-hyperlink" ng-controller="HyperlinkController as hyperlink" href="{{domainObject.getModel().url}}"
|
||||
<a class="c-hyperlink u-links" ng-controller="HyperlinkController as hyperlink" href="{{domainObject.getModel().url}}"
|
||||
ng-attr-target="{{hyperlink.openNewTab() ? '_blank' : undefined}}"
|
||||
ng-class="{
|
||||
's-button': hyperlink.isButton()
|
||||
}">
|
||||
<span class="label">{{domainObject.getModel().displayText}}</span>
|
||||
'c-hyperlink--button u-fills-container' : hyperlink.isButton(),
|
||||
'c-hyperlink--link' : !hyperlink.isButton() }">
|
||||
<span class="c-hyperlink__label">{{domainObject.getModel().displayText}}</span>
|
||||
</a>
|
||||
|
||||
@@ -1,356 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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/LayoutController",
|
||||
"./src/FixedController",
|
||||
"./src/LayoutCompositionPolicy",
|
||||
'./src/MCTTriggerModal',
|
||||
"./res/templates/layout.html",
|
||||
"./res/templates/fixed.html",
|
||||
"./res/templates/frame.html",
|
||||
"./res/templates/elements/telemetry.html",
|
||||
"./res/templates/elements/box.html",
|
||||
"./res/templates/elements/line.html",
|
||||
"./res/templates/elements/text.html",
|
||||
"./res/templates/elements/image.html",
|
||||
'legacyRegistry'
|
||||
], function (
|
||||
LayoutController,
|
||||
FixedController,
|
||||
LayoutCompositionPolicy,
|
||||
MCTTriggerModal,
|
||||
layoutTemplate,
|
||||
fixedTemplate,
|
||||
frameTemplate,
|
||||
telemetryTemplate,
|
||||
boxTemplate,
|
||||
lineTemplate,
|
||||
textTemplate,
|
||||
imageTemplate,
|
||||
legacyRegistry
|
||||
) {
|
||||
|
||||
legacyRegistry.register("platform/features/layout", {
|
||||
"name": "Layout components.",
|
||||
"description": "Plug in adding Layout capabilities.",
|
||||
"extensions": {
|
||||
"views": [
|
||||
{
|
||||
"key": "layout",
|
||||
"name": "Display Layout",
|
||||
"cssClass": "icon-layout",
|
||||
"type": "layout",
|
||||
"template": layoutTemplate,
|
||||
"editable": true,
|
||||
"uses": []
|
||||
},
|
||||
{
|
||||
"key": "fixed",
|
||||
"name": "Fixed Position",
|
||||
"cssClass": "icon-box-with-dashed-lines",
|
||||
"type": "telemetry.panel",
|
||||
"template": fixedTemplate,
|
||||
"uses": [
|
||||
"composition"
|
||||
],
|
||||
"toolbar": {
|
||||
"sections": [
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"method": "add",
|
||||
"cssClass": "icon-plus",
|
||||
"control": "menu-button",
|
||||
"text": "Add",
|
||||
"title": "Add",
|
||||
"description": "Add new items",
|
||||
"options": [
|
||||
{
|
||||
"name": "Box",
|
||||
"cssClass": "icon-box",
|
||||
"key": "fixed.box"
|
||||
},
|
||||
{
|
||||
"name": "Line",
|
||||
"cssClass": "icon-line-horz",
|
||||
"key": "fixed.line"
|
||||
},
|
||||
{
|
||||
"name": "Text",
|
||||
"cssClass": "icon-T",
|
||||
"key": "fixed.text"
|
||||
},
|
||||
{
|
||||
"name": "Image",
|
||||
"cssClass": "icon-image",
|
||||
"key": "fixed.image"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"method": "order",
|
||||
"cssClass": "icon-layers",
|
||||
"control": "menu-button",
|
||||
"title": "Layering",
|
||||
"description": "Move the selected object above or below other objects",
|
||||
"options": [
|
||||
{
|
||||
"name": "Move to Top",
|
||||
"cssClass": "icon-arrow-double-up",
|
||||
"key": "top"
|
||||
},
|
||||
{
|
||||
"name": "Move Up",
|
||||
"cssClass": "icon-arrow-up",
|
||||
"key": "up"
|
||||
},
|
||||
{
|
||||
"name": "Move Down",
|
||||
"cssClass": "icon-arrow-down",
|
||||
"key": "down"
|
||||
},
|
||||
{
|
||||
"name": "Move to Bottom",
|
||||
"cssClass": "icon-arrow-double-down",
|
||||
"key": "bottom"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"property": "fill",
|
||||
"cssClass": "icon-paint-bucket",
|
||||
"title": "Fill color",
|
||||
"description": "Set fill color",
|
||||
"control": "color"
|
||||
},
|
||||
{
|
||||
"property": "stroke",
|
||||
"cssClass": "icon-line-horz",
|
||||
"title": "Border color",
|
||||
"description": "Set border color",
|
||||
"control": "color"
|
||||
},
|
||||
{
|
||||
"property": "color",
|
||||
"cssClass": "icon-T",
|
||||
"title": "Text color",
|
||||
"description": "Set text color",
|
||||
"mandatory": true,
|
||||
"control": "color"
|
||||
},
|
||||
{
|
||||
"property": "url",
|
||||
"cssClass": "icon-image",
|
||||
"control": "dialog-button",
|
||||
"title": "Image Properties",
|
||||
"description": "Edit image properties",
|
||||
"dialog": {
|
||||
"control": "textfield",
|
||||
"name": "Image URL",
|
||||
"cssClass": "l-input-lg",
|
||||
"required": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"property": "text",
|
||||
"cssClass": "icon-gear",
|
||||
"control": "dialog-button",
|
||||
"title": "Text Properties",
|
||||
"description": "Edit text properties",
|
||||
"dialog": {
|
||||
"control": "textfield",
|
||||
"name": "Text",
|
||||
"required": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "showTitle",
|
||||
"cssClass": "icon-two-parts-both",
|
||||
"control": "button",
|
||||
"title": "Show title",
|
||||
"description": "Show telemetry element title"
|
||||
},
|
||||
{
|
||||
"method": "hideTitle",
|
||||
"cssClass": "icon-two-parts-one-only",
|
||||
"control": "button",
|
||||
"title": "Hide title",
|
||||
"description": "Hide telemetry element title"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"method": "remove",
|
||||
"control": "button",
|
||||
"cssClass": "icon-trash",
|
||||
"title": "Delete",
|
||||
"description": "Delete the selected item"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"representations": [
|
||||
{
|
||||
"key": "frame",
|
||||
"template": frameTemplate
|
||||
}
|
||||
],
|
||||
"directives": [
|
||||
{
|
||||
"key": "mctTriggerModal",
|
||||
"implementation": MCTTriggerModal,
|
||||
"depends": [
|
||||
"$document"
|
||||
]
|
||||
}
|
||||
],
|
||||
"controllers": [
|
||||
{
|
||||
"key": "LayoutController",
|
||||
"implementation": LayoutController,
|
||||
"depends": [
|
||||
"$scope",
|
||||
"$element",
|
||||
"openmct"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "FixedController",
|
||||
"implementation": FixedController,
|
||||
"depends": [
|
||||
"$scope",
|
||||
"$q",
|
||||
"dialogService",
|
||||
"openmct",
|
||||
"$element"
|
||||
]
|
||||
}
|
||||
],
|
||||
"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
|
||||
}
|
||||
],
|
||||
"policies": [
|
||||
{
|
||||
"category": "composition",
|
||||
"implementation": LayoutCompositionPolicy
|
||||
}
|
||||
],
|
||||
"toolbars": [
|
||||
{
|
||||
name: "Display Layout Toolbar",
|
||||
key: "layout",
|
||||
description: "A toolbar for objects inside a display layout.",
|
||||
forSelection: function (selection) {
|
||||
// Apply the layout toolbar if the selected object is inside a layout.
|
||||
return (selection && selection[1] && selection[1].context.item.type === 'layout');
|
||||
},
|
||||
toolbar: function (selection) {
|
||||
return [
|
||||
{
|
||||
control: "checkbox",
|
||||
name: "Show frame",
|
||||
domainObject: selection[1].context.item,
|
||||
property: "configuration.layout.panels[" + selection[0].context.oldItem.id + "].hasFrame"
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
],
|
||||
"types": [
|
||||
{
|
||||
"key": "layout",
|
||||
"name": "Display Layout",
|
||||
"cssClass": "icon-layout",
|
||||
"description": "Assemble other objects and components together into a reusable screen layout. Working in a simple canvas workspace, simply drag in the objects you want, position and size them. Save your design and view or edit it at any time.",
|
||||
"priority": 900,
|
||||
"features": "creation",
|
||||
"model": {
|
||||
"composition": [],
|
||||
configuration: {
|
||||
layout: {
|
||||
panels: {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"name": "Layout Grid",
|
||||
"control": "composite",
|
||||
"pattern": "^(\\d*[1-9]\\d*)?$",
|
||||
"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"
|
||||
}
|
||||
],
|
||||
"key": "layoutGrid",
|
||||
"conversion": "number[]"
|
||||
},
|
||||
{
|
||||
"name": "Advanced",
|
||||
"control": "textfield",
|
||||
"key": "layoutAdvancedCss",
|
||||
"cssClass": "l-input-lg"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,83 +0,0 @@
|
||||
<!--
|
||||
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="abs l-layout {{ domainObject.getModel().layoutAdvancedCss }}"
|
||||
ng-controller="LayoutController as controller"
|
||||
ng-click="controller.bypassSelection($event)">
|
||||
|
||||
<!-- Background grid -->
|
||||
<div class="l-grid-holder"
|
||||
ng-show="!controller.drilledIn"
|
||||
ng-click="controller.bypassSelection($event)">
|
||||
<div class="l-grid l-grid-x"
|
||||
ng-if="!controller.getGridSize()[0] < 3"
|
||||
ng-style="{ 'background-size': controller.getGridSize() [0] + 'px 100%' }"></div>
|
||||
<div class="l-grid l-grid-y"
|
||||
ng-if="!controller.getGridSize()[1] < 3"
|
||||
ng-style="{ 'background-size': '100% ' + controller.getGridSize() [1] + 'px' }"></div>
|
||||
</div>
|
||||
|
||||
<div class="frame t-frame-outer child-frame panel s-selectable s-moveable s-hover-border t-object-type-{{ childObject.getModel().type }}"
|
||||
data-layout-id="{{childObject.getId() + '-' + $id}}"
|
||||
ng-class="{ 'no-frame': !controller.hasFrame(childObject), 's-drilled-in': controller.isDrilledIn(childObject) }"
|
||||
ng-repeat="childObject in composition"
|
||||
ng-init="controller.selectIfNew(childObject.getId() + '-' + $id, childObject)"
|
||||
mct-selectable="controller.getContext(childObject)"
|
||||
ng-dblclick="controller.drill($event, childObject)"
|
||||
ng-style="controller.getFrameStyle(childObject.getId())">
|
||||
|
||||
<mct-representation key="'frame'"
|
||||
class="t-rep-frame holder contents abs"
|
||||
mct-object="childObject">
|
||||
</mct-representation>
|
||||
<!-- Drag handles -->
|
||||
<span class="abs t-edit-handle-holder" ng-if="controller.selected(childObject) && !controller.isDrilledIn(childObject)">
|
||||
<span class="edit-handle edit-move"
|
||||
mct-drag-down="controller.startDrag(childObject.getId(), [1,1], [0,0])"
|
||||
mct-drag="controller.continueDrag(delta)"
|
||||
mct-drag-up="controller.endDrag()">
|
||||
</span>
|
||||
|
||||
<span class="edit-corner edit-resize-nw"
|
||||
mct-drag-down="controller.startDrag(childObject.getId(), [1,1], [-1,-1])"
|
||||
mct-drag="controller.continueDrag(delta)"
|
||||
mct-drag-up="controller.endDrag()">
|
||||
</span>
|
||||
<span class="edit-corner edit-resize-ne"
|
||||
mct-drag-down="controller.startDrag(childObject.getId(), [0,1], [1,-1])"
|
||||
mct-drag="controller.continueDrag(delta)"
|
||||
mct-drag-up="controller.endDrag()">
|
||||
</span>
|
||||
<span class="edit-corner edit-resize-sw"
|
||||
mct-drag-down="controller.startDrag(childObject.getId(), [1,0], [-1,1])"
|
||||
mct-drag="controller.continueDrag(delta)"
|
||||
mct-drag-up="controller.endDrag()">
|
||||
</span>
|
||||
<span class="edit-corner edit-resize-se"
|
||||
mct-drag-down="controller.startDrag(childObject.getId(), [0,0], [1,1])"
|
||||
mct-drag="controller.continueDrag(delta)"
|
||||
mct-drag-up="controller.endDrag()">
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -1,524 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* This bundle implements object types and associated views for
|
||||
* display-building.
|
||||
* @namespace platform/features/layout
|
||||
*/
|
||||
define(
|
||||
[
|
||||
'zepto',
|
||||
'./LayoutDrag'
|
||||
],
|
||||
function (
|
||||
$,
|
||||
LayoutDrag
|
||||
) {
|
||||
|
||||
var DEFAULT_DIMENSIONS = [12, 8],
|
||||
DEFAULT_GRID_SIZE = [32, 32],
|
||||
MINIMUM_FRAME_SIZE = [320, 180];
|
||||
|
||||
var DEFAULT_HIDDEN_FRAME_TYPES = [
|
||||
'hyperlink'
|
||||
];
|
||||
|
||||
/**
|
||||
* The LayoutController is responsible for supporting the
|
||||
* Layout 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 LayoutController($scope, $element, openmct) {
|
||||
var self = this,
|
||||
callbackCount = 0;
|
||||
|
||||
this.$element = $element;
|
||||
|
||||
// Update grid size when it changed
|
||||
function updateGridSize(layoutGrid) {
|
||||
var oldSize = self.gridSize;
|
||||
|
||||
self.gridSize = layoutGrid || DEFAULT_GRID_SIZE;
|
||||
|
||||
// Only update panel positions if this actually changed things
|
||||
if (self.gridSize[0] !== oldSize[0] ||
|
||||
self.gridSize[1] !== oldSize[1]) {
|
||||
self.layoutPanels(Object.keys(self.positions));
|
||||
}
|
||||
}
|
||||
|
||||
// Position a panel after a drop event
|
||||
function handleDrop(e, id, position) {
|
||||
if (e.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.configuration = $scope.configuration || {};
|
||||
$scope.configuration.panels = $scope.configuration.panels || {};
|
||||
|
||||
self.openmct.objects.get(id).then(function (object) {
|
||||
$scope.configuration.panels[id] = {
|
||||
position: [
|
||||
Math.floor(position.x / self.gridSize[0]),
|
||||
Math.floor(position.y / self.gridSize[1])
|
||||
],
|
||||
dimensions: self.defaultDimensions(),
|
||||
hasFrame: self.getDefaultFrame(object.type)
|
||||
};
|
||||
|
||||
// Store the id so that the newly-dropped object
|
||||
// gets selected during refresh composition
|
||||
self.droppedIdToSelectAfterRefresh = id;
|
||||
|
||||
self.commit();
|
||||
|
||||
// Populate template-facing position for this id
|
||||
self.rawPositions[id] = $scope.configuration.panels[id];
|
||||
self.populatePosition(id);
|
||||
refreshComposition();
|
||||
});
|
||||
|
||||
// Layout may contain embedded views which will
|
||||
// listen for drops, so call preventDefault() so
|
||||
// that they can recognize that this event is handled.
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
//Will fetch fully contextualized composed objects, and populate
|
||||
// scope with them.
|
||||
function refreshComposition() {
|
||||
//Keep a track of how many composition callbacks have been made
|
||||
var thisCount = ++callbackCount;
|
||||
|
||||
$scope.domainObject.useCapability('composition').then(function (composition) {
|
||||
var ids;
|
||||
|
||||
//Is this callback for the most recent composition
|
||||
// request? If not, discard it. Prevents race condition
|
||||
if (thisCount === callbackCount) {
|
||||
ids = composition.map(function (object) {
|
||||
return object.getId();
|
||||
}) || [];
|
||||
|
||||
$scope.composition = composition;
|
||||
self.layoutPanels(ids);
|
||||
self.setFrames(ids);
|
||||
|
||||
if (self.selectedId &&
|
||||
self.selectedId !== $scope.domainObject.getId() &&
|
||||
composition.indexOf(self.selectedId) === -1) {
|
||||
// Click triggers selection of layout parent.
|
||||
self.$element[0].click();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// End drag; we don't want to put $scope into this
|
||||
// because it triggers "cpws" (copy window or scope)
|
||||
// errors in Angular.
|
||||
this.endDragInScope = function () {
|
||||
// Write to configuration; this is watched and
|
||||
// saved by the EditRepresenter.
|
||||
$scope.configuration =
|
||||
$scope.configuration || {};
|
||||
|
||||
$scope.configuration.panels =
|
||||
$scope.configuration.panels || {};
|
||||
|
||||
$scope.configuration.panels[self.activeDragId] =
|
||||
$scope.configuration.panels[self.activeDragId] || {};
|
||||
|
||||
$scope.configuration.panels[self.activeDragId].position =
|
||||
self.rawPositions[self.activeDragId].position;
|
||||
$scope.configuration.panels[self.activeDragId].dimensions =
|
||||
self.rawPositions[self.activeDragId].dimensions;
|
||||
|
||||
self.commit();
|
||||
};
|
||||
|
||||
// Sets the selectable object in response to the selection change event.
|
||||
function setSelection(selectable) {
|
||||
var selection = selectable[0];
|
||||
|
||||
if (!selection) {
|
||||
delete self.selectedId;
|
||||
return;
|
||||
}
|
||||
|
||||
self.selectedId = selection.context.oldItem.getId();
|
||||
self.drilledIn = undefined;
|
||||
self.selectable = selectable;
|
||||
}
|
||||
|
||||
this.positions = {};
|
||||
this.rawPositions = {};
|
||||
this.gridSize = DEFAULT_GRID_SIZE;
|
||||
this.$scope = $scope;
|
||||
this.drilledIn = undefined;
|
||||
this.openmct = openmct;
|
||||
|
||||
// Watch for changes to the grid size in the model
|
||||
$scope.$watch("model.layoutGrid", updateGridSize);
|
||||
|
||||
// Update composed objects on screen, and position panes
|
||||
$scope.$watchCollection("model.composition", refreshComposition);
|
||||
|
||||
openmct.selection.on('change', setSelection);
|
||||
|
||||
$scope.$on("$destroy", function () {
|
||||
openmct.selection.off("change", setSelection);
|
||||
self.unlisten();
|
||||
});
|
||||
|
||||
$scope.$on("mctDrop", handleDrop);
|
||||
|
||||
self.unlisten = self.$scope.domainObject.getCapability('mutation').listen(function (model) {
|
||||
$scope.configuration = model.configuration.layout;
|
||||
$scope.model = model;
|
||||
var panels = $scope.configuration.panels;
|
||||
|
||||
Object.keys(panels).forEach(function (key) {
|
||||
if (self.frames && self.frames.hasOwnProperty(key)) {
|
||||
self.frames[key] = panels[key].hasFrame;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Utility function to copy raw positions from configuration,
|
||||
// without writing directly to configuration (to avoid triggering
|
||||
// persistence from watchers during drags).
|
||||
function shallowCopy(obj, keys) {
|
||||
var copy = {};
|
||||
keys.forEach(function (k) {
|
||||
copy[k] = obj[k];
|
||||
});
|
||||
return copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the frames value. If a configuration panel has "hasFrame' property,
|
||||
* use that value, otherwise set a default value. A 'hyperlink' object should
|
||||
* have no frame by default.
|
||||
*
|
||||
* @param {string[]} ids the object ids
|
||||
* @private
|
||||
*/
|
||||
LayoutController.prototype.setFrames = function (ids) {
|
||||
var panels = shallowCopy(this.$scope.configuration.panels || {}, ids);
|
||||
this.frames = {};
|
||||
|
||||
this.$scope.composition.forEach(function (object) {
|
||||
var id = object.getId();
|
||||
panels[id] = panels[id] || {};
|
||||
|
||||
if (panels[id].hasOwnProperty('hasFrame')) {
|
||||
this.frames[id] = panels[id].hasFrame;
|
||||
} else {
|
||||
this.frames[id] = this.getDefaultFrame(object.getModel().type);
|
||||
}
|
||||
}, this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the default value for frame.
|
||||
*
|
||||
* @param type the domain object type
|
||||
* @return {boolean} true if the object should have
|
||||
* frame by default, false, otherwise
|
||||
*/
|
||||
LayoutController.prototype.getDefaultFrame = function (type) {
|
||||
return DEFAULT_HIDDEN_FRAME_TYPES.indexOf(type) === -1;
|
||||
};
|
||||
|
||||
// Convert from { positions: ..., dimensions: ... } to an
|
||||
// appropriate ng-style argument, to position frames.
|
||||
LayoutController.prototype.convertPosition = function (raw) {
|
||||
var gridSize = this.gridSize;
|
||||
// Multiply position/dimensions by grid size
|
||||
return {
|
||||
left: (gridSize[0] * raw.position[0]) + 'px',
|
||||
top: (gridSize[1] * raw.position[1]) + 'px',
|
||||
width: (gridSize[0] * raw.dimensions[0]) + 'px',
|
||||
height: (gridSize[1] * raw.dimensions[1]) + 'px',
|
||||
minWidth: (gridSize[0] * raw.dimensions[0]) + 'px',
|
||||
minHeight: (gridSize[1] * raw.dimensions[1]) + 'px'
|
||||
};
|
||||
};
|
||||
|
||||
// Generate default positions for a new panel
|
||||
LayoutController.prototype.defaultDimensions = function () {
|
||||
var gridSize = this.gridSize;
|
||||
return MINIMUM_FRAME_SIZE.map(function (min, i) {
|
||||
return Math.max(
|
||||
Math.ceil(min / gridSize[i]),
|
||||
DEFAULT_DIMENSIONS[i]
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
// Generate a default position (in its raw format) for a frame.
|
||||
// Use an index to ensure that default positions are unique.
|
||||
LayoutController.prototype.defaultPosition = function (index) {
|
||||
return {
|
||||
position: [index, index],
|
||||
dimensions: this.defaultDimensions()
|
||||
};
|
||||
};
|
||||
|
||||
// Store a computed position for a contained frame by its
|
||||
// domain object id. Called in a forEach loop, so arguments
|
||||
// are as expected there.
|
||||
LayoutController.prototype.populatePosition = function (id, index) {
|
||||
this.rawPositions[id] =
|
||||
this.rawPositions[id] || this.defaultPosition(index || 0);
|
||||
this.positions[id] =
|
||||
this.convertPosition(this.rawPositions[id]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a style object for a frame with the specified domain
|
||||
* object identifier, suitable for use in an `ng-style`
|
||||
* directive to position a frame as configured for this layout.
|
||||
* @param {string} id the object identifier
|
||||
* @returns {Object.<string, string>} an object with
|
||||
* appropriate left, width, etc fields for positioning
|
||||
*/
|
||||
LayoutController.prototype.getFrameStyle = function (id) {
|
||||
// Called in a loop, so just look up; the "positions"
|
||||
// object is kept up to date by a watch.
|
||||
return this.positions[id];
|
||||
};
|
||||
|
||||
/**
|
||||
* Start a drag gesture to move/resize a frame.
|
||||
*
|
||||
* The provided position and dimensions factors will determine
|
||||
* whether this is a move or a resize, and what type 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 {string} id the identifier of the domain object
|
||||
* in the frame being manipulated
|
||||
* @param {number[]} posFactor the position factor
|
||||
* @param {number[]} dimFactor the dimensions factor
|
||||
*/
|
||||
LayoutController.prototype.startDrag = function (id, posFactor, dimFactor) {
|
||||
this.activeDragId = id;
|
||||
this.activeDrag = new LayoutDrag(
|
||||
this.rawPositions[id],
|
||||
posFactor,
|
||||
dimFactor,
|
||||
this.gridSize
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Continue an active drag gesture.
|
||||
* @param {number[]} delta the offset, in pixels,
|
||||
* of the current pointer position, relative
|
||||
* to its position when the drag started
|
||||
*/
|
||||
LayoutController.prototype.continueDrag = function (delta) {
|
||||
if (this.activeDrag) {
|
||||
this.rawPositions[this.activeDragId] =
|
||||
this.activeDrag.getAdjustedPosition(delta);
|
||||
this.populatePosition(this.activeDragId);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Compute panel positions based on the layout's object model.
|
||||
* Defined as member function to facilitate testing.
|
||||
* @private
|
||||
*/
|
||||
LayoutController.prototype.layoutPanels = function (ids) {
|
||||
var configuration = this.$scope.configuration || {},
|
||||
self = this;
|
||||
|
||||
// Pull panel positions from configuration
|
||||
this.rawPositions =
|
||||
shallowCopy(configuration.panels || {}, ids);
|
||||
|
||||
// Clear prior computed positions
|
||||
this.positions = {};
|
||||
|
||||
// Update width/height that we are tracking
|
||||
this.gridSize =
|
||||
(this.$scope.model || {}).layoutGrid || DEFAULT_GRID_SIZE;
|
||||
|
||||
// Compute positions and add defaults where needed
|
||||
ids.forEach(function (id, index) {
|
||||
self.populatePosition(id, index);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* End the active drag gesture. This will update the
|
||||
* view configuration.
|
||||
*/
|
||||
LayoutController.prototype.endDrag = function () {
|
||||
this.dragInProgress = true;
|
||||
|
||||
setTimeout(function () {
|
||||
this.dragInProgress = false;
|
||||
}.bind(this), 0);
|
||||
|
||||
this.endDragInScope();
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if the object is currently selected.
|
||||
*
|
||||
* @param {string} obj the object to check for selection
|
||||
* @returns {boolean} true if selected, otherwise false
|
||||
*/
|
||||
LayoutController.prototype.selected = function (obj) {
|
||||
var sobj = this.openmct.selection.get()[0];
|
||||
return (sobj && sobj.context.oldItem.getId() === obj.getId()) ? true : false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Bypasses selection if drag is in progress.
|
||||
*
|
||||
* @param event the angular event object
|
||||
*/
|
||||
LayoutController.prototype.bypassSelection = function (event) {
|
||||
if (this.dragInProgress) {
|
||||
if (event) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if the domain object is drilled in.
|
||||
*
|
||||
* @param domainObject the domain object
|
||||
* @return true if the object is drilled in, false otherwise
|
||||
*/
|
||||
LayoutController.prototype.isDrilledIn = function (domainObject) {
|
||||
return this.drilledIn === domainObject.getId();
|
||||
};
|
||||
|
||||
/**
|
||||
* Puts the given object in the drilled-in mode.
|
||||
*
|
||||
* @param event the angular event object
|
||||
* @param domainObject the domain object
|
||||
*/
|
||||
LayoutController.prototype.drill = function (event, domainObject) {
|
||||
if (event) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
if (!domainObject.getCapability('editor').inEditContext()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!domainObject.hasCapability('composition')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable since fixed position doesn't use the selection API yet
|
||||
if (domainObject.getModel().type === 'telemetry.fixed') {
|
||||
return;
|
||||
}
|
||||
|
||||
this.drilledIn = domainObject.getId();
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the object has frame.
|
||||
*
|
||||
* @param {object} obj the object
|
||||
* @return {boolean} true if object has frame, otherwise false
|
||||
*/
|
||||
LayoutController.prototype.hasFrame = function (obj) {
|
||||
return this.frames[obj.getId()];
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the size of the grid, in pixels. The returned array
|
||||
* is in the form `[x, y]`.
|
||||
* @returns {number[]} the grid size
|
||||
*/
|
||||
LayoutController.prototype.getGridSize = function () {
|
||||
return this.gridSize;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the selection context.
|
||||
*
|
||||
* @param domainObject the domain object
|
||||
* @returns {object} the context object which includes item and oldItem
|
||||
*/
|
||||
LayoutController.prototype.getContext = function (domainObject) {
|
||||
return {
|
||||
item: domainObject.useCapability('adapter'),
|
||||
oldItem: domainObject
|
||||
};
|
||||
};
|
||||
|
||||
LayoutController.prototype.commit = function () {
|
||||
var model = this.$scope.model;
|
||||
model.configuration = model.configuration || {};
|
||||
model.configuration.layout = this.$scope.configuration;
|
||||
|
||||
this.$scope.domainObject.useCapability('mutation', function () {
|
||||
return model;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Selects a newly-dropped object.
|
||||
*
|
||||
* @param classSelector the css class selector
|
||||
* @param domainObject the domain object
|
||||
*/
|
||||
LayoutController.prototype.selectIfNew = function (selector, domainObject) {
|
||||
if (domainObject.getId() === this.droppedIdToSelectAfterRefresh) {
|
||||
setTimeout(function () {
|
||||
$('[data-layout-id="' + selector + '"]')[0].click();
|
||||
delete this.droppedIdToSelectAfterRefresh;
|
||||
}.bind(this), 0);
|
||||
}
|
||||
};
|
||||
|
||||
return LayoutController;
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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/LayoutCompositionPolicy"],
|
||||
function (LayoutCompositionPolicy) {
|
||||
describe("Layout's composition policy", function () {
|
||||
var mockChild,
|
||||
mockCandidateObj,
|
||||
mockCandidate,
|
||||
mockContext,
|
||||
candidateType,
|
||||
contextType,
|
||||
policy;
|
||||
|
||||
beforeEach(function () {
|
||||
mockChild = jasmine.createSpyObj(
|
||||
'childObject',
|
||||
['getCapability']
|
||||
);
|
||||
mockCandidate =
|
||||
jasmine.createSpyObj('candidateType', ['instanceOf']);
|
||||
mockContext =
|
||||
jasmine.createSpyObj('contextType', ['instanceOf']);
|
||||
|
||||
mockCandidateObj = jasmine.createSpyObj('domainObj', [
|
||||
'getCapability'
|
||||
]);
|
||||
mockCandidateObj.getCapability.and.returnValue(mockCandidate);
|
||||
|
||||
mockChild.getCapability.and.returnValue(mockContext);
|
||||
|
||||
mockCandidate.instanceOf.and.callFake(function (t) {
|
||||
return t === candidateType;
|
||||
});
|
||||
mockContext.instanceOf.and.callFake(function (t) {
|
||||
return t === contextType;
|
||||
});
|
||||
|
||||
policy = new LayoutCompositionPolicy();
|
||||
});
|
||||
|
||||
it("disallows folders in layouts", function () {
|
||||
candidateType = 'layout';
|
||||
contextType = 'folder';
|
||||
expect(policy.allow(mockCandidateObj, mockChild)).toBe(false);
|
||||
});
|
||||
|
||||
it("does not disallow folders elsewhere", function () {
|
||||
candidateType = 'nonlayout';
|
||||
contextType = 'folder';
|
||||
expect(policy.allow(mockCandidateObj, mockChild)).toBe(true);
|
||||
});
|
||||
|
||||
it("allows things other than folders in layouts", function () {
|
||||
candidateType = 'layout';
|
||||
contextType = 'nonfolder';
|
||||
expect(policy.allow(mockCandidateObj, mockChild)).toBe(true);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -1,479 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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/LayoutController",
|
||||
"zepto"
|
||||
],
|
||||
function (
|
||||
LayoutController,
|
||||
$
|
||||
) {
|
||||
|
||||
describe("The Layout controller", function () {
|
||||
var mockScope,
|
||||
mockEvent,
|
||||
testModel,
|
||||
testConfiguration,
|
||||
controller,
|
||||
mockCompositionCapability,
|
||||
mockComposition,
|
||||
mockCompositionObjects,
|
||||
mockOpenMCT,
|
||||
mockSelection,
|
||||
mockDomainObjectCapability,
|
||||
mockObjects,
|
||||
unlistenFunc,
|
||||
$element = [],
|
||||
selectable = [];
|
||||
|
||||
function mockPromise(value) {
|
||||
return {
|
||||
then: function (thenFunc) {
|
||||
return mockPromise(thenFunc(value));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function mockDomainObject(id) {
|
||||
return {
|
||||
getId: function () {
|
||||
return id;
|
||||
},
|
||||
useCapability: function () {
|
||||
return mockCompositionCapability;
|
||||
},
|
||||
getModel: function () {
|
||||
if (id === 'b') {
|
||||
return {
|
||||
type : 'hyperlink'
|
||||
};
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
getCapability: function () {
|
||||
return mockDomainObjectCapability;
|
||||
},
|
||||
hasCapability: function (param) {
|
||||
if (param === 'composition') {
|
||||
return id !== 'b';
|
||||
}
|
||||
},
|
||||
type: "testType"
|
||||
};
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
mockScope = jasmine.createSpyObj(
|
||||
"$scope",
|
||||
["$watch", "$watchCollection", "$on"]
|
||||
);
|
||||
mockEvent = jasmine.createSpyObj(
|
||||
'event',
|
||||
['preventDefault', 'stopPropagation']
|
||||
);
|
||||
|
||||
testModel = {};
|
||||
|
||||
mockComposition = ["a", "b", "c"];
|
||||
mockCompositionObjects = mockComposition.map(mockDomainObject);
|
||||
|
||||
testConfiguration = {
|
||||
panels: {
|
||||
a: {
|
||||
position: [20, 10],
|
||||
dimensions: [5, 5]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
unlistenFunc = jasmine.createSpy("unlisten");
|
||||
mockDomainObjectCapability = jasmine.createSpyObj('capability',
|
||||
['inEditContext', 'listen']
|
||||
);
|
||||
mockDomainObjectCapability.listen.and.returnValue(unlistenFunc);
|
||||
|
||||
mockCompositionCapability = mockPromise(mockCompositionObjects);
|
||||
|
||||
mockScope.domainObject = mockDomainObject("mockDomainObject");
|
||||
mockScope.model = testModel;
|
||||
mockScope.configuration = testConfiguration;
|
||||
|
||||
selectable[0] = {
|
||||
context: {
|
||||
oldItem: mockScope.domainObject
|
||||
}
|
||||
};
|
||||
|
||||
mockSelection = jasmine.createSpyObj("selection", [
|
||||
'select',
|
||||
'on',
|
||||
'off',
|
||||
'get'
|
||||
]);
|
||||
mockSelection.get.and.returnValue(selectable);
|
||||
|
||||
mockObjects = jasmine.createSpyObj('objects', [
|
||||
'get'
|
||||
]);
|
||||
mockObjects.get.and.returnValue(mockPromise(mockDomainObject("mockObject")));
|
||||
mockOpenMCT = {
|
||||
selection: mockSelection,
|
||||
objects: mockObjects
|
||||
};
|
||||
|
||||
$element = $('<div></div>');
|
||||
$(document).find('body').append($element);
|
||||
spyOn($element[0], 'click');
|
||||
|
||||
spyOn(mockScope.domainObject, "useCapability").and.callThrough();
|
||||
|
||||
controller = new LayoutController(mockScope, $element, mockOpenMCT);
|
||||
spyOn(controller, "layoutPanels").and.callThrough();
|
||||
spyOn(controller, "commit");
|
||||
|
||||
jasmine.clock().install();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
$element.remove();
|
||||
jasmine.clock().uninstall();
|
||||
});
|
||||
|
||||
|
||||
it("listens for selection change events", function () {
|
||||
expect(mockOpenMCT.selection.on).toHaveBeenCalledWith(
|
||||
'change',
|
||||
jasmine.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it("cleans up on scope destroy", function () {
|
||||
expect(mockScope.$on).toHaveBeenCalledWith(
|
||||
'$destroy',
|
||||
jasmine.any(Function)
|
||||
);
|
||||
|
||||
mockScope.$on.calls.all()[0].args[1]();
|
||||
|
||||
expect(mockOpenMCT.selection.off).toHaveBeenCalledWith(
|
||||
'change',
|
||||
jasmine.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
// Model changes will indicate that panel positions
|
||||
// may have changed, for instance.
|
||||
it("watches for changes to composition", function () {
|
||||
expect(mockScope.$watchCollection).toHaveBeenCalledWith(
|
||||
"model.composition",
|
||||
jasmine.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it("Retrieves updated composition from composition capability", function () {
|
||||
mockScope.$watchCollection.calls.mostRecent().args[1]();
|
||||
expect(mockScope.domainObject.useCapability).toHaveBeenCalledWith(
|
||||
"composition"
|
||||
);
|
||||
expect(controller.layoutPanels).toHaveBeenCalledWith(
|
||||
mockComposition
|
||||
);
|
||||
});
|
||||
|
||||
it("Is robust to concurrent changes to composition", function () {
|
||||
var secondMockComposition = ["a", "b", "c", "d"],
|
||||
secondMockCompositionObjects = secondMockComposition.map(mockDomainObject),
|
||||
firstCompositionCB,
|
||||
secondCompositionCB;
|
||||
|
||||
spyOn(mockCompositionCapability, "then");
|
||||
mockScope.$watchCollection.calls.mostRecent().args[1]();
|
||||
mockScope.$watchCollection.calls.mostRecent().args[1]();
|
||||
|
||||
firstCompositionCB = mockCompositionCapability.then.calls.all()[0].args[0];
|
||||
secondCompositionCB = mockCompositionCapability.then.calls.all()[1].args[0];
|
||||
|
||||
//Resolve promises in reverse order
|
||||
secondCompositionCB(secondMockCompositionObjects);
|
||||
firstCompositionCB(mockCompositionObjects);
|
||||
|
||||
//Expect the promise call that was initiated most recently to
|
||||
// be the one used to populate scope, irrespective of order that
|
||||
// it was eventually resolved
|
||||
expect(mockScope.composition).toBe(secondMockCompositionObjects);
|
||||
});
|
||||
|
||||
|
||||
it("provides styles for frames, from configuration", function () {
|
||||
mockScope.$watchCollection.calls.mostRecent().args[1]();
|
||||
expect(controller.getFrameStyle("a")).toEqual({
|
||||
top: "320px",
|
||||
left: "640px",
|
||||
width: "160px",
|
||||
height: "160px",
|
||||
minWidth : '160px',
|
||||
minHeight : '160px'
|
||||
});
|
||||
});
|
||||
|
||||
it("provides default styles for frames", function () {
|
||||
var styleB, styleC;
|
||||
|
||||
// b and c do not have configured positions
|
||||
mockScope.$watchCollection.calls.mostRecent().args[1]();
|
||||
|
||||
styleB = controller.getFrameStyle("b");
|
||||
styleC = controller.getFrameStyle("c");
|
||||
|
||||
// Should have a position, but we don't care what
|
||||
expect(styleB.left).toBeDefined();
|
||||
expect(styleB.top).toBeDefined();
|
||||
expect(styleC.left).toBeDefined();
|
||||
expect(styleC.top).toBeDefined();
|
||||
|
||||
// Should have ensured some difference in position
|
||||
expect(styleB).not.toEqual(styleC);
|
||||
});
|
||||
|
||||
it("allows panels to be dragged", function () {
|
||||
// Populate scope
|
||||
mockScope.$watchCollection.calls.mostRecent().args[1]();
|
||||
|
||||
// Verify precondition
|
||||
expect(testConfiguration.panels.b).not.toBeDefined();
|
||||
|
||||
// Do a drag
|
||||
controller.startDrag("b", [1, 1], [0, 0]);
|
||||
controller.continueDrag([100, 100]);
|
||||
controller.endDrag();
|
||||
|
||||
// We do not look closely at the details here;
|
||||
// that is tested in LayoutDragSpec. Just make sure
|
||||
// that a configuration for b has been defined.
|
||||
expect(testConfiguration.panels.b).toBeDefined();
|
||||
});
|
||||
|
||||
|
||||
it("invokes commit after drag", function () {
|
||||
// Populate scope
|
||||
mockScope.$watchCollection.calls.mostRecent().args[1]();
|
||||
|
||||
// Do a drag
|
||||
controller.startDrag("b", [1, 1], [0, 0]);
|
||||
controller.continueDrag([100, 100]);
|
||||
controller.endDrag();
|
||||
|
||||
expect(controller.commit).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("listens for drop events", function () {
|
||||
// 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.panels.d).not.toBeDefined();
|
||||
|
||||
// Notify that a drop occurred
|
||||
mockScope.$on.calls.mostRecent().args[1](
|
||||
mockEvent,
|
||||
'd',
|
||||
{ x: 300, y: 100 }
|
||||
);
|
||||
expect(testConfiguration.panels.d).toBeDefined();
|
||||
expect(mockEvent.preventDefault).toHaveBeenCalled();
|
||||
expect(controller.commit).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("ignores drops when default has been prevented", function () {
|
||||
// Avoids redundant drop-handling, WTD-1233
|
||||
mockEvent.defaultPrevented = true;
|
||||
|
||||
// Notify that a drop occurred
|
||||
mockScope.$on.calls.mostRecent().args[1](
|
||||
mockEvent,
|
||||
'd',
|
||||
{ x: 300, y: 100 }
|
||||
);
|
||||
expect(testConfiguration.panels.d).not.toBeDefined();
|
||||
});
|
||||
|
||||
it("ensures a minimum frame size", function () {
|
||||
var styleB;
|
||||
|
||||
// Start with a very small frame size
|
||||
testModel.layoutGrid = [1, 1];
|
||||
|
||||
// White-boxy; we know which watch is which
|
||||
mockScope.$watch.calls.all()[0].args[1](testModel.layoutGrid);
|
||||
mockScope.$watchCollection.calls.all()[0].args[1](testModel.composition);
|
||||
|
||||
styleB = controller.getFrameStyle("b");
|
||||
|
||||
// Resulting size should still be reasonably large pixel-size
|
||||
expect(parseInt(styleB.width, 10)).toBeGreaterThan(63);
|
||||
expect(parseInt(styleB.width, 10)).toBeGreaterThan(31);
|
||||
});
|
||||
|
||||
it("ensures a minimum frame size on drop", function () {
|
||||
var style;
|
||||
|
||||
// Start with a very small frame size
|
||||
testModel.layoutGrid = [1, 1];
|
||||
mockScope.$watch.calls.all()[0].args[1](testModel.layoutGrid);
|
||||
|
||||
// Add a new object to the composition
|
||||
mockComposition = ["a", "b", "c", "d"];
|
||||
mockCompositionObjects = mockComposition.map(mockDomainObject);
|
||||
mockCompositionCapability = mockPromise(mockCompositionObjects);
|
||||
|
||||
// Notify that a drop occurred
|
||||
mockScope.$on.calls.mostRecent().args[1](
|
||||
mockEvent,
|
||||
'd',
|
||||
{ x: 300, y: 100 }
|
||||
);
|
||||
|
||||
style = controller.getFrameStyle("d");
|
||||
|
||||
// Resulting size should still be reasonably large pixel-size
|
||||
expect(parseInt(style.width, 10)).toBeGreaterThan(63);
|
||||
expect(parseInt(style.height, 10)).toBeGreaterThan(31);
|
||||
});
|
||||
|
||||
it("updates positions of existing objects on a drop", function () {
|
||||
var oldStyle;
|
||||
|
||||
mockScope.$watchCollection.calls.mostRecent().args[1]();
|
||||
|
||||
oldStyle = controller.getFrameStyle("b");
|
||||
|
||||
expect(oldStyle).toBeDefined();
|
||||
|
||||
// ...drop event...
|
||||
mockScope.$on.calls.mostRecent()
|
||||
.args[1](mockEvent, 'b', { x: 300, y: 100 });
|
||||
|
||||
expect(controller.getFrameStyle("b"))
|
||||
.not.toEqual(oldStyle);
|
||||
});
|
||||
|
||||
it("allows objects to be selected", function () {
|
||||
mockScope.$watchCollection.calls.mostRecent().args[1]();
|
||||
var childObj = mockCompositionObjects[0];
|
||||
selectable[0].context.oldItem = childObj;
|
||||
mockOpenMCT.selection.on.calls.mostRecent().args[1](selectable);
|
||||
|
||||
expect(controller.selected(childObj)).toBe(true);
|
||||
});
|
||||
|
||||
it("prevents event bubbling while drag is in progress", function () {
|
||||
mockScope.$watchCollection.calls.mostRecent().args[1]();
|
||||
var childObj = mockCompositionObjects[0];
|
||||
|
||||
// Do a drag
|
||||
controller.startDrag(childObj.getId(), [1, 1], [0, 0]);
|
||||
controller.continueDrag([100, 100]);
|
||||
controller.endDrag();
|
||||
|
||||
// Because mouse position could cause the parent object to be selected, this should be ignored.
|
||||
controller.bypassSelection(mockEvent);
|
||||
|
||||
expect(mockEvent.stopPropagation).toHaveBeenCalled();
|
||||
|
||||
// Shoud be able to select another object when dragging is done.
|
||||
jasmine.clock().tick(0);
|
||||
mockEvent.stopPropagation.calls.reset();
|
||||
controller.bypassSelection(mockEvent);
|
||||
|
||||
expect(mockEvent.stopPropagation).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("shows frames by default", function () {
|
||||
mockScope.$watchCollection.calls.mostRecent().args[1]();
|
||||
|
||||
expect(controller.hasFrame(mockCompositionObjects[0])).toBe(true);
|
||||
});
|
||||
|
||||
it("hyperlinks hide frame by default", function () {
|
||||
mockScope.$watchCollection.calls.mostRecent().args[1]();
|
||||
|
||||
expect(controller.hasFrame(mockCompositionObjects[1])).toBe(false);
|
||||
});
|
||||
|
||||
it("selects the parent object when selected object is removed", function () {
|
||||
mockScope.$watchCollection.calls.mostRecent().args[1]();
|
||||
var childObj = mockCompositionObjects[0];
|
||||
selectable[0].context.oldItem = childObj;
|
||||
mockOpenMCT.selection.on.calls.mostRecent().args[1](selectable);
|
||||
|
||||
var composition = ["b", "c"];
|
||||
mockScope.$watchCollection.calls.mostRecent().args[1](composition);
|
||||
|
||||
expect($element[0].click).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("allows objects to be drilled-in only when editing", function () {
|
||||
mockScope.$watchCollection.calls.mostRecent().args[1]();
|
||||
var childObj = mockCompositionObjects[0];
|
||||
childObj.getCapability().inEditContext.and.returnValue(false);
|
||||
controller.drill(mockEvent, childObj);
|
||||
|
||||
expect(controller.isDrilledIn(childObj)).toBe(false);
|
||||
});
|
||||
|
||||
it("allows objects to be drilled-in only if it has sub objects", function () {
|
||||
mockScope.$watchCollection.calls.mostRecent().args[1]();
|
||||
var childObj = mockCompositionObjects[1];
|
||||
childObj.getCapability().inEditContext.and.returnValue(true);
|
||||
controller.drill(mockEvent, childObj);
|
||||
|
||||
expect(controller.isDrilledIn(childObj)).toBe(false);
|
||||
});
|
||||
|
||||
it("selects a newly-dropped object", function () {
|
||||
mockScope.$on.calls.mostRecent().args[1](
|
||||
mockEvent,
|
||||
'd',
|
||||
{ x: 300, y: 100 }
|
||||
);
|
||||
|
||||
var childObj = mockDomainObject("d");
|
||||
var testElement = $("<div data-layout-id='some-id'></div>");
|
||||
$element.append(testElement);
|
||||
spyOn(testElement[0], 'click');
|
||||
|
||||
controller.selectIfNew('some-id', childObj);
|
||||
jasmine.clock().tick(0);
|
||||
|
||||
expect(testElement[0].click).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -22,7 +22,6 @@
|
||||
|
||||
define([
|
||||
"./src/MCTForm",
|
||||
"./src/MCTToolbar",
|
||||
"./src/MCTControl",
|
||||
"./src/MCTFileInput",
|
||||
"./src/FileInputService",
|
||||
@@ -48,7 +47,6 @@ define([
|
||||
'legacyRegistry'
|
||||
], function (
|
||||
MCTForm,
|
||||
MCTToolbar,
|
||||
MCTControl,
|
||||
MCTFileInput,
|
||||
FileInputService,
|
||||
@@ -83,10 +81,6 @@ define([
|
||||
"key": "mctForm",
|
||||
"implementation": MCTForm
|
||||
},
|
||||
{
|
||||
"key": "mctToolbar",
|
||||
"implementation": MCTToolbar
|
||||
},
|
||||
{
|
||||
"key": "mctControl",
|
||||
"implementation": MCTControl,
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<form novalidate>
|
||||
<div class="tool-bar btn-bar contents abs">
|
||||
<span ng-repeat="item in structure">
|
||||
<span ng-if="item.control === 'divider'" class="l-control-group">
|
||||
</span>
|
||||
<ng-form ng-class="{ 'input-labeled': item.name }"
|
||||
ng-hide="item.hidden"
|
||||
ng-if="item.control !== 'divider'"
|
||||
class="inline"
|
||||
title="{{item.description}}"
|
||||
name="mctFormInner">
|
||||
|
||||
<label ng-if="item.name">
|
||||
{{item.name}}:
|
||||
</label>
|
||||
<mct-control key="item.control"
|
||||
ng-class="{ disabled: item.disabled }"
|
||||
ng-model="ngModel"
|
||||
ng-required="item.required"
|
||||
ng-pattern="getRegExp(item.pattern)"
|
||||
options="item.options"
|
||||
structure="item"
|
||||
field="item.key">
|
||||
</mct-control>
|
||||
</ng-form>
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
@@ -1,69 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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 MCTForm. Created by vwoeltje on 11/10/14.
|
||||
*/
|
||||
define(
|
||||
[
|
||||
"./MCTForm",
|
||||
"../res/templates/toolbar.html",
|
||||
"./controllers/ToolbarController"
|
||||
],
|
||||
function (
|
||||
MCTForm,
|
||||
toolbarTemplate,
|
||||
ToolbarController
|
||||
) {
|
||||
|
||||
/**
|
||||
* The mct-toolbar directive allows generation of displayable
|
||||
* forms based on a declarative description of the form's
|
||||
* structure.
|
||||
*
|
||||
* This directive accepts three attributes:
|
||||
*
|
||||
* * `ng-model`: The model for the form; where user input
|
||||
* will be stored.
|
||||
* * `structure`: The declarative structure of the toolbar.
|
||||
* Describes what controls should be shown and where
|
||||
* their values should be read/written in the model.
|
||||
* * `name`: The name under which to expose the form's
|
||||
* dirty/valid state. This is similar to ng-form's use
|
||||
* of name, except this will be made available in the
|
||||
* parent scope.
|
||||
*
|
||||
* @memberof platform/forms
|
||||
* @constructor
|
||||
*/
|
||||
function MCTToolbar() {
|
||||
// Use Directive Definition Object from mct-form,
|
||||
// but use the toolbar's template and controller instead.
|
||||
var ddo = new MCTForm();
|
||||
ddo.template = toolbarTemplate;
|
||||
ddo.controller = ['$scope', 'openmct', ToolbarController];
|
||||
return ddo;
|
||||
}
|
||||
|
||||
return MCTToolbar;
|
||||
}
|
||||
);
|
||||
@@ -1,84 +0,0 @@
|
||||
define(
|
||||
[
|
||||
'../../../commonUI/edit/src/representers/EditToolbar'
|
||||
],
|
||||
function (EditToolbar) {
|
||||
|
||||
// Default ng-pattern; any non whitespace
|
||||
var NON_WHITESPACE = /\S/;
|
||||
|
||||
/**
|
||||
* Controller for mct-toolbar directive.
|
||||
*
|
||||
* @memberof platform/forms
|
||||
* @constructor
|
||||
*/
|
||||
function ToolbarController($scope, openmct) {
|
||||
var regexps = [];
|
||||
|
||||
// ng-pattern seems to want a RegExp, and not a
|
||||
// string (despite what documentation says) but
|
||||
// we want toolbar structure to be JSON-expressible,
|
||||
// so we make RegExp's from strings as-needed
|
||||
function getRegExp(pattern) {
|
||||
// If undefined, don't apply a pattern
|
||||
if (!pattern) {
|
||||
return NON_WHITESPACE;
|
||||
}
|
||||
|
||||
// Just echo if it's already a regexp
|
||||
if (pattern instanceof RegExp) {
|
||||
return pattern;
|
||||
}
|
||||
|
||||
// Otherwise, assume a string
|
||||
// Cache for easy lookup later (so we don't
|
||||
// creat a new RegExp every digest cycle)
|
||||
if (!regexps[pattern]) {
|
||||
regexps[pattern] = new RegExp(pattern);
|
||||
}
|
||||
|
||||
return regexps[pattern];
|
||||
}
|
||||
|
||||
this.openmct = openmct;
|
||||
this.$scope = $scope;
|
||||
$scope.editToolbar = {};
|
||||
$scope.getRegExp = getRegExp;
|
||||
|
||||
$scope.$on("$destroy", this.destroy.bind(this));
|
||||
openmct.selection.on('change', this.handleSelection.bind(this));
|
||||
}
|
||||
|
||||
ToolbarController.prototype.handleSelection = function (selection) {
|
||||
var domainObject = selection[0].context.oldItem;
|
||||
var element = selection[0].context.elementProxy;
|
||||
|
||||
if ((domainObject && domainObject === this.selectedObject) || (element && element === this.selectedObject)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.selectedObject = domainObject || element;
|
||||
|
||||
if (this.editToolbar) {
|
||||
this.editToolbar.destroy();
|
||||
}
|
||||
|
||||
var structure = this.openmct.toolbars.get(selection) || [];
|
||||
this.editToolbar = new EditToolbar(this.$scope, this.openmct, structure);
|
||||
this.$scope.$parent.editToolbar = this.editToolbar;
|
||||
this.$scope.$parent.editToolbar.structure = this.editToolbar.getStructure();
|
||||
this.$scope.$parent.editToolbar.state = this.editToolbar.getState();
|
||||
|
||||
setTimeout(function () {
|
||||
this.$scope.$apply();
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
ToolbarController.prototype.destroy = function () {
|
||||
this.openmct.selection.off("change", this.handleSelection);
|
||||
};
|
||||
|
||||
return ToolbarController;
|
||||
}
|
||||
);
|
||||
@@ -1,112 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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/MCTToolbar"],
|
||||
function (MCTToolbar) {
|
||||
|
||||
describe("The mct-toolbar directive", function () {
|
||||
var mockScope,
|
||||
mockOpenMCT,
|
||||
mockSelection,
|
||||
mctToolbar;
|
||||
|
||||
function installController() {
|
||||
var Controller = mctToolbar.controller[2];
|
||||
return new Controller(mockScope, mockOpenMCT);
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
mockScope = jasmine.createSpyObj("$scope", [
|
||||
"$watch",
|
||||
"$on"
|
||||
]);
|
||||
mockScope.$parent = {};
|
||||
mockSelection = jasmine.createSpyObj("selection", [
|
||||
'on',
|
||||
'off'
|
||||
]);
|
||||
mockOpenMCT = {
|
||||
selection: mockSelection
|
||||
};
|
||||
mctToolbar = new MCTToolbar();
|
||||
});
|
||||
|
||||
it("is restricted to elements", function () {
|
||||
expect(mctToolbar.restrict).toEqual("E");
|
||||
});
|
||||
|
||||
it("listens for selection change event", function () {
|
||||
installController();
|
||||
|
||||
expect(mockOpenMCT.selection.on).toHaveBeenCalledWith(
|
||||
"change",
|
||||
jasmine.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it("allows strings to be converted to RegExps", function () {
|
||||
// This is needed to support ng-pattern in the template
|
||||
installController();
|
||||
|
||||
// Should have added getRegExp to the scope,
|
||||
// to convert strings to regular expressions
|
||||
expect(mockScope.getRegExp("^\\d+$")).toEqual(/^\d+$/);
|
||||
});
|
||||
|
||||
it("returns the same regexp instance for the same string", function () {
|
||||
// Don't want new instances each digest cycle, for performance
|
||||
var strRegExp = "^[a-z]\\d+$",
|
||||
regExp;
|
||||
|
||||
// Add getRegExp to scope
|
||||
installController();
|
||||
regExp = mockScope.getRegExp(strRegExp);
|
||||
|
||||
// Same object instance each time...
|
||||
expect(mockScope.getRegExp(strRegExp)).toBe(regExp);
|
||||
expect(mockScope.getRegExp(strRegExp)).toBe(regExp);
|
||||
});
|
||||
|
||||
it("passes RegExp objects through untouched", function () {
|
||||
// Permit using forms to simply provide their own RegExp object
|
||||
var regExp = /^\d+[a-d]$/;
|
||||
|
||||
// Add getRegExp to scope
|
||||
installController();
|
||||
|
||||
// Should have added getRegExp to the scope,
|
||||
// to convert strings to regular expressions
|
||||
expect(mockScope.getRegExp(regExp)).toBe(regExp);
|
||||
});
|
||||
|
||||
it("passes a non-whitespace regexp when no pattern is defined", function () {
|
||||
// If no pattern is supplied, ng-pattern should match anything
|
||||
installController();
|
||||
expect(mockScope.getRegExp()).toEqual(/\S/);
|
||||
expect(mockScope.getRegExp(undefined)).toEqual(/\S/);
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
);
|
||||
70
src/MCT.js
70
src/MCT.js
@@ -26,6 +26,7 @@ define([
|
||||
'uuid',
|
||||
'./defaultRegistry',
|
||||
'./api/api',
|
||||
'./api/overlays/OverlayAPI',
|
||||
'./selection/Selection',
|
||||
'./api/objects/object-utils',
|
||||
'./plugins/plugins',
|
||||
@@ -40,6 +41,8 @@ define([
|
||||
'./styles-new/core.scss',
|
||||
'./styles-new/notebook.scss',
|
||||
'./ui/components/layout/Layout.vue',
|
||||
'../platform/core/src/objects/DomainObjectImpl',
|
||||
'../platform/core/src/capabilities/ContextualDomainObject',
|
||||
'vue'
|
||||
], function (
|
||||
EventEmitter,
|
||||
@@ -47,6 +50,7 @@ define([
|
||||
uuid,
|
||||
defaultRegistry,
|
||||
api,
|
||||
OverlayAPI,
|
||||
Selection,
|
||||
objectUtils,
|
||||
plugins,
|
||||
@@ -61,6 +65,8 @@ define([
|
||||
coreStyles,
|
||||
NotebookStyles,
|
||||
Layout,
|
||||
DomainObjectImpl,
|
||||
ContextualDomainObject,
|
||||
Vue
|
||||
) {
|
||||
/**
|
||||
@@ -183,15 +189,6 @@ define([
|
||||
*/
|
||||
this.types = new api.TypeRegistry();
|
||||
|
||||
/**
|
||||
* Utilities for attaching common behaviors to views.
|
||||
*
|
||||
* @type {module:openmct.GestureAPI}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name gestures
|
||||
*/
|
||||
this.gestures = new api.GestureAPI();
|
||||
|
||||
/**
|
||||
* An interface for interacting with domain objects and the domain
|
||||
* object hierarchy.
|
||||
@@ -221,11 +218,18 @@ define([
|
||||
*/
|
||||
this.indicators = new api.IndicatorAPI(this);
|
||||
|
||||
this.Dialog = api.Dialog;
|
||||
this.notifications = new api.NotificationAPI();
|
||||
|
||||
this.editor = new api.EditorAPI.default(this);
|
||||
|
||||
this.overlays = new OverlayAPI.default();
|
||||
|
||||
this.contextMenu = new api.ContextMenuRegistry();
|
||||
|
||||
this.legacyRegistry = defaultRegistry;
|
||||
this.install(this.plugins.Plot());
|
||||
this.install(this.plugins.TelemetryTable());
|
||||
this.install(this.plugins.DisplayLayout());
|
||||
|
||||
if (typeof BUILD_CONSTANTS !== 'undefined') {
|
||||
this.install(buildInfoPlugin(BUILD_CONSTANTS));
|
||||
@@ -243,6 +247,42 @@ define([
|
||||
this.legacyBundle.extensions[category].push(extension);
|
||||
};
|
||||
|
||||
/**
|
||||
* Return a legacy object, for compatibility purposes only. This method
|
||||
* will be deprecated and removed in the future.
|
||||
* @private
|
||||
*/
|
||||
MCT.prototype.legacyObject = function (domainObject) {
|
||||
let capabilityService = this.$injector.get('capabilityService');
|
||||
|
||||
function instantiate(model, keyString) {
|
||||
var capabilities = capabilityService.getCapabilities(model, keyString);
|
||||
model.id = keyString;
|
||||
return new DomainObjectImpl(keyString, model, capabilities);
|
||||
}
|
||||
|
||||
if (Array.isArray(domainObject)) {
|
||||
// an array of domain objects. [object, ...ancestors] representing
|
||||
// a single object with a given chain of ancestors. We instantiate
|
||||
// as a single contextual domain object.
|
||||
return domainObject
|
||||
.map((o) => {
|
||||
let keyString = objectUtils.makeKeyString(o.identifier);
|
||||
let oldModel = objectUtils.toOldFormat(o);
|
||||
return instantiate(oldModel, keyString);
|
||||
})
|
||||
.reverse()
|
||||
.reduce((parent, child) => {
|
||||
return new ContextualDomainObject(child, parent);
|
||||
});
|
||||
|
||||
} else {
|
||||
let keyString = objectUtils.makeKeyString(domainObject.identifier);
|
||||
let oldModel = objectUtils.toOldFormat(domainObject);
|
||||
return instantiate(oldModel, keyString);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set path to where assets are hosted. This should be the path to main.js.
|
||||
* @memberof module:openmct.MCT#
|
||||
@@ -310,13 +350,17 @@ define([
|
||||
this.$injector.get('objectService');
|
||||
|
||||
var appLayout = new Vue({
|
||||
mixins: [Layout.default],
|
||||
components: {
|
||||
'Layout': Layout.default
|
||||
},
|
||||
provide: {
|
||||
openmct: this
|
||||
}
|
||||
},
|
||||
template: '<Layout ref="layout"></Layout>'
|
||||
});
|
||||
domElement.appendChild(appLayout.$mount().$el);
|
||||
this.layout = appLayout;
|
||||
|
||||
this.layout = appLayout.$refs.layout;
|
||||
Browse(this);
|
||||
this.router.start();
|
||||
this.emit('start');
|
||||
|
||||
@@ -20,32 +20,18 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
|
||||
/**
|
||||
* Defines composition policy for Display Layout objects.
|
||||
* They cannot contain folders.
|
||||
* @constructor
|
||||
* @memberof platform/features/layout
|
||||
* @implements {Policy.<View, DomainObject>}
|
||||
*/
|
||||
function LayoutCompositionPolicy() {
|
||||
}
|
||||
|
||||
LayoutCompositionPolicy.prototype.allow = function (parent, child) {
|
||||
var parentType = parent.getCapability('type');
|
||||
if (parentType.instanceOf('layout') &&
|
||||
child.getCapability('type').instanceOf('folder')) {
|
||||
|
||||
return false;
|
||||
}
|
||||
import LegacyContextMenuAction from './LegacyContextMenuAction';
|
||||
|
||||
export default function LegacyActionAdapter(openmct, legacyActions) {
|
||||
function contextualCategoryOnly(action) {
|
||||
if (action.category === 'contextual') {
|
||||
return true;
|
||||
};
|
||||
|
||||
return LayoutCompositionPolicy;
|
||||
}
|
||||
console.warn(`DEPRECATION WARNING: Action ${action.definition.key} in bundle ${action.bundle.path} is non-contextual and should be migrated.`);
|
||||
return false;
|
||||
}
|
||||
);
|
||||
|
||||
legacyActions.filter(contextualCategoryOnly)
|
||||
.map(LegacyAction => new LegacyContextMenuAction(openmct, LegacyAction))
|
||||
.forEach(openmct.contextMenu.registerAction);
|
||||
}
|
||||
57
src/adapter/actions/LegacyContextMenuAction.js
Normal file
57
src/adapter/actions/LegacyContextMenuAction.js
Normal file
@@ -0,0 +1,57 @@
|
||||
import { timingSafeEqual } from "crypto";
|
||||
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
export default class LegacyContextMenuAction {
|
||||
constructor(openmct, LegacyAction) {
|
||||
this.openmct = openmct;
|
||||
this.name = LegacyAction.definition.name;
|
||||
this.description = LegacyAction.definition.description;
|
||||
this.cssClass = LegacyAction.definition.cssClass;
|
||||
this.LegacyAction = LegacyAction;
|
||||
}
|
||||
|
||||
appliesTo(objectPath) {
|
||||
let legacyObject = this.openmct.legacyObject(objectPath);
|
||||
return this.LegacyAction.appliesTo({
|
||||
domainObject: legacyObject
|
||||
});
|
||||
}
|
||||
|
||||
invoke(objectPath) {
|
||||
let context = {
|
||||
category: 'contextual',
|
||||
domainObject: this.openmct.legacyObject(objectPath)
|
||||
}
|
||||
let legacyAction = new this.LegacyAction(context);
|
||||
|
||||
if (!legacyAction.getMetadata) {
|
||||
let metadata = Object.create(this.LegacyAction.definition);
|
||||
metadata.context = context;
|
||||
legacyAction.getMetadata = function () {
|
||||
return metadata;
|
||||
}.bind(legacyAction);
|
||||
}
|
||||
legacyAction.perform();
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,6 @@ define([
|
||||
'./services/Instantiate',
|
||||
'./services/MissingModelCompatibilityDecorator',
|
||||
'./capabilities/APICapabilityDecorator',
|
||||
'./policies/AdapterCompositionPolicy',
|
||||
'./policies/AdaptedViewPolicy',
|
||||
'./runs/AlternateCompositionInitializer',
|
||||
'./runs/TimeSettingsURLHandler',
|
||||
@@ -36,7 +35,9 @@ define([
|
||||
'./runs/LegacyTelemetryProvider',
|
||||
'./runs/RegisterLegacyTypes',
|
||||
'./services/LegacyObjectAPIInterceptor',
|
||||
'./views/installLegacyViews'
|
||||
'./views/installLegacyViews',
|
||||
'./policies/legacyCompositionPolicyAdapter',
|
||||
'./actions/LegacyActionAdapter'
|
||||
], function (
|
||||
legacyRegistry,
|
||||
ActionDialogDecorator,
|
||||
@@ -45,7 +46,6 @@ define([
|
||||
Instantiate,
|
||||
MissingModelCompatibilityDecorator,
|
||||
APICapabilityDecorator,
|
||||
AdapterCompositionPolicy,
|
||||
AdaptedViewPolicy,
|
||||
AlternateCompositionInitializer,
|
||||
TimeSettingsURLHandler,
|
||||
@@ -53,7 +53,9 @@ define([
|
||||
LegacyTelemetryProvider,
|
||||
RegisterLegacyTypes,
|
||||
LegacyObjectAPIInterceptor,
|
||||
installLegacyViews
|
||||
installLegacyViews,
|
||||
legacyCompositionPolicyAdapter,
|
||||
LegacyActionAdapter
|
||||
) {
|
||||
legacyRegistry.register('src/adapter', {
|
||||
"extensions": {
|
||||
@@ -117,11 +119,6 @@ define([
|
||||
}
|
||||
],
|
||||
policies: [
|
||||
{
|
||||
category: "composition",
|
||||
implementation: AdapterCompositionPolicy,
|
||||
depends: ["openmct"]
|
||||
},
|
||||
{
|
||||
category: "view",
|
||||
implementation: AdaptedViewPolicy,
|
||||
@@ -168,6 +165,19 @@ define([
|
||||
"types[]",
|
||||
"openmct"
|
||||
]
|
||||
},
|
||||
{
|
||||
implementation: legacyCompositionPolicyAdapter.default,
|
||||
depends: [
|
||||
"openmct"
|
||||
]
|
||||
},
|
||||
{
|
||||
implementation: LegacyActionAdapter.default,
|
||||
depends: [
|
||||
"openmct",
|
||||
"actions[]"
|
||||
]
|
||||
}
|
||||
],
|
||||
licenses: [
|
||||
|
||||
42
src/adapter/policies/LegacyCompositionPolicyAdapter.js
Normal file
42
src/adapter/policies/LegacyCompositionPolicyAdapter.js
Normal file
@@ -0,0 +1,42 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
export default function legacyCompositionPolicyAdapter(openmct) {
|
||||
const instantiate = this.openmct.$injector.get('instantiate');
|
||||
const policyService = this.openmct.$injector.get('policyService');
|
||||
|
||||
openmct.composition.addPolicy((parent, child) => {
|
||||
|
||||
let parentId = this.openmct.objects.makeKeyString(parent.identifier);
|
||||
let childId = this.openmct.objects.makeKeyString(child.identifier);
|
||||
|
||||
let legacyParent = instantiate(parent, parentId);
|
||||
let legacyChild = instantiate(child, childId);
|
||||
let result = policyService.allow(
|
||||
'composition',
|
||||
legacyParent,
|
||||
legacyChild
|
||||
);
|
||||
|
||||
return result;
|
||||
});
|
||||
}
|
||||
95
src/adapter/views/TypeInspectorViewProvider.js
Normal file
95
src/adapter/views/TypeInspectorViewProvider.js
Normal file
@@ -0,0 +1,95 @@
|
||||
define([
|
||||
|
||||
], function (
|
||||
|
||||
) {
|
||||
const DEFAULT_VIEW_PRIORITY = 100;
|
||||
|
||||
const PRIORITY_LEVELS = {
|
||||
"fallback": Number.NEGATIVE_INFINITY,
|
||||
"default": -100,
|
||||
"none": 0,
|
||||
"optional": DEFAULT_VIEW_PRIORITY,
|
||||
"preferred": 1000,
|
||||
"mandatory": Number.POSITIVE_INFINITY
|
||||
};
|
||||
|
||||
function TypeInspectorViewProvider(typeDefinition, openmct, convertToLegacyObject) {
|
||||
console.warn(`DEPRECATION WARNING: Migrate ${typeDefinition.key} from ${typeDefinition.bundle.path} to use the new Inspector View APIs. Legacy Inspector view support will be removed soon.`);
|
||||
let representation = openmct.$injector.get('representations[]')
|
||||
.filter((r) => r.key === typeDefinition.inspector)[0];
|
||||
|
||||
return {
|
||||
key: representation.key,
|
||||
name: representation.name,
|
||||
cssClass: representation.cssClass,
|
||||
description: representation.description,
|
||||
canView: function (selection) {
|
||||
if (!selection[0] || !selection[0].context.item) {
|
||||
return false;
|
||||
}
|
||||
let domainObject = selection[0].context.item;
|
||||
return domainObject.type === typeDefinition.key;
|
||||
},
|
||||
view: function (selection) {
|
||||
let domainObject = selection[0].context.item;
|
||||
let $rootScope = openmct.$injector.get('$rootScope');
|
||||
let templateLinker = openmct.$injector.get('templateLinker');
|
||||
let scope = $rootScope.$new();
|
||||
let legacyObject = convertToLegacyObject(domainObject);
|
||||
let isDestroyed = false;
|
||||
scope.domainObject = legacyObject;
|
||||
scope.model = legacyObject.getModel();
|
||||
|
||||
|
||||
return {
|
||||
show: function (container) {
|
||||
// TODO: implement "gestures" support ?
|
||||
let uses = representation.uses || [];
|
||||
let promises = [];
|
||||
let results = uses.map(function (capabilityKey, i) {
|
||||
let result = legacyObject.useCapability(capabilityKey);
|
||||
if (result.then) {
|
||||
promises.push(result.then(function (r) {
|
||||
results[i] = r;
|
||||
}));
|
||||
}
|
||||
return result;
|
||||
});
|
||||
|
||||
function link() {
|
||||
if (isDestroyed) {
|
||||
return;
|
||||
}
|
||||
uses.forEach(function (key, i) {
|
||||
scope[key] = results[i];
|
||||
});
|
||||
templateLinker.link(
|
||||
scope,
|
||||
openmct.$angular.element(container),
|
||||
representation
|
||||
);
|
||||
container.style.height = '100%';
|
||||
}
|
||||
|
||||
if (promises.length) {
|
||||
Promise.all(promises)
|
||||
.then(function () {
|
||||
link();
|
||||
scope.$digest();
|
||||
});
|
||||
} else {
|
||||
link();
|
||||
}
|
||||
},
|
||||
destroy: function () {
|
||||
scope.$destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
return TypeInspectorViewProvider;
|
||||
|
||||
});
|
||||
@@ -1,8 +1,10 @@
|
||||
define([
|
||||
'./LegacyViewProvider',
|
||||
'./TypeInspectorViewProvider',
|
||||
'../../api/objects/object-utils'
|
||||
], function (
|
||||
LegacyViewProvider,
|
||||
TypeInspectorViewProvider,
|
||||
objectUtils
|
||||
) {
|
||||
function installLegacyViews(openmct, legacyViews, instantiate) {
|
||||
@@ -16,6 +18,13 @@ define([
|
||||
legacyViews.forEach(function (legacyView) {
|
||||
openmct.objectViews.addProvider(new LegacyViewProvider(legacyView, openmct, convertToLegacyObject));
|
||||
});
|
||||
|
||||
let inspectorTypes = openmct.$injector.get('types[]')
|
||||
.filter((t) => t.hasOwnProperty('inspector'));
|
||||
|
||||
inspectorTypes.forEach(function (typeDefinition) {
|
||||
openmct.inspectorViews.addProvider(new TypeInspectorViewProvider(typeDefinition, openmct, convertToLegacyObject));
|
||||
});
|
||||
}
|
||||
|
||||
return installLegacyViews;
|
||||
|
||||
86
src/api/Editor.js
Normal file
86
src/api/Editor.js
Normal file
@@ -0,0 +1,86 @@
|
||||
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
import EventEmitter from 'EventEmitter';
|
||||
|
||||
export default class Editor extends EventEmitter {
|
||||
constructor(openmct) {
|
||||
super();
|
||||
this.editing = false;
|
||||
this.openmct = openmct;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate an editing session. This will start a transaction during
|
||||
* which any persist operations will be deferred until either save()
|
||||
* or finish() are called.
|
||||
*/
|
||||
edit() {
|
||||
if (this.editing === true) {
|
||||
throw "Already editing";
|
||||
}
|
||||
this.editing = true;
|
||||
this.getTransactionService().startTransaction();
|
||||
this.emit('isEditing', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns true if the application is in edit mode, false otherwise.
|
||||
*/
|
||||
isEditing() {
|
||||
return this.editing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save any unsaved changes from this editing session. This will
|
||||
* end the current transaction.
|
||||
*/
|
||||
save() {
|
||||
return this.getTransactionService().commit().then((result)=>{
|
||||
this.editing = false;
|
||||
this.emit('isEditing', false);
|
||||
return result
|
||||
}).catch((error)=>{
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* End the currently active transaction and discard unsaved changes.
|
||||
*/
|
||||
cancel() {
|
||||
this.getTransactionService().cancel();
|
||||
this.editing = false;
|
||||
this.emit('isEditing', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
getTransactionService() {
|
||||
if (!this.transactionService) {
|
||||
this.transactionService = this.openmct.$injector.get('transactionService');
|
||||
}
|
||||
return this.transactionService;
|
||||
}
|
||||
}
|
||||
@@ -25,28 +25,32 @@ define([
|
||||
'./objects/ObjectAPI',
|
||||
'./composition/CompositionAPI',
|
||||
'./types/TypeRegistry',
|
||||
'./ui/Dialog',
|
||||
'./ui/GestureAPI',
|
||||
'./telemetry/TelemetryAPI',
|
||||
'./indicators/IndicatorAPI'
|
||||
'./indicators/IndicatorAPI',
|
||||
'./notifications/NotificationAPI',
|
||||
'./contextMenu/ContextMenuAPI',
|
||||
'./Editor'
|
||||
|
||||
], function (
|
||||
TimeAPI,
|
||||
ObjectAPI,
|
||||
CompositionAPI,
|
||||
TypeRegistry,
|
||||
Dialog,
|
||||
GestureAPI,
|
||||
TelemetryAPI,
|
||||
IndicatorAPI
|
||||
IndicatorAPI,
|
||||
NotificationAPI,
|
||||
ContextMenuAPI,
|
||||
EditorAPI
|
||||
) {
|
||||
return {
|
||||
TimeAPI: TimeAPI,
|
||||
ObjectAPI: ObjectAPI,
|
||||
CompositionAPI: CompositionAPI,
|
||||
Dialog: Dialog,
|
||||
TypeRegistry: TypeRegistry,
|
||||
GestureAPI: GestureAPI,
|
||||
TelemetryAPI: TelemetryAPI,
|
||||
IndicatorAPI: IndicatorAPI
|
||||
IndicatorAPI: IndicatorAPI,
|
||||
NotificationAPI: NotificationAPI.default,
|
||||
EditorAPI: EditorAPI,
|
||||
ContextMenuRegistry: ContextMenuAPI.default
|
||||
};
|
||||
});
|
||||
|
||||
@@ -44,7 +44,7 @@ define([
|
||||
function CompositionAPI(publicAPI) {
|
||||
this.registry = [];
|
||||
this.policies = [];
|
||||
this.addProvider(new DefaultCompositionProvider(publicAPI));
|
||||
this.addProvider(new DefaultCompositionProvider(publicAPI, this));
|
||||
this.publicAPI = publicAPI;
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user