Compare commits

..

1 Commits

Author SHA1 Message Date
ca88996d86 Persist table sort options 2019-03-22 17:44:29 -07:00
288 changed files with 10664 additions and 7669 deletions

View File

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

View File

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

View File

@ -27,9 +27,11 @@
<meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes">
<title></title> <title></title>
<script src="dist/openmct.js"></script> <script src="dist/openmct.js"></script>
<link rel="icon" type="image/png" href="dist/favicons/favicon-96x96.png" sizes="96x96" type="image/x-icon"> <link rel="stylesheet" href="dist/styles/openmct.css">
<link rel="icon" type="image/png" href="dist/favicons/favicon-32x32.png" sizes="32x32" type="image/x-icon"> <link rel="icon" type="image/png" href="dist/favicons/favicon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="dist/favicons/favicon-16x16.png" sizes="16x16" type="image/x-icon"> <link rel="icon" type="image/png" href="dist/favicons/favicon-96x96.png" sizes="96x96">
<link rel="icon" type="image/png" href="dist/favicons/favicon-16x16.png" sizes="16x16">
<link rel="shortcut icon" href="dist/favicons/favicon.ico">
</head> </head>
<body> <body>
</body> </body>
@ -48,12 +50,10 @@
openmct.install(openmct.plugins.Generator()); openmct.install(openmct.plugins.Generator());
openmct.install(openmct.plugins.ExampleImagery()); openmct.install(openmct.plugins.ExampleImagery());
openmct.install(openmct.plugins.UTCTimeSystem()); openmct.install(openmct.plugins.UTCTimeSystem());
openmct.install(openmct.plugins.ImportExport());
openmct.install(openmct.plugins.AutoflowView({ openmct.install(openmct.plugins.AutoflowView({
type: "telemetry.panel" type: "telemetry.panel"
})); }));
openmct.install(openmct.plugins.DisplayLayout({
showAsView: ['summary-widget', 'example.imagery']
}));
openmct.install(openmct.plugins.Conductor({ openmct.install(openmct.plugins.Conductor({
menuOptions: [ menuOptions: [
{ {
@ -77,10 +77,12 @@
})); }));
openmct.install(openmct.plugins.SummaryWidget()); openmct.install(openmct.plugins.SummaryWidget());
openmct.install(openmct.plugins.Notebook()); openmct.install(openmct.plugins.Notebook());
openmct.install(openmct.plugins.FolderView());
openmct.install(openmct.plugins.Tabs());
openmct.install(openmct.plugins.FlexibleLayout());
openmct.install(openmct.plugins.LADTable()); openmct.install(openmct.plugins.LADTable());
openmct.install(openmct.plugins.Filters(['table', 'telemetry.plot.overlay'])); openmct.install(openmct.plugins.Filters(['table', 'telemetry.plot.overlay']));
openmct.install(openmct.plugins.ObjectMigration()); openmct.install(openmct.plugins.ObjectMigration());
openmct.install(openmct.plugins.ClearData(['table', 'telemetry.plot.overlay', 'telemetry.plot.stacked']));
openmct.start(); openmct.start();
</script> </script>
</html> </html>

View File

@ -23,9 +23,9 @@
/*global module,process*/ /*global module,process*/
const devMode = process.env.NODE_ENV !== 'production'; const devMode = process.env.NODE_ENV !== 'production';
const browsers = [process.env.NODE_ENV === 'debug' ? 'ChromeDebugging' : 'ChromeHeadless'];
module.exports = (config) => { module.exports = (config) => {
const webpackConfig = require('./webpack.config.js'); const webpackConfig = require('./webpack.config.js');
delete webpackConfig.output; delete webpackConfig.output;
@ -50,17 +50,11 @@ module.exports = (config) => {
'coverage', 'coverage',
'html' 'html'
], ],
browsers: browsers, browsers: ['ChromeHeadless'],
customLaunchers: {
ChromeDebugging: {
base: 'Chrome',
flags: ['--remote-debugging-port=9222'],
debug: true
}
},
colors: true, colors: true,
logLevel: config.LOG_INFO, logLevel: config.LOG_INFO,
autoWatch: true, autoWatch: true,
coverageReporter: { coverageReporter: {
dir: process.env.CIRCLE_ARTIFACTS ? dir: process.env.CIRCLE_ARTIFACTS ?
process.env.CIRCLE_ARTIFACTS + '/coverage' : process.env.CIRCLE_ARTIFACTS + '/coverage' :
@ -72,18 +66,22 @@ module.exports = (config) => {
} }
} }
}, },
// HTML test reporting. // HTML test reporting.
htmlReporter: { htmlReporter: {
outputDir: "dist/reports/tests", outputDir: "dist/reports/tests",
preserveDescribeNesting: true, preserveDescribeNesting: true,
foldAll: false foldAll: false
}, },
preprocessors: { preprocessors: {
// add webpack as preprocessor // add webpack as preprocessor
'platform/**/*Spec.js': [ 'webpack', 'sourcemap' ], 'platform/**/*Spec.js': [ 'webpack' ],
'src/**/*Spec.js': [ 'webpack', 'sourcemap' ] 'src/**/*Spec.js': [ 'webpack' ]
}, },
webpack: webpackConfig, webpack: webpackConfig,
webpackMiddleware: { webpackMiddleware: {
stats: 'errors-only', stats: 'errors-only',
logLevel: 'warn' logLevel: 'warn'
@ -91,3 +89,4 @@ module.exports = (config) => {
singleRun: true singleRun: true
}); });
} }

View File

@ -19,7 +19,7 @@
* this source code distribution or the Licensing information page available * this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
/*global module*/ /*global module,BUILD_CONSTANTS*/
const matcher = /\/openmct.js$/; const matcher = /\/openmct.js$/;
if (document.currentScript) { if (document.currentScript) {

View File

@ -1,10 +1,9 @@
{ {
"name": "openmct", "name": "openmct",
"version": "1.0.0-beta", "version": "0.14.0-SNAPSHOT",
"description": "The Open MCT core platform", "description": "The Open MCT core platform",
"dependencies": {}, "dependencies": {},
"devDependencies": { "devDependencies": {
"acorn": "6.2.0",
"angular": "1.4.14", "angular": "1.4.14",
"angular-route": "1.4.14", "angular-route": "1.4.14",
"babel-eslint": "8.2.6", "babel-eslint": "8.2.6",
@ -43,7 +42,6 @@
"karma-coverage": "^1.1.2", "karma-coverage": "^1.1.2",
"karma-html-reporter": "^0.2.7", "karma-html-reporter": "^0.2.7",
"karma-jasmine": "^1.1.2", "karma-jasmine": "^1.1.2",
"karma-sourcemap-loader": "^0.3.7",
"karma-webpack": "^3.0.0", "karma-webpack": "^3.0.0",
"location-bar": "^3.0.1", "location-bar": "^3.0.1",
"lodash": "^3.10.1", "lodash": "^3.10.1",
@ -57,7 +55,7 @@
"node-bourbon": "^4.2.3", "node-bourbon": "^4.2.3",
"node-sass": "^4.9.2", "node-sass": "^4.9.2",
"painterro": "^0.2.65", "painterro": "^0.2.65",
"printj": "^1.2.1", "printj": "^1.1.0",
"raw-loader": "^0.5.1", "raw-loader": "^0.5.1",
"request": "^2.69.0", "request": "^2.69.0",
"split": "^1.0.0", "split": "^1.0.0",
@ -80,7 +78,6 @@
"build:dev": "webpack", "build:dev": "webpack",
"build:watch": "webpack --watch", "build:watch": "webpack --watch",
"test": "karma start --single-run", "test": "karma start --single-run",
"test-debug": "NODE_ENV=debug karma start --no-single-run",
"test:watch": "karma start --no-single-run", "test:watch": "karma start --no-single-run",
"verify": "concurrently 'npm:test' 'npm:lint'", "verify": "concurrently 'npm:test' 'npm:lint'",
"jsdoc": "jsdoc -c jsdoc.json -R API.md -r -d dist/docs/api", "jsdoc": "jsdoc -c jsdoc.json -R API.md -r -d dist/docs/api",

View File

@ -21,10 +21,17 @@
*****************************************************************************/ *****************************************************************************/
define([ define([
"./src/BrowseController",
"./src/PaneController",
"./src/InspectorPaneController",
"./src/BrowseObjectController",
"./src/MenuArrowController",
"./src/ObjectHeaderController",
"./src/navigation/NavigationService", "./src/navigation/NavigationService",
"./src/navigation/NavigateAction", "./src/navigation/NavigateAction",
"./src/navigation/OrphanNavigationHandler", "./src/navigation/OrphanNavigationHandler",
"./src/windowing/NewTabAction", "./src/windowing/NewTabAction",
"./src/windowing/WindowTitler",
"./res/templates/browse.html", "./res/templates/browse.html",
"./res/templates/browse-object.html", "./res/templates/browse-object.html",
"./res/templates/browse/object-header.html", "./res/templates/browse/object-header.html",
@ -35,10 +42,17 @@ define([
"./res/templates/browse/inspector-region.html", "./res/templates/browse/inspector-region.html",
'legacyRegistry' 'legacyRegistry'
], function ( ], function (
BrowseController,
PaneController,
InspectorPaneController,
BrowseObjectController,
MenuArrowController,
ObjectHeaderController,
NavigationService, NavigationService,
NavigateAction, NavigateAction,
OrphanNavigationHandler, OrphanNavigationHandler,
NewTabAction, NewTabAction,
WindowTitler,
browseTemplate, browseTemplate,
browseObjectTemplate, browseObjectTemplate,
objectHeaderTemplate, objectHeaderTemplate,
@ -61,6 +75,70 @@ define([
"priority": "fallback" "priority": "fallback"
} }
], ],
"controllers": [
{
"key": "BrowseController",
"implementation": BrowseController,
"depends": [
"$scope",
"$route",
"$location",
"objectService",
"navigationService",
"urlService",
"DEFAULT_PATH"
]
},
{
"key": "PaneController",
"implementation": PaneController,
"priority": "preferred",
"depends": [
"$scope",
"agentService",
"$window",
"$location",
"$attrs",
"navigationService"
]
},
{
"key": "BrowseObjectController",
"implementation": BrowseObjectController,
"depends": [
"$scope",
"$location",
"$route"
]
},
{
"key": "MenuArrowController",
"implementation": MenuArrowController,
"depends": [
"$scope"
]
},
{
"key": "InspectorPaneController",
"implementation": InspectorPaneController,
"priority": "preferred",
"depends": [
"$scope",
"agentService",
"$window",
"navigationService",
"$location",
"$attrs"
]
},
{
"key": "ObjectHeaderController",
"implementation": ObjectHeaderController,
"depends": [
"$scope"
]
}
],
"representations": [ "representations": [
{ {
"key": "browse-object", "key": "browse-object",
@ -148,6 +226,14 @@ define([
} }
], ],
"runs": [ "runs": [
{
"implementation": WindowTitler,
"depends": [
"navigationService",
"$rootScope",
"$document"
]
},
{ {
"implementation": OrphanNavigationHandler, "implementation": OrphanNavigationHandler,
"depends": [ "depends": [

View File

@ -0,0 +1,215 @@
/*****************************************************************************
* 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 Browse mode.
* @namespace platform/commonUI/browse
*/
define(
['lodash'],
function (_) {
/**
* The BrowseController is used to populate the initial scope in Browse
* mode. It loads the root object from the objectService and makes it
* available in the scope for Angular template's; this is the point at
* which Angular templates first have access to the domain object
* hierarchy.
*
* @memberof platform/commonUI/browse
* @constructor
*/
function BrowseController(
$scope,
$route,
$location,
objectService,
navigationService,
urlService,
defaultPath
) {
window.browseScope = $scope;
var initialPath = ($route.current.params.ids || defaultPath).split("/"),
currentIds;
$scope.treeModel = {
selectedObject: undefined,
onSelection: function (object) {
navigationService.setNavigation(object, true);
},
allowSelection: function (object) {
var domainObjectInView = navigationService.getNavigation(),
isInEditMode = domainObjectInView.getCapability('status').get('editing');
if (isInEditMode) {
var actions = object.getCapability('action'),
previewAction = actions.getActions({key: 'mct-preview-action'})[0];
if (previewAction && previewAction.perform) {
previewAction.perform();
return false;
} else {
return navigationService.shouldNavigate();
}
} else {
return true;
}
}
};
function idsForObject(domainObject) {
return urlService
.urlForLocation("", domainObject)
.replace('/', '');
}
// Find an object in an array of objects.
function findObject(domainObjects, id) {
var i;
for (i = 0; i < domainObjects.length; i += 1) {
if (domainObjects[i].getId() === id) {
return domainObjects[i];
}
}
}
// helper, fetch a single object from the object service.
function getObject(id) {
return objectService.getObjects([id])
.then(function (results) {
return results[id];
});
}
// recursively locate and return an object inside of a container
// via a path. If at any point in the recursion it fails to find
// the next object, it will return the parent.
function findViaComposition(containerObject, path) {
var nextId = path.shift();
if (!nextId) {
return containerObject;
}
return containerObject.useCapability('composition')
.then(function (composees) {
var nextObject = findObject(composees, nextId);
if (!nextObject) {
return containerObject;
}
if (!nextObject.hasCapability('composition')) {
return nextObject;
}
return findViaComposition(nextObject, path);
});
}
function navigateToObject(desiredObject) {
$scope.navigatedObject = desiredObject;
$scope.treeModel.selectedObject = desiredObject;
currentIds = idsForObject(desiredObject);
$route.current.pathParams.ids = currentIds;
$location.path('/browse/' + currentIds);
}
function getLastChildIfRoot(object) {
if (object.getId() !== 'ROOT') {
return object;
}
return object.useCapability('composition')
.then(function (composees) {
return composees[composees.length - 1];
});
}
function navigateToPath(path) {
return getObject('ROOT')
.then(function (root) {
return findViaComposition(root, path);
})
.then(getLastChildIfRoot)
.then(function (object) {
navigationService.setNavigation(object);
});
}
getObject('ROOT')
.then(function (root) {
$scope.domainObject = root;
navigateToPath(initialPath);
});
// Handle navigation events from view service. Only navigates
// if path has changed.
function navigateDirectlyToModel(domainObject) {
var newIds = idsForObject(domainObject);
if (currentIds !== newIds) {
currentIds = newIds;
navigateToObject(domainObject);
}
}
// Listen for changes in navigation state.
navigationService.addListener(navigateDirectlyToModel);
// Listen for route changes which are caused by browser events
// (e.g. bookmarks to pages in OpenMCT) and prevent them. Instead,
// navigate to the path ourselves, which results in it being
// properly set.
$scope.$on('$routeChangeStart', function (event, route, oldRoute) {
if (route.$$route === $route.current.$$route) {
if (route.pathParams.ids &&
route.pathParams.ids !== $route.current.pathParams.ids) {
var otherParams = _.omit(route.params, 'ids');
var oldOtherParams = _.omit(oldRoute.params, 'ids');
var deletedParams = _.omit(oldOtherParams, _.keys(otherParams));
event.preventDefault();
navigateToPath(route.pathParams.ids.split('/'))
.then(function () {
if (!_.isEqual(otherParams, oldOtherParams)) {
_.forEach(otherParams, function (v, k) {
$location.search(k, v);
});
_.forEach(deletedParams, function (k) {
$location.search(k, null);
});
}
});
} else {
navigateToPath([]);
}
}
});
// Clean up when the scope is destroyed
$scope.$on("$destroy", function () {
navigationService.removeListener(navigateDirectlyToModel);
});
}
return BrowseController;
}
);

View File

@ -0,0 +1,72 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
[],
function () {
/**
* Controller for the `browse-object` representation of a domain
* object (the right-hand side of Browse mode.)
* @memberof platform/commonUI/browse
* @constructor
*/
function BrowseObjectController($scope, $location, $route) {
function setViewForDomainObject(domainObject) {
var locationViewKey = $location.search().view;
function selectViewIfMatching(view) {
if (view.key === locationViewKey) {
$scope.representation = $scope.representation || {};
$scope.representation.selected = view;
}
}
if (locationViewKey) {
((domainObject && domainObject.useCapability('view')) || [])
.forEach(selectViewIfMatching);
}
}
function updateQueryParam(viewKey) {
if (viewKey && $location.search().view !== viewKey) {
$location.search('view', viewKey);
}
}
$scope.$watch('domainObject', setViewForDomainObject);
$scope.$watch('representation.selected.key', updateQueryParam);
$scope.$on('$locationChangeSuccess', function () {
setViewForDomainObject($scope.domainObject);
});
$scope.doAction = function (action) {
return $scope[action] && $scope[action]();
};
}
return BrowseObjectController;
}
);

View File

@ -0,0 +1,78 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web 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 Web 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(
["./PaneController"],
function (PaneController) {
/**
* Pane controller that reveals inspector, if hidden, when object
* switches to edit mode.
*
* @param $scope
* @param agentService
* @param $window
* @param navigationService
* @constructor
*/
function InspectorPaneController($scope, agentService, $window, navigationService, $location, $attrs) {
PaneController.call(this, $scope, agentService, $window, $location, $attrs);
var statusListener,
self = this;
function showInspector(statuses) {
if (statuses.indexOf('editing') !== -1 && !self.visible()) {
self.toggle();
}
}
function attachStatusListener(domainObject) {
// Remove existing status listener if existing
if (statusListener) {
statusListener();
}
if (domainObject.hasCapability("status")) {
statusListener = domainObject.getCapability("status").listen(showInspector);
}
return statusListener;
}
var domainObject = navigationService.getNavigation();
if (domainObject) {
attachStatusListener(domainObject);
}
navigationService.addListener(attachStatusListener);
$scope.$on("$destroy", function () {
statusListener();
navigationService.removeListener(attachStatusListener);
});
}
InspectorPaneController.prototype = Object.create(PaneController.prototype);
return InspectorPaneController;
}
);

View File

@ -20,21 +20,40 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
export default class ClearDataAction { /**
constructor(openmct, appliesToObjects) { * Module defining MenuArrowController. Created by shale on 06/30/2015.
this.name = 'Clear Data for Object'; */
this.description = 'Clears current data for object, unsubscribes and resubscribes to data'; define(
this.cssClass = 'icon-clear-data'; [],
function () {
this._openmct = openmct; /**
this._appliesToObjects = appliesToObjects; * A left-click on the menu arrow should display a
* context menu. This controller launches the context
* menu.
* @memberof platform/commonUI/browse
* @constructor
*/
function MenuArrowController($scope) {
this.$scope = $scope;
} }
invoke(objectPath) {
this._openmct.objectViews.emit('clearData', objectPath[0]);
}
appliesTo(objectPath) {
let contextualDomainObject = objectPath[0];
return this._appliesToObjects.filter(type => contextualDomainObject.type === type).length; /**
} * Show a context menu for the domain object in this scope.
*
* @param event the browser event which caused this (used to
* position the menu)
*/
MenuArrowController.prototype.showMenu = function (event) {
var actionContext = {
key: 'menu',
domainObject: this.$scope.domainObject,
event: event
};
this.$scope.domainObject.getCapability('action').perform(actionContext);
};
return MenuArrowController;
} }
);

View File

@ -0,0 +1,92 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
[],
function () {
/**
* Controller to provide the ability to inline edit an object name.
*
* @constructor
* @memberof platform/commonUI/browse
*/
function ObjectHeaderController($scope) {
this.$scope = $scope;
this.domainObject = $scope.domainObject;
this.editable = this.allowEdit();
}
/**
* Updates the object name on blur and enter keypress events.
*
* @param event the mouse event
*/
ObjectHeaderController.prototype.updateName = function (event) {
if (!event || !event.currentTarget) {
return;
}
if (event.type === 'blur') {
this.updateModel(event);
} else if (event.which === 13) {
this.updateModel(event);
event.currentTarget.blur();
window.getSelection().removeAllRanges();
}
};
/**
* Updates the model.
*
* @param event the mouse event
* @param private
*/
ObjectHeaderController.prototype.updateModel = function (event) {
var name = event.currentTarget.textContent.replace(/\n/g, ' ');
if (name.length === 0) {
name = "Unnamed " + this.domainObject.getCapability("type").typeDef.name;
event.currentTarget.textContent = name;
}
if (name !== this.domainObject.getModel().name) {
this.domainObject.getCapability('mutation').mutate(function (model) {
model.name = name;
});
}
};
/**
* Checks if the domain object is editable.
*
* @private
* @return true if object is editable
*/
ObjectHeaderController.prototype.allowEdit = function () {
var type = this.domainObject && this.domainObject.getCapability('type');
return !!(type && type.hasFeature('creation'));
};
return ObjectHeaderController;
}
);

View File

@ -0,0 +1,88 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
[],
function () {
var navigationListenerAdded = false;
/**
* Controller to provide the ability to show/hide the tree in
* Browse mode.
* @constructor
* @memberof platform/commonUI/browse
*/
function PaneController($scope, agentService, $window, $location, $attrs, navigationService) {
var self = this;
this.agentService = agentService;
var hideParameterPresent = $location.search().hasOwnProperty($attrs.hideParameter);
if ($attrs.hideParameter && hideParameterPresent) {
this.state = false;
$location.search($attrs.hideParameter, undefined);
} else {
this.state = true;
}
/**
* Callback to invoke when any selection occurs in the tree.
* This controller can be passed in as the `parameters` object
* to the tree representation.
*
* @property {Function} callback
* @memberof platform/commonUI/browse.PaneController#
*/
this.callback = function () {
// Note that, since this is a callback to pass, this is not
// declared as a method but as a property which happens to
// be a function.
if (agentService.isPhone() && agentService.isPortrait()) {
// On phones, trees should collapse in portrait mode
// when something is navigated-to.
self.state = false;
}
};
if (navigationService && navigationService.addListener && !navigationListenerAdded) {
navigationService.addListener(this.callback);
navigationListenerAdded = true;
}
}
/**
* Toggle the visibility of the pane.
*/
PaneController.prototype.toggle = function () {
this.state = !this.state;
};
/**
* Get the desired visibility state of the pane.
* @returns {boolean} true when visible
*/
PaneController.prototype.visible = function () {
return !!this.state;
};
return PaneController;
}
);

View File

@ -20,16 +20,32 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
import PreviewAction from './PreviewAction'; define(
[],
function () {
export default class ViewHistoricalDataAction extends PreviewAction { /**
constructor(openmct) { * Updates the title of the current window to reflect the name
super(openmct); * of the currently navigated-to domain object.
* @memberof platform/commonUI/browse
* @constructor
*/
function WindowTitler(navigationService, $rootScope, $document) {
// Look up name of the navigated domain object...
function getNavigatedObjectName() {
var navigatedObject = navigationService.getNavigation();
return navigatedObject && navigatedObject.getModel().name;
}
this.name = 'View Historical Data'; // Set the window title...
this.key = 'viewHistoricalData'; function setTitle(name) {
this.description = 'View Historical Data in a Table or Plot'; $document[0].title = name;
this.cssClass = 'icon-eye-open';
this.hideInDefaultMenu = true;
} }
// Watch the former, and invoke the latter
$rootScope.$watch(getNavigatedObjectName, setTitle);
} }
return WindowTitler;
}
);

View File

@ -0,0 +1,266 @@
/*****************************************************************************
* 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 console*/
/**
* MCTRepresentationSpec. Created by vwoeltje on 11/6/14.
*/
define(
[
"../src/BrowseController",
"../src/navigation/NavigationService"
],
function (
BrowseController,
NavigationService
) {
describe("The browse controller", function () {
var mockScope,
mockRoute,
mockLocation,
mockObjectService,
mockNavigationService,
mockRootObject,
mockUrlService,
mockDefaultRootObject,
mockOtherDomainObject,
mockNextObject,
testDefaultRoot,
controller;
function waitsForNavigation() {
return new Promise(function (resolve) {
mockNavigationService.setNavigation.and.callFake(function (obj) {
var returnValue;
try {
returnValue = NavigationService.prototype.setNavigation.call(mockNavigationService, obj);
} catch (err) {
console.error(err);
//Not rejecting because 'setNavigation' has been called, which is what's being tested here.
//Rejecting will fail tests.
}
resolve();
return returnValue;
});
});
}
function instantiateController() {
controller = new BrowseController(
mockScope,
mockRoute,
mockLocation,
mockObjectService,
mockNavigationService,
mockUrlService,
testDefaultRoot
);
}
beforeEach(function () {
testDefaultRoot = "some-root-level-domain-object";
mockScope = jasmine.createSpyObj(
"$scope",
["$on", "$watch"]
);
mockRoute = { current: { params: {}, pathParams: {} } };
mockUrlService = jasmine.createSpyObj(
"urlService",
["urlForLocation"]
);
mockUrlService.urlForLocation.and.callFake(function (mode, object) {
if (object === mockDefaultRootObject) {
return [mode, testDefaultRoot].join('/');
}
if (object === mockOtherDomainObject) {
return [mode, 'other'].join('/');
}
if (object === mockNextObject) {
return [mode, testDefaultRoot, 'next'].join('/');
}
throw new Error('Tried to get url for unexpected object');
});
mockLocation = jasmine.createSpyObj(
"$location",
["path"]
);
mockObjectService = jasmine.createSpyObj(
"objectService",
["getObjects"]
);
mockNavigationService = new NavigationService({});
[
"getNavigation",
"setNavigation",
"addListener",
"removeListener"
].forEach(function (method) {
spyOn(mockNavigationService, method)
.and.callThrough();
});
mockRootObject = jasmine.createSpyObj(
"rootObjectContainer",
["getId", "getCapability", "getModel", "useCapability", "hasCapability"]
);
mockDefaultRootObject = jasmine.createSpyObj(
"defaultRootObject",
["getId", "getCapability", "getModel", "useCapability", "hasCapability"]
);
mockOtherDomainObject = jasmine.createSpyObj(
"otherDomainObject",
["getId", "getCapability", "getModel", "useCapability", "hasCapability"]
);
mockNextObject = jasmine.createSpyObj(
"nestedDomainObject",
["getId", "getCapability", "getModel", "useCapability", "hasCapability"]
);
mockObjectService.getObjects.and.returnValue(Promise.resolve({
ROOT: mockRootObject
}));
mockRootObject.useCapability.and.returnValue(Promise.resolve([
mockOtherDomainObject,
mockDefaultRootObject
]));
mockRootObject.hasCapability.and.returnValue(true);
mockDefaultRootObject.useCapability.and.returnValue(Promise.resolve([
mockNextObject
]));
mockDefaultRootObject.hasCapability.and.returnValue(true);
mockOtherDomainObject.hasCapability.and.returnValue(false);
mockNextObject.useCapability.and.returnValue(undefined);
mockNextObject.hasCapability.and.returnValue(false);
mockNextObject.getId.and.returnValue("next");
mockDefaultRootObject.getId.and.returnValue(testDefaultRoot);
instantiateController();
return waitsForNavigation();
});
it("uses composition to set the navigated object, if there is none", function () {
instantiateController();
return waitsForNavigation().then(function () {
expect(mockNavigationService.setNavigation)
.toHaveBeenCalledWith(mockDefaultRootObject);
});
});
it("navigates to a root-level object, even when default path is not found", function () {
mockDefaultRootObject.getId
.and.returnValue("something-other-than-the-" + testDefaultRoot);
instantiateController();
return waitsForNavigation().then(function () {
expect(mockNavigationService.setNavigation)
.toHaveBeenCalledWith(mockDefaultRootObject);
});
});
it("does not try to override navigation", function () {
mockNavigationService.getNavigation.and.returnValue(mockDefaultRootObject);
instantiateController();
return waitsForNavigation().then(function () {
expect(mockScope.navigatedObject).toBe(mockDefaultRootObject);
});
});
it("updates scope when navigated object changes", function () {
// Should have registered a listener - call it
mockNavigationService.addListener.calls.mostRecent().args[0](
mockOtherDomainObject
);
expect(mockScope.navigatedObject).toEqual(mockOtherDomainObject);
});
it("releases its navigation listener when its scope is destroyed", function () {
expect(mockScope.$on).toHaveBeenCalledWith(
"$destroy",
jasmine.any(Function)
);
mockScope.$on.calls.mostRecent().args[1]();
// Should remove the listener it added earlier
expect(mockNavigationService.removeListener).toHaveBeenCalledWith(
mockNavigationService.addListener.calls.mostRecent().args[0]
);
});
it("uses route parameters to choose initially-navigated object", function () {
mockRoute.current.params.ids = testDefaultRoot + "/next";
instantiateController();
return waitsForNavigation().then(function () {
expect(mockScope.navigatedObject).toBe(mockNextObject);
expect(mockNavigationService.setNavigation)
.toHaveBeenCalledWith(mockNextObject);
});
});
it("handles invalid IDs by going as far as possible", function () {
// Idea here is that if we get a bad path of IDs,
// browse controller should traverse down it until
// it hits an invalid ID.
mockRoute.current.params.ids = testDefaultRoot + "/junk";
instantiateController();
return waitsForNavigation().then(function () {
expect(mockScope.navigatedObject).toBe(mockDefaultRootObject);
expect(mockNavigationService.setNavigation)
.toHaveBeenCalledWith(mockDefaultRootObject);
});
});
it("handles compositionless objects by going as far as possible", function () {
// Idea here is that if we get a path which passes
// through an object without a composition, browse controller
// should stop at it since remaining IDs cannot be loaded.
mockRoute.current.params.ids = testDefaultRoot + "/next/junk";
instantiateController();
return waitsForNavigation().then(function () {
expect(mockScope.navigatedObject).toBe(mockNextObject);
expect(mockNavigationService.setNavigation)
.toHaveBeenCalledWith(mockNextObject);
});
});
it("updates the displayed route to reflect current navigation", function () {
// In order to trigger a route update and not a route change,
// the current route must be updated before location.path is
// called.
expect(mockRoute.current.pathParams.ids)
.not
.toBe(testDefaultRoot + '/next');
mockLocation.path.and.callFake(function () {
expect(mockRoute.current.pathParams.ids)
.toBe(testDefaultRoot + '/next');
});
mockNavigationService.addListener.calls.mostRecent().args[0](
mockNextObject
);
expect(mockLocation.path).toHaveBeenCalledWith(
'/browse/' + testDefaultRoot + '/next'
);
});
});
}
);

View File

@ -0,0 +1,93 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
["../src/BrowseObjectController"],
function (BrowseObjectController) {
describe("The browse object controller", function () {
var mockScope,
mockLocation,
mockRoute,
controller;
// Utility function; look for a $watch on scope and fire it
function fireWatch(expr, value) {
mockScope.$watch.calls.all().forEach(function (call) {
if (call.args[0] === expr) {
call.args[1](value);
}
});
}
beforeEach(function () {
mockScope = jasmine.createSpyObj(
"$scope",
["$on", "$watch"]
);
mockRoute = { current: { params: {} } };
mockLocation = jasmine.createSpyObj(
"$location",
["path", "search"]
);
mockLocation.search.and.returnValue({});
controller = new BrowseObjectController(
mockScope,
mockLocation,
mockRoute
);
});
it("updates query parameters when selected view changes", function () {
fireWatch("representation.selected.key", "xyz");
expect(mockLocation.search).toHaveBeenCalledWith('view', "xyz");
// Allows the path index to be checked
// prior to setting $route.current
mockLocation.path.and.returnValue("/browse/");
});
it("sets the active view from query parameters", function () {
var mockDomainObject = jasmine.createSpyObj(
"domainObject",
['getId', 'getModel', 'getCapability', 'useCapability']
),
testViews = [
{ key: 'abc' },
{ key: 'def', someKey: 'some value' },
{ key: 'xyz' }
];
mockDomainObject.useCapability.and.callFake(function (c) {
return (c === 'view') && testViews;
});
mockLocation.search.and.returnValue({ view: 'def' });
fireWatch('domainObject', mockDomainObject);
expect(mockScope.representation.selected)
.toEqual(testViews[1]);
});
});
}
);

View File

@ -0,0 +1,103 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web 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 Web 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/InspectorPaneController"],
function (InspectorPaneController) {
describe("The InspectorPaneController", function () {
var mockScope,
mockAgentService,
mockDomainObject,
mockWindow,
mockStatusCapability,
mockNavigationService,
mockNavigationUnlistener,
mockStatusUnlistener,
controller,
mockLocation,
mockAttrs;
beforeEach(function () {
mockScope = jasmine.createSpyObj("$scope", ["$on"]);
mockWindow = jasmine.createSpyObj("$window", ["open"]);
mockAgentService = jasmine.createSpyObj(
"agentService",
["isMobile", "isPhone", "isTablet", "isPortrait", "isLandscape"]
);
mockNavigationUnlistener = jasmine.createSpy("navigationUnlistener");
mockNavigationService = jasmine.createSpyObj(
"navigationService",
["getNavigation", "addListener"]
);
mockNavigationService.addListener.and.returnValue(mockNavigationUnlistener);
mockStatusUnlistener = jasmine.createSpy("statusUnlistener");
mockStatusCapability = jasmine.createSpyObj(
"statusCapability",
["listen"]
);
mockStatusCapability.listen.and.returnValue(mockStatusUnlistener);
mockDomainObject = jasmine.createSpyObj(
'domainObject',
[
'getId',
'getModel',
'getCapability',
'hasCapability'
]
);
mockDomainObject.getId.and.returnValue("domainObject");
mockDomainObject.getModel.and.returnValue({});
mockDomainObject.hasCapability.and.returnValue(true);
mockDomainObject.getCapability.and.returnValue(mockStatusCapability);
mockLocation = jasmine.createSpyObj('location', ['search']);
mockLocation.search.and.returnValue({});
mockAttrs = {};
controller = new InspectorPaneController(mockScope, mockAgentService, mockWindow, mockNavigationService, mockLocation, mockAttrs);
});
it("listens for changes to navigation and attaches a status" +
" listener", function () {
expect(mockNavigationService.addListener).toHaveBeenCalledWith(jasmine.any(Function));
mockNavigationService.addListener.calls.mostRecent().args[0](mockDomainObject);
expect(mockStatusCapability.listen).toHaveBeenCalledWith(jasmine.any(Function));
});
it("if hidden, shows the inspector when domain object switches to" +
" edit mode", function () {
controller.toggle();
// test pre-condition that inspector is hidden
expect(controller.visible()).toBe(false);
mockNavigationService.addListener.calls.mostRecent().args[0](mockDomainObject);
mockStatusCapability.listen.calls.mostRecent().args[0](["editing"]);
expect(controller.visible()).toBe(true);
});
});
}
);

View File

@ -0,0 +1,79 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/**
* MenuArrowControllerSpec. Created by shale on 07/02/2015.
*/
define(
["../src/MenuArrowController"],
function (MenuArrowController) {
describe("The menu arrow controller ", function () {
var mockScope,
mockDomainObject,
mockEvent,
mockContextMenuAction,
mockActionContext,
controller;
beforeEach(function () {
mockScope = jasmine.createSpyObj(
"$scope",
[""]
);
mockDomainObject = jasmine.createSpyObj(
"domainObject",
["getCapability"]
);
mockEvent = jasmine.createSpyObj(
"event",
["preventDefault"]
);
mockContextMenuAction = jasmine.createSpyObj(
"action",
["perform", "getActions"]
);
mockActionContext = jasmine.createSpyObj(
"actionContext",
[""]
);
mockActionContext.domainObject = mockDomainObject;
mockActionContext.event = mockEvent;
mockScope.domainObject = mockDomainObject;
mockDomainObject.getCapability.and.returnValue(mockContextMenuAction);
mockContextMenuAction.perform.and.returnValue(jasmine.any(Function));
controller = new MenuArrowController(mockScope);
});
it("calls the context menu action when clicked", function () {
// Simulate a click on the menu arrow
controller.showMenu(mockEvent);
// Expect the menu action to be performed
expect(mockDomainObject.getCapability).toHaveBeenCalledWith('action');
expect(mockContextMenuAction.perform).toHaveBeenCalled();
});
});
}
);

View File

@ -0,0 +1,137 @@
/*****************************************************************************
* 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/ObjectHeaderController"],
function (ObjectHeaderController) {
describe("The object header controller", function () {
var mockScope,
mockDomainObject,
mockCapabilities,
mockMutationCapability,
mockTypeCapability,
mockEvent,
mockCurrentTarget,
model,
controller;
beforeEach(function () {
mockMutationCapability = jasmine.createSpyObj("mutation", ["mutate"]);
mockTypeCapability = jasmine.createSpyObj("type", ["typeDef", "hasFeature"]);
mockTypeCapability.typeDef = { name: ""};
mockTypeCapability.hasFeature.and.callFake(function (feature) {
return feature === 'creation';
});
mockCapabilities = {
mutation: mockMutationCapability,
type: mockTypeCapability
};
model = {
name: "Test name"
};
mockDomainObject = jasmine.createSpyObj("domainObject", ["getCapability", "getModel"]);
mockDomainObject.getModel.and.returnValue(model);
mockDomainObject.getCapability.and.callFake(function (key) {
return mockCapabilities[key];
});
mockScope = {
domainObject: mockDomainObject
};
mockCurrentTarget = jasmine.createSpyObj("currentTarget", ["blur", "textContent"]);
mockCurrentTarget.blur.and.returnValue(mockCurrentTarget);
mockEvent = {
which: {},
type: {},
currentTarget: mockCurrentTarget
};
controller = new ObjectHeaderController(mockScope);
});
it("updates the model with new name on blur", function () {
mockEvent.type = "blur";
mockCurrentTarget.textContent = "New name";
controller.updateName(mockEvent);
expect(mockMutationCapability.mutate).toHaveBeenCalled();
});
it("updates the model with a default for blank names", function () {
mockEvent.type = "blur";
mockCurrentTarget.textContent = "";
controller.updateName(mockEvent);
expect(mockCurrentTarget.textContent.length).not.toEqual(0);
expect(mockMutationCapability.mutate).toHaveBeenCalled();
});
it("does not update the model if the same name", function () {
mockEvent.type = "blur";
mockCurrentTarget.textContent = mockDomainObject.getModel().name;
controller.updateName(mockEvent);
expect(mockMutationCapability.mutate).not.toHaveBeenCalled();
});
it("updates the model on enter keypress event only", function () {
mockCurrentTarget.textContent = "New name";
controller.updateName(mockEvent);
expect(mockMutationCapability.mutate).not.toHaveBeenCalled();
mockEvent.which = 13;
controller.updateName(mockEvent);
expect(mockMutationCapability.mutate).toHaveBeenCalledWith(jasmine.any(Function));
mockMutationCapability.mutate.calls.mostRecent().args[0](model);
expect(mockDomainObject.getModel().name).toBe("New name");
});
it("blurs the field on enter key press", function () {
mockCurrentTarget.textContent = "New name";
mockEvent.which = 13;
controller.updateName(mockEvent);
expect(mockEvent.currentTarget.blur).toHaveBeenCalled();
});
it("allows editting name when object is creatable", function () {
expect(controller.allowEdit()).toBe(true);
});
it("disallows editting name when object is non-creatable", function () {
mockTypeCapability.hasFeature.and.returnValue(false);
expect(controller.allowEdit()).toBe(false);
});
});
}
);

View File

@ -0,0 +1,106 @@
/*****************************************************************************
* 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/PaneController"],
function (PaneController) {
describe("The PaneController", function () {
var mockScope,
mockAgentService,
mockWindow,
controller,
mockLocation,
mockAttrs;
// We want to reinstantiate for each test case
// because device state can influence constructor-time behavior
function instantiateController() {
return new PaneController(
mockScope,
mockAgentService,
mockWindow,
mockLocation,
mockAttrs
);
}
beforeEach(function () {
mockScope = jasmine.createSpyObj("$scope", ["$on"]);
mockAgentService = jasmine.createSpyObj(
"agentService",
["isMobile", "isPhone", "isTablet", "isPortrait", "isLandscape"]
);
mockWindow = jasmine.createSpyObj("$window", ["open"]);
mockLocation = jasmine.createSpyObj('location', ['search']);
mockLocation.search.and.returnValue({});
mockAttrs = {};
});
it("is initially visible", function () {
expect(instantiateController().visible()).toBeTruthy();
});
it("allows visibility to be toggled", function () {
controller = instantiateController();
controller.toggle();
expect(controller.visible()).toBeFalsy();
controller.toggle();
expect(controller.visible()).toBeTruthy();
});
it("collapses on navigation changes on portrait-oriented phones", function () {
mockAgentService.isMobile.and.returnValue(true);
mockAgentService.isPhone.and.returnValue(true);
mockAgentService.isPortrait.and.returnValue(true);
controller = instantiateController();
expect(controller.visible()).toBeTruthy();
// Simulate a change from the tree by invoking controller's
controller.callback();
// Tree should have collapsed
expect(controller.visible()).toBeFalsy();
});
describe("specifying hideParameter", function () {
beforeEach(function () {
mockAttrs = {hideParameter: 'hideTree'};
});
it("sets pane state to false when in location.search", function () {
mockLocation.search.and.returnValue({'hideTree': true});
expect(instantiateController().visible()).toBe(false);
expect(mockLocation.search).toHaveBeenCalledWith('hideTree', undefined);
});
it("sets state to true when not found in location.search", function () {
mockLocation.search.and.returnValue({});
expect(instantiateController().visible()).toBe(true);
expect(mockLocation.search).not.toHaveBeenCalledWith('hideTree', undefined);
});
});
});
}
);

View File

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

View File

@ -65,8 +65,7 @@ define([
"depends": [ "depends": [
"$document", "$document",
"$compile", "$compile",
"$rootScope", "$rootScope"
"$timeout"
] ]
} }
], ],

View File

@ -9,7 +9,6 @@
ng-model="ngModel" ng-model="ngModel"
ng-show="ngModel.progressPerc !== undefined"></mct-include> ng-show="ngModel.progressPerc !== undefined"></mct-include>
</div> </div>
</div>
<div class="c-overlay__button-bar"> <div class="c-overlay__button-bar">
<button ng-repeat="dialogOption in ngModel.options" <button ng-repeat="dialogOption in ngModel.options"
class="c-button" class="c-button"
@ -23,3 +22,4 @@
</button> </button>
</div> </div>
</div> </div>
</div>

View File

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

View File

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

View File

@ -27,6 +27,7 @@ define([
"./src/actions/EditAndComposeAction", "./src/actions/EditAndComposeAction",
"./src/actions/EditAction", "./src/actions/EditAction",
"./src/actions/PropertiesAction", "./src/actions/PropertiesAction",
"./src/actions/RemoveAction",
"./src/actions/SaveAction", "./src/actions/SaveAction",
"./src/actions/SaveAndStopEditingAction", "./src/actions/SaveAndStopEditingAction",
"./src/actions/SaveAsAction", "./src/actions/SaveAsAction",
@ -57,6 +58,7 @@ define([
EditAndComposeAction, EditAndComposeAction,
EditAction, EditAction,
PropertiesAction, PropertiesAction,
RemoveAction,
SaveAction, SaveAction,
SaveAndStopEditingAction, SaveAndStopEditingAction,
SaveAsAction, SaveAsAction,
@ -156,6 +158,18 @@ define([
"dialogService" "dialogService"
] ]
}, },
{
"key": "remove",
"category": "legacy",
"implementation": RemoveAction,
"cssClass": "icon-trash",
"name": "Remove",
"description": "Remove this object from its containing object.",
"depends": [
"openmct",
"navigationService"
]
},
{ {
"key": "save-and-stop-editing", "key": "save-and-stop-editing",
"category": "save", "category": "save",

View File

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

View File

@ -0,0 +1,131 @@
/*****************************************************************************
* 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 RemoveAction. Created by vwoeltje on 11/17/14.
*/
define([], function () {
/**
* Construct an action which will remove the provided object manifestation.
* The object will be removed from its parent's composition; the parent
* is looked up via the "context" capability (so this will be the
* immediate ancestor by which this specific object was reached.)
*
* @param {DialogService} dialogService a service which will show the dialog
* @param {NavigationService} navigationService a service that maintains the current navigation state
* @param {ActionContext} context the context in which this action is performed
* @memberof platform/commonUI/edit
* @constructor
* @implements {Action}
*/
function RemoveAction(openmct, navigationService, context) {
this.domainObject = (context || {}).domainObject;
this.openmct = openmct;
this.navigationService = navigationService;
}
/**
* Perform this action.
*/
RemoveAction.prototype.perform = function () {
var dialog,
domainObject = this.domainObject,
navigationService = this.navigationService;
/*
* Check whether an object ID matches the ID of the object being
* removed (used to filter a parent's composition to handle the
* removal.)
*/
function isNotObject(otherObjectId) {
return otherObjectId !== domainObject.getId();
}
/*
* Mutate a parent object such that it no longer contains the object
* which is being removed.
*/
function doMutate(model) {
model.composition = model.composition.filter(isNotObject);
}
/*
* Checks current object and ascendants of current
* object with object being removed, if the current
* object or any in the current object's path is being removed,
* navigate back to parent of removed object.
*/
function checkObjectNavigation(object, parentObject) {
// Traverse object starts at current location
var traverseObject = (navigationService).getNavigation(),
context;
// Stop when object is not defined (above ROOT)
while (traverseObject) {
// If object currently traversed to is object being removed
// navigate to parent of current object and then exit loop
if (traverseObject.getId() === object.getId()) {
navigationService.setNavigation(parentObject);
return;
}
// Traverses to parent of current object, moving
// up the ascendant path
context = traverseObject.getCapability('context');
traverseObject = context && context.getParent();
}
}
/*
* Remove the object from its parent, as identified by its context
* capability. Based on object's location and selected object's location
* user may be navigated to existing parent object
*/
function removeFromContext() {
var contextCapability = domainObject.getCapability('context'),
parent = contextCapability.getParent();
// If currently within path of removed object(s),
// navigates to existing object up tree
checkObjectNavigation(domainObject, parent);
return parent.useCapability('mutation', doMutate);
}
removeFromContext();
};
// Object needs to have a parent for Remove to be applicable
RemoveAction.appliesTo = function (context) {
var object = (context || {}).domainObject,
contextCapability = object && object.getCapability("context"),
parent = contextCapability && contextCapability.getParent(),
parentType = parent && parent.getCapability('type'),
parentCreatable = parentType && parentType.hasFeature('creation');
// Only creatable types should be modifiable
return parent !== undefined &&
Array.isArray(parent.getModel().composition) &&
parentCreatable;
};
return RemoveAction;
});

View File

@ -162,6 +162,9 @@ function (
function saveAfterClone(clonedObject) { function saveAfterClone(clonedObject) {
return this.openmct.editor.save().then(() => { return this.openmct.editor.save().then(() => {
// Force mutation for search indexing // Force mutation for search indexing
clonedObject.useCapability('mutation', (model) => {
return model;
});
return clonedObject; return clonedObject;
}) })
} }
@ -170,14 +173,6 @@ function (
return fetchObject(clonedObject.getId()) return fetchObject(clonedObject.getId())
} }
function indexForSearch(savedObject) {
savedObject.useCapability('mutation', (model) => {
return model;
});
return savedObject;
}
function onSuccess(object) { function onSuccess(object) {
self.notificationService.info("Save Succeeded"); self.notificationService.info("Save Succeeded");
return object; return object;
@ -199,7 +194,6 @@ function (
.then(undirtyOriginals) .then(undirtyOriginals)
.then(saveAfterClone) .then(saveAfterClone)
.then(finishEditing) .then(finishEditing)
.then(indexForSearch)
.then(hideBlockingDialog) .then(hideBlockingDialog)
.then(onSuccess) .then(onSuccess)
.catch(onFailure); .catch(onFailure);

View File

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

View File

@ -71,12 +71,6 @@ define(
openmct.editor.cancel(); openmct.editor.cancel();
} }
function isFirstViewEditable(domainObject) {
let firstView = openmct.objectViews.get(domainObject)[0];
return firstView && firstView.canEdit && firstView.canEdit(domainObject);
}
function navigateAndEdit(object) { function navigateAndEdit(object) {
let objectPath = object.getCapability('context').getPath(), let objectPath = object.getCapability('context').getPath(),
url = '#/browse/' + objectPath url = '#/browse/' + objectPath
@ -88,10 +82,8 @@ define(
window.location.href = url; window.location.href = url;
if (isFirstViewEditable(object.useCapability('adapter'))) {
openmct.editor.edit(); openmct.editor.edit();
} }
}
newModel.type = this.type.getKey(); newModel.type = this.type.getKey();
newModel.location = this.parent.getId(); newModel.location = this.parent.getId();

View File

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

View File

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

View File

@ -0,0 +1,255 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
["../../src/actions/RemoveAction"],
function (RemoveAction) {
describe("The Remove action", function () {
var action,
actionContext,
capabilities,
mockContext,
mockOverlayAPI,
mockDomainObject,
mockMutation,
mockNavigationService,
mockParent,
mockType,
model;
beforeEach(function () {
mockDomainObject = jasmine.createSpyObj(
"domainObject",
["getId", "getCapability", "getModel"]
);
mockMutation = jasmine.createSpyObj("mutation", ["invoke"]);
mockType = jasmine.createSpyObj("type", ["hasFeature"]);
mockType.hasFeature.and.returnValue(true);
capabilities = {
mutation: mockMutation,
type: mockType
};
model = {
composition: ["a", "test", "b"]
};
mockParent = {
getModel: function () {
return model;
},
getCapability: function (k) {
return capabilities[k];
},
useCapability: function (k, v) {
return capabilities[k].invoke(v);
}
};
mockOverlayAPI = jasmine.createSpyObj(
"overlayAPI",
["dialog"]
);
mockNavigationService = jasmine.createSpyObj(
"navigationService",
[
"getNavigation",
"setNavigation",
"addListener",
"removeListener"
]
);
mockNavigationService.getNavigation.and.returnValue(mockDomainObject);
mockContext = jasmine.createSpyObj("context", ["getParent"]);
mockContext.getParent.and.returnValue(mockParent);
mockDomainObject.getId.and.returnValue("test");
mockDomainObject.getCapability.and.returnValue(mockContext);
mockDomainObject.getModel.and.returnValue({name: 'test object'});
mockContext.getParent.and.returnValue(mockParent);
mockType.hasFeature.and.returnValue(true);
actionContext = { domainObject: mockDomainObject };
action = new RemoveAction({overlays: mockOverlayAPI}, mockNavigationService, actionContext);
});
it("only applies to objects with parents", function () {
expect(RemoveAction.appliesTo(actionContext)).toBeTruthy();
mockContext.getParent.and.returnValue(undefined);
expect(RemoveAction.appliesTo(actionContext)).toBeFalsy();
// Also verify that creatability was checked
expect(mockType.hasFeature).toHaveBeenCalledWith('creation');
});
it("shows a blocking message dialog", function () {
mockParent = jasmine.createSpyObj(
"parent",
["getModel", "getCapability", "useCapability"]
);
action.perform();
expect(mockOverlayAPI.dialog).toHaveBeenCalled();
// Also check that no mutation happens at this point
expect(mockParent.useCapability).not.toHaveBeenCalledWith("mutation", jasmine.any(Function));
});
describe("after the remove callback is triggered", function () {
var mockChildContext,
mockChildObject,
mockDialogHandle,
mockGrandchildContext,
mockGrandchildObject,
mockRootContext,
mockRootObject;
beforeEach(function () {
mockChildObject = jasmine.createSpyObj(
"domainObject",
["getId", "getCapability"]
);
mockDialogHandle = jasmine.createSpyObj(
"dialogHandle",
["dismiss"]
);
mockGrandchildObject = jasmine.createSpyObj(
"domainObject",
["getId", "getCapability"]
);
mockRootObject = jasmine.createSpyObj(
"domainObject",
["getId", "getCapability"]
);
mockChildContext = jasmine.createSpyObj("context", ["getParent"]);
mockGrandchildContext = jasmine.createSpyObj("context", ["getParent"]);
mockRootContext = jasmine.createSpyObj("context", ["getParent"]);
mockOverlayAPI.dialog.and.returnValue(mockDialogHandle);
});
it("mutates the parent when performed", function () {
action.perform();
mockOverlayAPI.dialog.calls.mostRecent().args[0]
.buttons[0].callback();
expect(mockMutation.invoke)
.toHaveBeenCalledWith(jasmine.any(Function));
});
it("changes composition from its mutation function", function () {
var mutator, result;
action.perform();
mockOverlayAPI.dialog.calls.mostRecent().args[0]
.buttons[0].callback();
mutator = mockMutation.invoke.calls.mostRecent().args[0];
result = mutator(model);
// Should not have cancelled the mutation
expect(result).not.toBe(false);
// Simulate mutate's behavior (remove can either return a
// new model or modify this one in-place)
result = result || model;
// Should have removed "test" - that was our
// mock domain object's id.
expect(result.composition).toEqual(["a", "b"]);
});
it("removes parent of object currently navigated to", function () {
// Navigates to child object
mockNavigationService.getNavigation.and.returnValue(mockChildObject);
// Test is id of object being removed
// Child object has different id
mockDomainObject.getId.and.returnValue("test");
mockChildObject.getId.and.returnValue("not test");
// Sets context for the child and domainObject
mockDomainObject.getCapability.and.returnValue(mockContext);
mockChildObject.getCapability.and.returnValue(mockChildContext);
// Parents of child and domainObject are set
mockContext.getParent.and.returnValue(mockParent);
mockChildContext.getParent.and.returnValue(mockDomainObject);
mockType.hasFeature.and.returnValue(true);
action.perform();
mockOverlayAPI.dialog.calls.mostRecent().args[0]
.buttons[0].callback();
// Expects navigation to parent of domainObject (removed object)
expect(mockNavigationService.setNavigation).toHaveBeenCalledWith(mockParent);
});
it("checks if removing object not in ascendent path (reaches ROOT)", function () {
// Navigates to grandchild of ROOT
mockNavigationService.getNavigation.and.returnValue(mockGrandchildObject);
// domainObject (grandparent) is set as ROOT, child and grandchild
// are set objects not being removed
mockDomainObject.getId.and.returnValue("test 1");
mockRootObject.getId.and.returnValue("ROOT");
mockChildObject.getId.and.returnValue("not test 2");
mockGrandchildObject.getId.and.returnValue("not test 3");
// Sets context for the grandchild, child, and domainObject
mockRootObject.getCapability.and.returnValue(mockRootContext);
mockChildObject.getCapability.and.returnValue(mockChildContext);
mockGrandchildObject.getCapability.and.returnValue(mockGrandchildContext);
// Parents of grandchild and child are set
mockChildContext.getParent.and.returnValue(mockRootObject);
mockGrandchildContext.getParent.and.returnValue(mockChildObject);
mockType.hasFeature.and.returnValue(true);
action.perform();
mockOverlayAPI.dialog.calls.mostRecent().args[0]
.buttons[0].callback();
// Expects no navigation to occur
expect(mockNavigationService.setNavigation).not.toHaveBeenCalled();
});
});
});
}
);

View File

@ -25,7 +25,7 @@ define(
["../../src/actions/SaveAsAction"], ["../../src/actions/SaveAsAction"],
function (SaveAsAction) { function (SaveAsAction) {
xdescribe("The Save As action", function () { describe("The Save As action", function () {
var mockDomainObject, var mockDomainObject,
mockClonedObject, mockClonedObject,
mockEditorCapability, mockEditorCapability,

View File

@ -24,7 +24,7 @@ define(
["../../src/capabilities/EditorCapability"], ["../../src/capabilities/EditorCapability"],
function (EditorCapability) { function (EditorCapability) {
xdescribe("The editor capability", function () { describe("The editor capability", function () {
var mockDomainObject, var mockDomainObject,
capabilities, capabilities,
mockParentObject, mockParentObject,

View File

@ -27,7 +27,7 @@ define(
["../../src/creation/CreateAction"], ["../../src/creation/CreateAction"],
function (CreateAction) { function (CreateAction) {
xdescribe("The create action", function () { describe("The create action", function () {
var mockType, var mockType,
mockParent, mockParent,
mockContext, mockContext,

View File

@ -27,7 +27,7 @@ define(
["../../src/creation/CreateWizard"], ["../../src/creation/CreateWizard"],
function (CreateWizard) { function (CreateWizard) {
xdescribe("The create wizard", function () { describe("The create wizard", function () {
var mockType, var mockType,
mockParent, mockParent,
mockProperties, mockProperties,

View File

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

View File

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

View File

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

View File

@ -26,7 +26,7 @@ define([
'zepto' 'zepto'
], function (TreeView, $) { ], function (TreeView, $) {
xdescribe("TreeView", function () { describe("TreeView", function () {
var mockGestureService, var mockGestureService,
mockGestureHandle, mockGestureHandle,
mockDomainObject, mockDomainObject,

View File

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

View File

@ -24,7 +24,7 @@ define(
['../src/NotificationIndicatorController'], ['../src/NotificationIndicatorController'],
function (NotificationIndicatorController) { function (NotificationIndicatorController) {
xdescribe("The notification indicator controller ", function () { describe("The notification indicator controller ", function () {
var mockNotificationService, var mockNotificationService,
mockScope, mockScope,
mockDialogService, mockDialogService,

View File

@ -23,7 +23,7 @@
define( define(
["../src/ComposeActionPolicy"], ["../src/ComposeActionPolicy"],
function (ComposeActionPolicy) { function (ComposeActionPolicy) {
xdescribe("The compose action policy", function () { describe("The compose action policy", function () {
var mockInjector, var mockInjector,
mockPolicyService, mockPolicyService,
mockTypes, mockTypes,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -42,7 +42,7 @@ define(
return promise; return promise;
} }
xdescribe("CopyService", function () { describe("CopyService", function () {
var policyService; var policyService;
beforeEach(function () { beforeEach(function () {

View File

@ -29,7 +29,7 @@ define(
], ],
function (LinkService, domainObjectFactory, ControlledPromise) { function (LinkService, domainObjectFactory, ControlledPromise) {
xdescribe("LinkService", function () { describe("LinkService", function () {
var linkService, var linkService,
mockPolicyService; mockPolicyService;

View File

@ -34,7 +34,7 @@ define(
ControlledPromise ControlledPromise
) { ) {
xdescribe("MoveService", function () { describe("MoveService", function () {
var moveService, var moveService,
policyService, policyService,

View File

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

View File

@ -31,7 +31,7 @@ define([
MCT, MCT,
$ $
) { ) {
xdescribe("The timer-following indicator", function () { describe("The timer-following indicator", function () {
var timerService; var timerService;
var openmct; var openmct;

View File

@ -30,7 +30,7 @@ define(
var MOCK_ELEMENT_TEMPLATE = var MOCK_ELEMENT_TEMPLATE =
'<div class="l-image-thumbs-wrapper"></div>'; '<div class="l-image-thumbs-wrapper"></div>';
xdescribe("The Imagery controller", function () { describe("The Imagery controller", function () {
var $scope, var $scope,
openmct, openmct,
oldDomainObject, oldDomainObject,

View File

@ -24,7 +24,7 @@ define(
["../src/MCTFileInput"], ["../src/MCTFileInput"],
function (MCTFileInput) { function (MCTFileInput) {
xdescribe("The mct-file-input directive", function () { describe("The mct-file-input directive", function () {
var mockScope, var mockScope,
mockFileInputService, mockFileInputService,

View File

@ -55,13 +55,13 @@ define([
FrameworkLayer.prototype.initializeApplication = function ( FrameworkLayer.prototype.initializeApplication = function (
angular, angular,
openmct, legacyRegistry,
logLevel logLevel
) { ) {
var $http = this.$http, var $http = this.$http,
$log = this.$log, $log = this.$log,
app = angular.module(Constants.MODULE_NAME, ["ngRoute"]), app = angular.module(Constants.MODULE_NAME, ["ngRoute"]),
loader = new BundleLoader($http, $log, openmct.legacyRegistry), loader = new BundleLoader($http, $log, legacyRegistry),
resolver = new BundleResolver( resolver = new BundleResolver(
new ExtensionResolver( new ExtensionResolver(
new ImplementationLoader({}), new ImplementationLoader({}),
@ -77,7 +77,7 @@ define([
), ),
bootstrapper = new ApplicationBootstrapper( bootstrapper = new ApplicationBootstrapper(
angular, angular,
openmct.element, window.document,
$log $log
), ),
initializer = new FrameworkInitializer( initializer = new FrameworkInitializer(

View File

@ -41,7 +41,7 @@ define(
function Main() { function Main() {
} }
Main.prototype.run = function (openmct) { Main.prototype.run = function (legacyRegistry) {
// Get a reference to Angular's injector, so we can get $http and $log // Get a reference to Angular's injector, so we can get $http and $log
// services, which are useful to the framework layer. // services, which are useful to the framework layer.
var injector = angular.injector(['ng']); var injector = angular.injector(['ng']);
@ -53,7 +53,7 @@ define(
} }
return injector.instantiate(['$http', '$log', FrameworkLayer]) return injector.instantiate(['$http', '$log', FrameworkLayer])
.initializeApplication(angular, openmct, logLevel()); .initializeApplication(angular, legacyRegistry, logLevel());
}; };
return Main; return Main;

View File

@ -72,6 +72,7 @@ define([
] ]
} }
}); });
openmct.legacyRegistry.enable('platform/import-export');
}; };
}; };
}); });

View File

@ -19,7 +19,8 @@
* this source code distribution or the Licensing information page available * this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define(['zepto', '../../../../src/api/objects/object-utils.js'], function ($, objectUtils) {
define(['zepto'], function ($) {
/** /**
* The ImportAsJSONAction is available from context menus and allows a user * The ImportAsJSONAction is available from context menus and allows a user
@ -60,39 +61,15 @@ define(['zepto', '../../../../src/api/objects/object-utils.js'], function ($, ob
ImportAsJSONAction.prototype.importObjectTree = function (objTree) { ImportAsJSONAction.prototype.importObjectTree = function (objTree) {
var parent = this.context.domainObject; var parent = this.context.domainObject;
var namespace = parent.useCapability('adapter').identifier.namespace; var tree = this.generateNewIdentifiers(objTree);
var tree = this.generateNewIdentifiers(objTree, namespace);
var rootId = tree.rootId; var rootId = tree.rootId;
var rootObj = this.instantiate(tree.openmct[rootId], rootId);
var rootModel = tree.openmct[rootId]; // Instantiate all objects in tree with their newly genereated ids,
delete rootModel.persisted;
var rootObj = this.instantiate(rootModel, rootId);
var newStyleParent = parent.useCapability('adapter');
var newStyleRootObj = rootObj.useCapability('adapter');
if (this.openmct.composition.checkPolicy(newStyleParent, newStyleRootObj)) {
// Instantiate all objects in tree with their newly generated ids,
// adding each to its rightful parent's composition // adding each to its rightful parent's composition
rootObj.getCapability("location").setPrimaryLocation(parent.getId()); rootObj.getCapability("location").setPrimaryLocation(parent.getId());
this.deepInstantiate(rootObj, tree.openmct, []); this.deepInstantiate(rootObj, tree.openmct, []);
parent.getCapability("composition").add(rootObj); parent.getCapability("composition").add(rootObj);
} else {
var dialog = this.openmct.overlays.dialog({
iconClass: 'alert',
message: "We're sorry, but you cannot import that object type into this object.",
buttons: [
{
label: "Ok",
emphasis: true,
callback: function () {
dialog.dismiss();
}
}
]
});
}
}; };
ImportAsJSONAction.prototype.deepInstantiate = function (parent, tree, seen) { ImportAsJSONAction.prototype.deepInstantiate = function (parent, tree, seen) {
@ -103,35 +80,25 @@ define(['zepto', '../../../../src/api/objects/object-utils.js'], function ($, ob
var newObj; var newObj;
seen.push(parent.getId()); seen.push(parent.getId());
parentModel.composition.forEach(function (childId, index) {
parentModel.composition.forEach(function (childId) { if (!tree[childId] || seen.includes(childId)) {
let keystring = this.openmct.objects.makeKeyString(childId);
if (!tree[keystring] || seen.includes(keystring)) {
return; return;
} }
let newModel = tree[keystring];
delete newModel.persisted;
newObj = this.instantiate(newModel, keystring); newObj = this.instantiate(tree[childId], childId);
parent.getCapability("composition").add(newObj);
newObj.getCapability("location") newObj.getCapability("location")
.setPrimaryLocation(tree[keystring].location); .setPrimaryLocation(tree[childId].location);
this.deepInstantiate(newObj, tree, seen); this.deepInstantiate(newObj, tree, seen);
}, this); }, this);
} }
}; };
ImportAsJSONAction.prototype.generateNewIdentifiers = function (tree, namespace) { ImportAsJSONAction.prototype.generateNewIdentifiers = function (tree) {
// For each domain object in the file, generate new ID, replace in tree // For each domain object in the file, generate new ID, replace in tree
Object.keys(tree.openmct).forEach(function (domainObjectId) { Object.keys(tree.openmct).forEach(function (domainObjectId) {
let newId = { var newId = this.identifierService.generate();
namespace: namespace, tree = this.rewriteId(domainObjectId, newId, tree);
key: this.identifierService.generate()
};
let oldId = objectUtils.parseKeyString(domainObjectId);
tree = this.rewriteId(oldId, newId, tree);
}, this); }, this);
return tree; return tree;
}; };
@ -142,21 +109,9 @@ define(['zepto', '../../../../src/api/objects/object-utils.js'], function ($, ob
* *
* @private * @private
*/ */
ImportAsJSONAction.prototype.rewriteId = function (oldId, newId, tree) { ImportAsJSONAction.prototype.rewriteId = function (oldID, newID, tree) {
let newIdKeyString = this.openmct.objects.makeKeyString(newId); tree = JSON.stringify(tree).replace(new RegExp(oldID, 'g'), newID);
let oldIdKeyString = this.openmct.objects.makeKeyString(oldId); return JSON.parse(tree);
tree = JSON.stringify(tree).replace(new RegExp(oldIdKeyString, 'g'), newIdKeyString);
return JSON.parse(tree, (key, value) => {
if (Object.prototype.hasOwnProperty.call(value, 'key') &&
Object.prototype.hasOwnProperty.call(value, 'namespace') &&
value.key === oldId.key &&
value.namespace === oldId.namespace) {
return newId
} else {
return value;
}
});
}; };
ImportAsJSONAction.prototype.getFormModel = function () { ImportAsJSONAction.prototype.getFormModel = function () {

View File

@ -29,7 +29,7 @@ define(
], ],
function (ExportAsJSONAction, domainObjectFactory, MCT, AdapterCapability) { function (ExportAsJSONAction, domainObjectFactory, MCT, AdapterCapability) {
xdescribe("The export JSON action", function () { describe("The export JSON action", function () {
var context, var context,
action, action,

View File

@ -27,7 +27,7 @@ define(
], ],
function (ImportAsJSONAction, domainObjectFactory) { function (ImportAsJSONAction, domainObjectFactory) {
xdescribe("The import JSON action", function () { describe("The import JSON action", function () {
var context = {}; var context = {};
var action, var action,

View File

@ -100,7 +100,7 @@ define(
} }
CouchIndicator.prototype.getCssClass = function () { CouchIndicator.prototype.getCssClass = function () {
return "c-indicator--clickable icon-suitcase " + this.state.statusClass; return "icon-database " + this.state.statusClass;
}; };
CouchIndicator.prototype.getGlyphClass = function () { CouchIndicator.prototype.getGlyphClass = function () {

View File

@ -24,7 +24,7 @@ define(
["../src/CouchIndicator"], ["../src/CouchIndicator"],
function (CouchIndicator) { function (CouchIndicator) {
xdescribe("The CouchDB status indicator", function () { describe("The CouchDB status indicator", function () {
var mockHttp, var mockHttp,
mockInterval, mockInterval,
testPath, testPath,

View File

@ -84,7 +84,7 @@ define(
} }
ElasticIndicator.prototype.getCssClass = function () { ElasticIndicator.prototype.getCssClass = function () {
return "c-indicator--clickable icon-suitcase"; return "icon-database";
}; };
ElasticIndicator.prototype.getGlyphClass = function () { ElasticIndicator.prototype.getGlyphClass = function () {
return this.state.glyphClass; return this.state.glyphClass;

View File

@ -24,7 +24,7 @@ define(
["../src/ElasticIndicator"], ["../src/ElasticIndicator"],
function (ElasticIndicator) { function (ElasticIndicator) {
xdescribe("The ElasticSearch status indicator", function () { describe("The ElasticSearch status indicator", function () {
var mockHttp, var mockHttp,
mockInterval, mockInterval,
testPath, testPath,
@ -59,7 +59,7 @@ define(
}); });
it("has a database icon", function () { it("has a database icon", function () {
expect(indicator.getCssClass()).toEqual("icon-suitcase"); expect(indicator.getCssClass()).toEqual("icon-database");
}); });
it("consults the database at the configured path", function () { it("consults the database at the configured path", function () {

View File

@ -41,7 +41,7 @@ define(
} }
LocalStorageIndicator.prototype.getCssClass = function () { LocalStorageIndicator.prototype.getCssClass = function () {
return "c-indicator--clickable icon-suitcase s-status-caution"; return "icon-database s-status-caution";
}; };
LocalStorageIndicator.prototype.getGlyphClass = function () { LocalStorageIndicator.prototype.getGlyphClass = function () {
return 'caution'; return 'caution';

View File

@ -24,7 +24,7 @@ define(
["../src/LocalStorageIndicator"], ["../src/LocalStorageIndicator"],
function (LocalStorageIndicator) { function (LocalStorageIndicator) {
xdescribe("The local storage status indicator", function () { describe("The local storage status indicator", function () {
var indicator; var indicator;
beforeEach(function () { beforeEach(function () {
@ -38,7 +38,7 @@ define(
}); });
it("has a database icon", function () { it("has a database icon", function () {
expect(indicator.getCssClass()).toEqual("icon-suitcase s-status-caution"); expect(indicator.getCssClass()).toEqual("icon-database s-status-caution");
}); });
it("has a 'caution' class to draw attention", function () { it("has a 'caution' class to draw attention", function () {

View File

@ -37,17 +37,75 @@ define(
} }
/** /**
* Discard failures * Handle persistence failures by providing the user with a
* dialog summarizing these failures, and giving the option
* to overwrite/cancel as appropriate.
* @param {Array} failures persistence failures, as prepared * @param {Array} failures persistence failures, as prepared
* by PersistenceQueueHandler * by PersistenceQueueHandler
* @memberof platform/persistence/queue.PersistenceFailureHandler# * @memberof platform/persistence/queue.PersistenceFailureHandler#
*/ */
PersistenceFailureHandler.prototype.handle = function handleFailures(failures) { PersistenceFailureHandler.prototype.handle = function handleFailures(failures) {
// Prepare dialog for display
var dialogModel = new PersistenceFailureDialog(failures), var dialogModel = new PersistenceFailureDialog(failures),
revisionErrors = dialogModel.model.revised, revisionErrors = dialogModel.model.revised,
$q = this.$q; $q = this.$q;
// Refresh revision information for the domain object associated
// with this persistence failure
function refresh(failure) {
// Refresh the domain object to the latest from persistence
return failure.persistence.refresh();
}
// Issue a new persist call for the domain object associated with
// this failure.
function persist(failure) {
// Note that we reissue the persist request here, but don't
// return it, to avoid a circular wait. We trust that the
// PersistenceQueue will behave correctly on the next round
// of flushing.
failure.requeue();
}
// Retry persistence (overwrite) for this set of failed attempts
function retry(failuresToRetry) {
var models = {};
// Cache a copy of the model
function cacheModel(failure) {
// Clone...
models[failure.id] = JSON.parse(JSON.stringify(
failure.domainObject.getModel()
));
}
// Mutate a domain object to restore its model
function remutate(failure) {
var model = models[failure.id];
return failure.domainObject.useCapability(
"mutation",
function () {
return model;
},
model.modified
);
}
// Cache the object models we might want to save
failuresToRetry.forEach(cacheModel);
// Strategy here:
// * Cache all of the models we might want to save (above)
// * Refresh all domain objects (so they are latest versions)
// * Re-insert the cached domain object models
// * Invoke persistence again
return $q.all(failuresToRetry.map(refresh)).then(function () {
return $q.all(failuresToRetry.map(remutate));
}).then(function () {
return $q.all(failuresToRetry.map(persist));
});
}
// Discard changes for a failed refresh // Discard changes for a failed refresh
function discard(failure) { function discard(failure) {
var persistence = var persistence =
@ -60,7 +118,19 @@ define(
return $q.all(failuresToDiscard.map(discard)); return $q.all(failuresToDiscard.map(discard));
} }
// Handle user input (did they choose to overwrite?)
function handleChoice(key) {
// If so, try again
if (key === PersistenceFailureConstants.OVERWRITE_KEY) {
return retry(revisionErrors);
} else {
return discardAll(revisionErrors); return discardAll(revisionErrors);
}
}
// Prompt for user input, the overwrite if they said so.
return this.dialogService.getUserChoice(dialogModel)
.then(handleChoice, handleChoice);
}; };
return PersistenceFailureHandler; return PersistenceFailureHandler;

View File

@ -32,6 +32,14 @@ define(
mockPromise, mockPromise,
handler; handler;
function asPromise(value) {
return (value || {}).then ? value : {
then: function (callback) {
return asPromise(callback(value));
}
};
}
function makeMockFailure(id, index) { function makeMockFailure(id, index) {
var mockFailure = jasmine.createSpyObj( var mockFailure = jasmine.createSpyObj(
'failure-' + id, 'failure-' + id,
@ -66,14 +74,43 @@ define(
handler = new PersistenceFailureHandler(mockQ, mockDialogService); handler = new PersistenceFailureHandler(mockQ, mockDialogService);
}); });
it("discards on handle", function () { it("shows a dialog to handle failures", function () {
handler.handle(mockFailures); handler.handle(mockFailures);
expect(mockDialogService.getUserChoice).toHaveBeenCalled();
});
it("overwrites on request", function () {
mockQ.all.and.returnValue(asPromise([]));
handler.handle(mockFailures);
// User chooses overwrite
mockPromise.then.calls.mostRecent().args[0](Constants.OVERWRITE_KEY);
// Should refresh, remutate, and requeue all objects
mockFailures.forEach(function (mockFailure, i) {
expect(mockFailure.persistence.refresh).toHaveBeenCalled();
expect(mockFailure.requeue).toHaveBeenCalled();
expect(mockFailure.domainObject.useCapability).toHaveBeenCalledWith(
'mutation',
jasmine.any(Function),
i // timestamp
);
expect(mockFailure.domainObject.useCapability.calls.mostRecent().args[1]())
.toEqual({ id: mockFailure.id, modified: i });
});
});
it("discards on request", function () {
mockQ.all.and.returnValue(asPromise([]));
handler.handle(mockFailures);
// User chooses overwrite
mockPromise.then.calls.mostRecent().args[0](false);
// Should refresh, but not remutate, and requeue all objects
mockFailures.forEach(function (mockFailure) { mockFailures.forEach(function (mockFailure) {
expect(mockFailure.persistence.refresh).toHaveBeenCalled(); expect(mockFailure.persistence.refresh).toHaveBeenCalled();
expect(mockFailure.requeue).not.toHaveBeenCalled(); expect(mockFailure.requeue).not.toHaveBeenCalled();
expect(mockFailure.domainObject.useCapability).not.toHaveBeenCalled(); expect(mockFailure.domainObject.useCapability).not.toHaveBeenCalled();
}); });
}); });
}); });
} }
); );

View File

@ -29,7 +29,6 @@ define([
"./res/templates/search.html", "./res/templates/search.html",
"./res/templates/search-menu.html", "./res/templates/search-menu.html",
"raw-loader!./src/services/GenericSearchWorker.js", "raw-loader!./src/services/GenericSearchWorker.js",
"raw-loader!./src/services/BareBonesSearchWorker.js",
'legacyRegistry' 'legacyRegistry'
], function ( ], function (
SearchController, SearchController,
@ -40,7 +39,6 @@ define([
searchTemplate, searchTemplate,
searchMenuTemplate, searchMenuTemplate,
searchWorkerText, searchWorkerText,
BareBonesSearchWorkerText,
legacyRegistry legacyRegistry
) { ) {
@ -55,11 +53,6 @@ define([
"ROOT" "ROOT"
], ],
"priority": "fallback" "priority": "fallback"
},
{
"key": "USE_LEGACY_INDEXER",
"value": false,
"priority": 2
} }
], ],
"controllers": [ "controllers": [
@ -108,7 +101,6 @@ define([
"workerService", "workerService",
"topic", "topic",
"GENERIC_SEARCH_ROOTS", "GENERIC_SEARCH_ROOTS",
"USE_LEGACY_INDEXER",
"openmct" "openmct"
] ]
}, },
@ -123,10 +115,6 @@ define([
} }
], ],
"workers": [ "workers": [
{
"key": "bareBonesSearchWorker",
"scriptText": BareBonesSearchWorkerText
},
{ {
"key": "genericSearchWorker", "key": "genericSearchWorker",
"scriptText": searchWorkerText "scriptText": searchWorkerText

View File

@ -1,80 +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 self*/
/**
* Module defining BareBonesSearchWorker. Created by deeptailor on 10/03/2019.
*/
(function () {
// An array of objects composed of domain object IDs and names
// {id: domainObject's ID, name: domainObject's name}
var indexedItems = [];
function indexItem(id, model) {
indexedItems.push({
id: id,
name: model.name.toLowerCase()
});
}
/**
* Gets search results from the indexedItems based on provided search
* input. Returns matching results from indexedItems
*
* @param data An object which contains:
* * input: The original string which we are searching with
* * maxResults: The maximum number of search results desired
* * queryId: an id identifying this query, will be returned.
*/
function search(data) {
// This results dictionary will have domain object ID keys which
// point to the value the domain object's score.
var results,
input = data.input.trim().toLowerCase(),
message = {
request: 'search',
results: {},
total: 0,
queryId: data.queryId
};
results = indexedItems.filter((indexedItem) => {
return indexedItem.name.includes(input);
});
message.total = results.length;
message.results = results
.slice(0, data.maxResults);
return message;
}
self.onmessage = function (event) {
if (event.data.request === 'index') {
indexItem(event.data.id, event.data.model);
} else if (event.data.request === 'search') {
self.postMessage(search(event.data));
}
};
}());

View File

@ -44,7 +44,7 @@ define([
* @param {TopicService} topic the topic service. * @param {TopicService} topic the topic service.
* @param {Array} ROOTS An array of object Ids to begin indexing. * @param {Array} ROOTS An array of object Ids to begin indexing.
*/ */
function GenericSearchProvider($q, $log, modelService, workerService, topic, ROOTS, USE_LEGACY_INDEXER, openmct) { function GenericSearchProvider($q, $log, modelService, workerService, topic, ROOTS, openmct) {
var provider = this; var provider = this;
this.$q = $q; this.$q = $q;
this.$log = $log; this.$log = $log;
@ -58,8 +58,6 @@ define([
this.pendingQueries = {}; this.pendingQueries = {};
this.USE_LEGACY_INDEXER = USE_LEGACY_INDEXER;
this.worker = this.startWorker(workerService); this.worker = this.startWorker(workerService);
this.indexOnMutation(topic); this.indexOnMutation(topic);
@ -103,14 +101,8 @@ define([
* @returns worker the created search worker. * @returns worker the created search worker.
*/ */
GenericSearchProvider.prototype.startWorker = function (workerService) { GenericSearchProvider.prototype.startWorker = function (workerService) {
var provider = this, var worker = workerService.run('genericSearchWorker'),
worker; provider = this;
if (this.USE_LEGACY_INDEXER) {
worker = workerService.run('genericSearchWorker');
} else {
worker = workerService.run('bareBonesSearchWorker');
}
worker.addEventListener('message', function (messageEvent) { worker.addEventListener('message', function (messageEvent) {
provider.onWorkerMessage(messageEvent); provider.onWorkerMessage(messageEvent);
@ -250,11 +242,7 @@ define([
return; return;
} }
var pendingQuery, var pendingQuery = this.pendingQueries[event.data.queryId],
modelResults;
if (this.USE_LEGACY_INDEXER) {
pendingQuery = this.pendingQueries[event.data.queryId];
modelResults = { modelResults = {
total: event.data.total total: event.data.total
}; };
@ -266,18 +254,6 @@ define([
score: hit.matchCount score: hit.matchCount
}; };
}); });
} else {
pendingQuery = this.pendingQueries[event.data.queryId];
modelResults = {
total: event.data.total
};
modelResults.hits = event.data.results.map(function (hit) {
return {
id: hit.id
};
});
}
pendingQuery.resolve(modelResults); pendingQuery.resolve(modelResults);
delete this.pendingQueries[event.data.queryId]; delete this.pendingQueries[event.data.queryId];

View File

@ -29,7 +29,7 @@ define([
GenericSearchProvider GenericSearchProvider
) { ) {
xdescribe('GenericSearchProvider', function () { describe('GenericSearchProvider', function () {
var $q, var $q,
$log, $log,
modelService, modelService,

View File

@ -38,8 +38,8 @@ define([
'./ui/router/ApplicationRouter', './ui/router/ApplicationRouter',
'./ui/router/Browse', './ui/router/Browse',
'../platform/framework/src/Main', '../platform/framework/src/Main',
'./styles/core.scss', './styles-new/core.scss',
'./styles/notebook.scss', './styles-new/notebook.scss',
'./ui/layout/Layout.vue', './ui/layout/Layout.vue',
'../platform/core/src/objects/DomainObjectImpl', '../platform/core/src/objects/DomainObjectImpl',
'../platform/core/src/capabilities/ContextualDomainObject', '../platform/core/src/capabilities/ContextualDomainObject',
@ -95,15 +95,12 @@ define([
*/ */
function MCT() { function MCT() {
EventEmitter.call(this); EventEmitter.call(this);
/* eslint-disable no-undef */
this.buildInfo = { this.buildInfo = {
version: __OPENMCT_VERSION__, version: __OPENMCT_VERSION__,
buildDate: __OPENMCT_BUILD_DATE__, buildDate: __OPENMCT_BUILD_DATE__,
revision: __OPENMCT_REVISION__, revision: __OPENMCT_REVISION__,
branch: __OPENMCT_BUILD_BRANCH__ branch: __OPENMCT_BUILD_BRANCH__
}; };
/* eslint-enable no-undef */
this.legacyBundle = { extensions: { this.legacyBundle = { extensions: {
services: [ services: [
@ -249,20 +246,18 @@ define([
this.branding = BrandingAPI.default; this.branding = BrandingAPI.default;
this.legacyRegistry = defaultRegistry; this.legacyRegistry = defaultRegistry;
// Plugin's that are installed by default
this.install(this.plugins.Plot()); this.install(this.plugins.Plot());
this.install(this.plugins.TelemetryTable()); this.install(this.plugins.TelemetryTable());
this.install(this.plugins.DisplayLayout());
this.install(PreviewPlugin.default()); this.install(PreviewPlugin.default());
this.install(LegacyIndicatorsPlugin()); this.install(LegacyIndicatorsPlugin());
this.install(LicensesPlugin.default()); this.install(LicensesPlugin.default());
this.install(RemoveActionPlugin.default()); this.install(RemoveActionPlugin.default());
this.install(this.plugins.ImportExport());
this.install(this.plugins.FolderView()); if (typeof BUILD_CONSTANTS !== 'undefined') {
this.install(this.plugins.Tabs()); this.install(buildInfoPlugin(BUILD_CONSTANTS));
this.install(this.plugins.FlexibleLayout()); }
this.install(this.plugins.GoToOriginalAction());
} }
MCT.prototype = Object.create(EventEmitter.prototype); MCT.prototype = Object.create(EventEmitter.prototype);
@ -333,18 +328,10 @@ define([
* MCT; if undefined, MCT will be run in the body of the document * MCT; if undefined, MCT will be run in the body of the document
*/ */
MCT.prototype.start = function (domElement) { MCT.prototype.start = function (domElement) {
if (!this.plugins.DisplayLayout._installed) {
this.install(this.plugins.DisplayLayout({
showAsView: ['summary-widget']
}));
}
if (!domElement) { if (!domElement) {
domElement = document.body; domElement = document.body;
} }
this.element = domElement;
this.legacyExtension('runs', { this.legacyExtension('runs', {
depends: ['navigationService'], depends: ['navigationService'],
implementation: function (navigationService) { implementation: function (navigationService) {
@ -365,7 +352,7 @@ define([
legacyRegistry.enable('adapter'); legacyRegistry.enable('adapter');
this.router.route(/^\/$/, () => { this.router.route(/^\/$/, () => {
this.router.setPath('/browse/'); this.router.setPath('/browse/mine');
}); });
/** /**
@ -374,8 +361,7 @@ define([
* @event start * @event start
* @memberof module:openmct.MCT~ * @memberof module:openmct.MCT~
*/ */
const startPromise = new Main() var startPromise = new Main().run(this.legacyRegistry)
startPromise.run(this)
.then(function (angular) { .then(function (angular) {
this.$angular = angular; this.$angular = angular;
// OpenMCT Object provider doesn't operate properly unless // OpenMCT Object provider doesn't operate properly unless

View File

@ -25,7 +25,7 @@ define([
'./plugins/plugins', './plugins/plugins',
'legacyRegistry' 'legacyRegistry'
], function (MCT, plugins, legacyRegistry) { ], function (MCT, plugins, legacyRegistry) {
xdescribe("MCT", function () { describe("MCT", function () {
var openmct; var openmct;
var mockPlugin; var mockPlugin;
var mockPlugin2; var mockPlugin2;

View File

@ -26,7 +26,6 @@ const OUTSIDE_EDIT_PATH_BLACKLIST = ["copy", "follow", "properties", "move", "li
export default class LegacyContextMenuAction { export default class LegacyContextMenuAction {
constructor(openmct, LegacyAction) { constructor(openmct, LegacyAction) {
this.openmct = openmct; this.openmct = openmct;
this.key = LegacyAction.definition.key;
this.name = LegacyAction.definition.name; this.name = LegacyAction.definition.name;
this.description = LegacyAction.definition.description; this.description = LegacyAction.definition.description;
this.cssClass = LegacyAction.definition.cssClass; this.cssClass = LegacyAction.definition.cssClass;
@ -34,13 +33,9 @@ export default class LegacyContextMenuAction {
} }
invoke(objectPath) { invoke(objectPath) {
this.openmct.objects.getRoot().then((root) => {
let pathWithRoot = objectPath.slice();
pathWithRoot.push(root);
let context = { let context = {
category: 'contextual', category: 'contextual',
domainObject: this.openmct.legacyObject(pathWithRoot) domainObject: this.openmct.legacyObject(objectPath)
} }
let legacyAction = new this.LegacyAction(context); let legacyAction = new this.LegacyAction(context);
@ -52,7 +47,6 @@ export default class LegacyContextMenuAction {
}.bind(legacyAction); }.bind(legacyAction);
} }
legacyAction.perform(); legacyAction.perform();
});
} }
appliesTo(objectPath) { appliesTo(objectPath) {

View File

@ -36,7 +36,7 @@ define([
'./runs/RegisterLegacyTypes', './runs/RegisterLegacyTypes',
'./services/LegacyObjectAPIInterceptor', './services/LegacyObjectAPIInterceptor',
'./views/installLegacyViews', './views/installLegacyViews',
'./policies/LegacyCompositionPolicyAdapter', './policies/legacyCompositionPolicyAdapter',
'./actions/LegacyActionAdapter' './actions/LegacyActionAdapter'
], function ( ], function (
legacyRegistry, legacyRegistry,

View File

@ -37,7 +37,7 @@ define(
var legacyExtensionFunction = MCT.prototype.legacyExtension; var legacyExtensionFunction = MCT.prototype.legacyExtension;
var legacyIndicatorsRunsFunction; var legacyIndicatorsRunsFunction;
xdescribe('The legacy indicators plugin', function () { describe('The legacy indicators plugin', function () {
beforeEach(function () { beforeEach(function () {
mockLegacyExtensionFunction(); mockLegacyExtensionFunction();

View File

@ -21,13 +21,13 @@
*****************************************************************************/ *****************************************************************************/
export default function legacyCompositionPolicyAdapter(openmct) { export default function legacyCompositionPolicyAdapter(openmct) {
const instantiate = openmct.$injector.get('instantiate'); const instantiate = this.openmct.$injector.get('instantiate');
const policyService = openmct.$injector.get('policyService'); const policyService = this.openmct.$injector.get('policyService');
openmct.composition.addPolicy((parent, child) => { openmct.composition.addPolicy((parent, child) => {
let parentId = openmct.objects.makeKeyString(parent.identifier); let parentId = this.openmct.objects.makeKeyString(parent.identifier);
let childId = openmct.objects.makeKeyString(child.identifier); let childId = this.openmct.objects.makeKeyString(child.identifier);
let legacyParent = instantiate(parent, parentId); let legacyParent = instantiate(parent, parentId);
let legacyChild = instantiate(child, childId); let legacyChild = instantiate(child, childId);

View File

@ -137,7 +137,8 @@ define([
function callbackWrapper(series) { function callbackWrapper(series) {
callback(createDatum(domainObject, metadata, series, series.getPointCount() - 1)); callback(createDatum(domainObject, metadata, series, series.getPointCount() - 1));
} }
return capability.subscribe(callbackWrapper, request) || function () {};
return capability.subscribe(callbackWrapper, request);
}; };
LegacyTelemetryProvider.prototype.supportsLimits = function (domainObject) { LegacyTelemetryProvider.prototype.supportsLimits = function (domainObject) {

View File

@ -57,10 +57,8 @@ define([
}.bind(this); }.bind(this);
handleLegacyMutation = function (legacyObject) { handleLegacyMutation = function (legacyObject) {
var newStyleObject = utils.toNewFormat(legacyObject.getModel(), legacyObject.getId()), var newStyleObject = utils.toNewFormat(legacyObject.getModel(), legacyObject.getId());
keystring = utils.makeKeyString(newStyleObject.identifier); this.eventEmitter.emit(newStyleObject.identifier.key + ":*", newStyleObject);
this.eventEmitter.emit(keystring + ":*", newStyleObject);
this.eventEmitter.emit('mutation', newStyleObject); this.eventEmitter.emit('mutation', newStyleObject);
}.bind(this); }.bind(this);

View File

@ -45,30 +45,15 @@ define([
view: function (domainObject) { view: function (domainObject) {
let $rootScope = openmct.$injector.get('$rootScope'); let $rootScope = openmct.$injector.get('$rootScope');
let templateLinker = openmct.$injector.get('templateLinker'); let templateLinker = openmct.$injector.get('templateLinker');
let scope = $rootScope.$new(true); let scope = $rootScope.$new();
let legacyObject = convertToLegacyObject(domainObject); let legacyObject = convertToLegacyObject(domainObject);
let isDestroyed = false; let isDestroyed = false;
let unlistenToStatus;
let element;
scope.domainObject = legacyObject; scope.domainObject = legacyObject;
scope.model = legacyObject.getModel(); scope.model = legacyObject.getModel();
let child;
let parent;
return { return {
show: function (container) { show: function (container) {
parent = container;
child = document.createElement('div');
parent.appendChild(child);
let statusCapability = legacyObject.getCapability('status');
unlistenToStatus = statusCapability.listen((newStatus) => {
child.classList.remove('s-status-timeconductor-unsynced');
if (newStatus.includes('timeconductor-unsynced')) {
child.classList.add('s-status-timeconductor-unsynced');
}
});
// TODO: implement "gestures" support ? // TODO: implement "gestures" support ?
let uses = legacyView.uses || []; let uses = legacyView.uses || [];
let promises = []; let promises = [];
@ -89,13 +74,12 @@ define([
uses.forEach(function (key, i) { uses.forEach(function (key, i) {
scope[key] = results[i]; scope[key] = results[i];
}); });
element = openmct.$angular.element(child);
templateLinker.link( templateLinker.link(
scope, scope,
element, openmct.$angular.element(container),
legacyView legacyView
); );
child.classList.add('u-contents'); container.classList.add('u-contents');
} }
if (promises.length) { if (promises.length) {
@ -108,16 +92,8 @@ define([
link(); link();
} }
}, },
onClearData() {
scope.$broadcast('clearData');
},
destroy: function () { destroy: function () {
element.off();
element.remove();
scope.$destroy(); scope.$destroy();
element = null;
scope = null;
unlistenToStatus();
} }
} }
}, },
@ -129,7 +105,7 @@ define([
return priority; return priority;
} }
}; };
} };
return LegacyViewProvider; return LegacyViewProvider;

View File

@ -3,6 +3,17 @@ define([
], function ( ], 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) { 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.`); 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[]') let representation = openmct.$injector.get('representations[]')
@ -14,34 +25,25 @@ define([
cssClass: representation.cssClass, cssClass: representation.cssClass,
description: representation.description, description: representation.description,
canView: function (selection) { canView: function (selection) {
if (selection.length !== 1 || selection[0].length === 0) { if (!selection[0] || !selection[0].context.item) {
return false; return false;
} }
let domainObject = selection[0].context.item;
let selectionContext = selection[0][0].context; return domainObject.type === typeDefinition.key;
if (!selectionContext.item) {
return false;
}
return selectionContext.item.type === typeDefinition.key;
}, },
view: function (selection) { view: function (selection) {
let domainObject = selection[0][0].context.item; let domainObject = selection[0].context.item;
let $rootScope = openmct.$injector.get('$rootScope'); let $rootScope = openmct.$injector.get('$rootScope');
let templateLinker = openmct.$injector.get('templateLinker'); let templateLinker = openmct.$injector.get('templateLinker');
let scope = $rootScope.$new(true); let scope = $rootScope.$new();
let legacyObject = convertToLegacyObject(domainObject); let legacyObject = convertToLegacyObject(domainObject);
let isDestroyed = false; let isDestroyed = false;
let element;
scope.domainObject = legacyObject; scope.domainObject = legacyObject;
scope.model = legacyObject.getModel(); scope.model = legacyObject.getModel();
return { return {
show: function (container) { show: function (container) {
let child = document.createElement('div');
container.appendChild(child);
// TODO: implement "gestures" support ? // TODO: implement "gestures" support ?
let uses = representation.uses || []; let uses = representation.uses || [];
let promises = []; let promises = [];
@ -62,10 +64,9 @@ define([
uses.forEach(function (key, i) { uses.forEach(function (key, i) {
scope[key] = results[i]; scope[key] = results[i];
}); });
element = openmct.$angular.element(child)
templateLinker.link( templateLinker.link(
scope, scope,
element, openmct.$angular.element(container),
representation representation
); );
container.style.height = '100%'; container.style.height = '100%';
@ -82,16 +83,12 @@ define([
} }
}, },
destroy: function () { destroy: function () {
element.off();
element.remove();
scope.$destroy(); scope.$destroy();
element = null;
scope = null;
} }
} }
} }
}; };
} };
return TypeInspectorViewProvider; return TypeInspectorViewProvider;

View File

@ -28,6 +28,11 @@ export default class Editor extends EventEmitter {
super(); super();
this.editing = false; this.editing = false;
this.openmct = openmct; this.openmct = openmct;
document.addEventListener('drop', (event) => {
if (!this.isEditing()) {
this.edit();
}
}, {capture: true});
} }
/** /**
@ -74,11 +79,9 @@ export default class Editor extends EventEmitter {
* @private * @private
*/ */
cancel() { cancel() {
let cancelPromise = this.getTransactionService().cancel(); this.getTransactionService().cancel();
this.editing = false; this.editing = false;
this.emit('isEditing', false); this.emit('isEditing', false);
return cancelPromise;
} }
/** /**

View File

@ -22,20 +22,8 @@ define([
publicAPI = {}; publicAPI = {};
publicAPI.objects = jasmine.createSpyObj('ObjectAPI', [ publicAPI.objects = jasmine.createSpyObj('ObjectAPI', [
'get', 'get',
'mutate', 'mutate'
'observe',
'areIdsEqual'
]); ]);
publicAPI.objects.areIdsEqual.and.callFake(function (id1, id2) {
return id1.namespace === id2.namespace && id1.key === id2.key;
});
publicAPI.composition = jasmine.createSpyObj('CompositionAPI', [
'checkPolicy'
]);
publicAPI.composition.checkPolicy.and.returnValue(true);
publicAPI.objects.eventEmitter = jasmine.createSpyObj('eventemitter', [ publicAPI.objects.eventEmitter = jasmine.createSpyObj('eventemitter', [
'on' 'on'
]); ]);
@ -131,16 +119,49 @@ define([
expect(newComposition[2].key).toEqual('a'); expect(newComposition[2].key).toEqual('a');
}) })
}); });
it('supports adding an object to composition', function () {
let addListener = jasmine.createSpy('addListener');
let mockChildObject = {
identifier: {key: 'mock-key', namespace: ''}
};
composition.on('add', addListener);
composition.add(mockChildObject);
expect(domainObject.composition.length).toBe(4); // TODO: Implement add/removal in new default provider.
expect(domainObject.composition[3]).toEqual(mockChildObject.identifier); xit('synchronizes changes between instances', function () {
var otherComposition = compositionAPI.get(domainObject);
var addListener = jasmine.createSpy('addListener');
var removeListener = jasmine.createSpy('removeListener');
var otherAddListener = jasmine.createSpy('otherAddListener');
var otherRemoveListener = jasmine.createSpy('otherRemoveListener');
composition.on('add', addListener);
composition.on('remove', removeListener);
otherComposition.on('add', otherAddListener);
otherComposition.on('remove', otherRemoveListener);
return Promise.all([composition.load(), otherComposition.load()])
.then(function () {
expect(addListener).toHaveBeenCalled();
expect(otherAddListener).toHaveBeenCalled();
expect(removeListener).not.toHaveBeenCalled();
expect(otherRemoveListener).not.toHaveBeenCalled();
var object = addListener.calls.mostRecent().args[0];
composition.remove(object);
expect(removeListener).toHaveBeenCalled();
expect(otherRemoveListener).toHaveBeenCalled();
addListener.reset();
otherAddListener.reset();
composition.add(object);
expect(addListener).toHaveBeenCalled();
expect(otherAddListener).toHaveBeenCalled();
removeListener.reset();
otherRemoveListener.reset();
otherComposition.remove(object);
expect(removeListener).toHaveBeenCalled();
expect(otherRemoveListener).toHaveBeenCalled();
addListener.reset();
otherAddListener.reset();
otherComposition.add(object);
expect(addListener).toHaveBeenCalled();
expect(otherAddListener).toHaveBeenCalled();
});
}); });
}); });
@ -163,9 +184,7 @@ define([
key: 'thing' key: 'thing'
} }
]); ]);
}, }
add: jasmine.createSpy('add'),
remove: jasmine.createSpy('remove')
}; };
domainObject = { domainObject = {
identifier: { identifier: {
@ -195,25 +214,6 @@ define([
}); });
}); });
}); });
describe('Calling add or remove', function () {
let mockChildObject;
beforeEach(function () {
mockChildObject = {
identifier: {key: 'mock-key', namespace: ''}
};
composition.add(mockChildObject);
});
it('calls add on the provider', function () {
expect(customProvider.add).toHaveBeenCalledWith(domainObject, mockChildObject.identifier);
});
it('calls remove on the provider', function () {
composition.remove(mockChildObject);
expect(customProvider.remove).toHaveBeenCalledWith(domainObject, mockChildObject.identifier);
});
});
}); });
describe('dynamic custom composition', function () { describe('dynamic custom composition', function () {

View File

@ -25,6 +25,7 @@ define([
], function ( ], function (
_ _
) { ) {
/** /**
* A CompositionCollection represents the list of domain objects contained * A CompositionCollection represents the list of domain objects contained
* by another domain object. It provides methods for loading this * by another domain object. It provides methods for loading this
@ -62,6 +63,7 @@ define([
this.onProviderRemove = this.onProviderRemove.bind(this); this.onProviderRemove = this.onProviderRemove.bind(this);
} }
/** /**
* Listen for changes to this composition. Supports 'add', 'remove', and * Listen for changes to this composition. Supports 'add', 'remove', and
* 'load' events. * 'load' events.
@ -74,9 +76,7 @@ define([
if (!this.listeners[event]) { if (!this.listeners[event]) {
throw new Error('Event not supported by composition: ' + event); throw new Error('Event not supported by composition: ' + event);
} }
if (!this.mutationListener) {
this._synchronize();
}
if (this.provider.on && this.provider.off) { if (this.provider.on && this.provider.off) {
if (event === 'add') { if (event === 'add') {
this.provider.on( this.provider.on(
@ -132,8 +132,6 @@ define([
this.listeners[event].splice(index, 1); this.listeners[event].splice(index, 1);
if (this.listeners[event].length === 0) { if (this.listeners[event].length === 0) {
this._destroy();
// Remove provider listener if this is the last callback to // Remove provider listener if this is the last callback to
// be removed. // be removed.
if (this.provider.off && this.provider.on) { if (this.provider.off && this.provider.on) {
@ -177,9 +175,6 @@ define([
*/ */
CompositionCollection.prototype.add = function (child, skipMutate) { CompositionCollection.prototype.add = function (child, skipMutate) {
if (!skipMutate) { if (!skipMutate) {
if (!this.publicAPI.composition.checkPolicy(this.domainObject, child)) {
throw `Object of type ${child.type} cannot be added to object of type ${this.domainObject.type}`;
}
this.provider.add(this.domainObject, child.identifier); this.provider.add(this.domainObject, child.identifier);
} else { } else {
this.emit('add', child); this.emit('add', child);
@ -271,19 +266,6 @@ define([
this.remove(child, true); this.remove(child, true);
}; };
CompositionCollection.prototype._synchronize = function () {
this.mutationListener = this.publicAPI.objects.observe(this.domainObject, '*', (newDomainObject) => {
this.domainObject = JSON.parse(JSON.stringify(newDomainObject));
});
};
CompositionCollection.prototype._destroy = function () {
if (this.mutationListener) {
this.mutationListener();
delete this.mutationListener;
}
};
/** /**
* Emit events. * Emit events.
* @private * @private

View File

@ -48,11 +48,24 @@ define([
this.listeningTo = {}; this.listeningTo = {};
this.onMutation = this.onMutation.bind(this); this.onMutation = this.onMutation.bind(this);
this.cannotContainDuplicates = this.cannotContainDuplicates.bind(this);
this.cannotContainItself = this.cannotContainItself.bind(this); this.cannotContainItself = this.cannotContainItself.bind(this);
compositionAPI.addPolicy(this.cannotContainDuplicates);
compositionAPI.addPolicy(this.cannotContainItself); compositionAPI.addPolicy(this.cannotContainItself);
} }
/**
* @private
*/
DefaultCompositionProvider.prototype.cannotContainDuplicates = function (parent, child) {
return this.appliesTo(parent) &&
parent.composition.findIndex((composeeId) => {
return composeeId.namespace === child.identifier.namespace &&
composeeId.key === child.identifier.key;
}) === -1;
}
/** /**
* @private * @private
*/ */
@ -186,18 +199,9 @@ define([
* @memberof module:openmct.CompositionProvider# * @memberof module:openmct.CompositionProvider#
* @method add * @method add
*/ */
DefaultCompositionProvider.prototype.add = function (parent, childId) { DefaultCompositionProvider.prototype.add = function (domainObject, child) {
if (!this.includes(parent, childId)) { throw new Error('Default Provider does not implement adding.');
parent.composition.push(childId); // TODO: this needs to be synchronized via mutation
this.publicAPI.objects.mutate(parent, 'composition', parent.composition);
}
};
/**
* @private
*/
DefaultCompositionProvider.prototype.includes = function (parent, childId) {
return parent.composition.findIndex(composee =>
this.publicAPI.objects.areIdsEqual(composee, childId)) !== -1;
}; };
DefaultCompositionProvider.prototype.reorder = function (domainObject, oldIndex, newIndex) { DefaultCompositionProvider.prototype.reorder = function (domainObject, oldIndex, newIndex) {

View File

@ -49,9 +49,6 @@ class ContextMenuAPI {
* a single sentence or short paragraph) of this kind of view * a single sentence or short paragraph) of this kind of view
* @property {string} cssClass the CSS class to apply to labels for this * @property {string} cssClass the CSS class to apply to labels for this
* view (to add icons, for instance) * view (to add icons, for instance)
* @property {string} key unique key to identify the context menu action
* (used in custom context menu eg table rows, to identify which actions to include)
* @property {boolean} hideInDefaultMenu optional flag to hide action from showing in the default context menu (tree item)
*/ */
/** /**
* @method appliesTo * @method appliesTo
@ -75,21 +72,12 @@ class ContextMenuAPI {
/** /**
* @private * @private
*/ */
_showContextMenuForObjectPath(objectPath, x, y, actionsToBeIncluded) { _showContextMenuForObjectPath(objectPath, x, y) {
let applicableActions = this._allActions.filter((action) => { let applicableActions = this._allActions.filter((action) => {
if (actionsToBeIncluded) {
if (action.appliesTo === undefined && actionsToBeIncluded.includes(action.key)) {
return true;
}
return action.appliesTo(objectPath, actionsToBeIncluded) && actionsToBeIncluded.includes(action.key);
} else {
if (action.appliesTo === undefined) { if (action.appliesTo === undefined) {
return true; return true;
} }
return action.appliesTo(objectPath) && !action.hideInDefaultMenu; return action.appliesTo(objectPath);
}
}); });
if (this._activeContextMenu) { if (this._activeContextMenu) {

View File

@ -29,7 +29,7 @@ define(
MCT, MCT,
MCTIndicators MCTIndicators
) { ) {
xdescribe("The Indicator API", function () { describe("The Indicator API", function () {
var openmct; var openmct;
var directive; var directive;
var holderElement; var holderElement;

View File

@ -28,11 +28,11 @@ define(['zepto', './res/indicator-template.html'],
this.openmct = openmct; this.openmct = openmct;
this.element = $(indicatorTemplate)[0]; this.element = $(indicatorTemplate)[0];
this.textElement = this.element.querySelector('.js-indicator-text'); this.textElement = this.element.querySelector('.indicator-text');
//Set defaults //Set defaults
this.text('New Indicator'); this.text('New Indicator');
this.description(''); this.description('A simple indicator');
this.iconClass(DEFAULT_ICON_CLASS); this.iconClass(DEFAULT_ICON_CLASS);
this.statusClass(''); this.statusClass('');
} }

View File

@ -1,3 +1,3 @@
<div class="c-indicator c-indicator--clickable c-indicator--simple" title=""> <div class="ls-indicator" title="">
<span class="label js-indicator-text c-indicator__label"></span> <span class="label indicator-text"></span>
</div> </div>

View File

@ -21,10 +21,8 @@
*****************************************************************************/ *****************************************************************************/
define([ define([
'./object-utils.js',
'lodash' 'lodash'
], function ( ], function (
utils,
_ _
) { ) {
var ANY_OBJECT_EVENT = "mutation"; var ANY_OBJECT_EVENT = "mutation";
@ -43,9 +41,7 @@ define([
} }
function qualifiedEventName(object, eventName) { function qualifiedEventName(object, eventName) {
var keystring = utils.makeKeyString(object.identifier); return [object.identifier.key, eventName].join(':');
return [keystring, eventName].join(':');
} }
MutableObject.prototype.stopListening = function () { MutableObject.prototype.stopListening = function () {

View File

@ -36,7 +36,7 @@
.c-message { .c-message {
display: flex; display: flex;
align-items: center; align-items: flex-start;
> * + * { > * + * {
margin-left: $interiorMarginLg; margin-left: $interiorMarginLg;
@ -66,17 +66,6 @@
font-size: 1.2em; // TEMP font-size: 1.2em; // TEMP
} }
&--simple {
// Icon and text elements only
&:before {
font-size: 30px !important;
}
[class*='__text'] {
font-size: 1.25em;
}
}
/************************** LEGACY */ /************************** LEGACY */
&.message-severity-info:before { &.message-severity-info:before {
@include legacyMessage(); @include legacyMessage();
@ -93,7 +82,7 @@
&.message-severity-error:before { &.message-severity-error:before {
@include legacyMessage(); @include legacyMessage();
content: $glyph-icon-alert-triangle; content: $glyph-icon-alert-triangle;
color: $colorWarningHi; color: $colorWarningLo;
} }
// Messages in a list // Messages in a list

View File

@ -69,7 +69,6 @@
flex: 1 1 auto; flex: 1 1 auto;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
overflow: hidden;
} }
&__top-bar { &__top-bar {
@ -93,7 +92,6 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
flex: 1 1 auto; flex: 1 1 auto;
height: 0; // Chrome 73 overflow bug fix
overflow: auto; overflow: auto;
padding-right: $interiorMargin; // fend off scroll bar padding-right: $interiorMargin; // fend off scroll bar
} }

View File

@ -280,11 +280,7 @@ define([
if (!provider) { if (!provider) {
return Promise.reject('No provider found'); return Promise.reject('No provider found');
} }
return provider.request.apply(provider, arguments).catch((rejected) => { return provider.request.apply(provider, arguments);
this.openmct.notifications.error('Error requesting telemetry data, see console for details');
console.error(rejected);
return Promise.reject(rejected);
});
}; };
/** /**

View File

@ -25,7 +25,7 @@ define([
], function ( ], function (
TelemetryAPI TelemetryAPI
) { ) {
xdescribe('Telemetry API', function () { describe('Telemetry API', function () {
var openmct; var openmct;
var telemetryAPI; var telemetryAPI;
var mockTypeService; var mockTypeService;

View File

@ -32,6 +32,6 @@ class CSVExporter {
let blob = new Blob([csvText], { type: "text/csv" }); let blob = new Blob([csvText], { type: "text/csv" });
saveAs(blob, filename); saveAs(blob, filename);
} }
} };
export default CSVExporter; export default CSVExporter;

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