Compare commits
	
		
			50 Commits
		
	
	
		
			code-cover
			...
			vista-r4.9
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					ea7cf080c3 | ||
| 
						 | 
					9b0762f201 | ||
| 
						 | 
					31aa291672 | ||
| 
						 | 
					65e8d8b23c | ||
| 
						 | 
					fdb35094f4 | ||
| 
						 | 
					3b99a12d7f | ||
| 
						 | 
					25ee2d8099 | ||
| 
						 | 
					c14cc25977 | ||
| 
						 | 
					50997270e9 | ||
| 
						 | 
					67e3094c6c | ||
| 
						 | 
					526e31d10c | ||
| 
						 | 
					3b316ed491 | ||
| 
						 | 
					282ead581a | ||
| 
						 | 
					c4f18a4797 | ||
| 
						 | 
					c273e83093 | ||
| 
						 | 
					e4b9242864 | ||
| 
						 | 
					ceddadcac6 | ||
| 
						 | 
					6e2437b09e | ||
| 
						 | 
					5a44931537 | ||
| 
						 | 
					f165d9c064 | ||
| 
						 | 
					613973d936 | ||
| 
						 | 
					830f321f90 | ||
| 
						 | 
					a14cd62878 | ||
| 
						 | 
					8314d03af5 | ||
| 
						 | 
					187da3c462 | ||
| 
						 | 
					e4f134ca59 | ||
| 
						 | 
					76829ad252 | ||
| 
						 | 
					a8da0d5917 | ||
| 
						 | 
					488beb5b3f | ||
| 
						 | 
					2f63718385 | ||
| 
						 | 
					433f1bf28e | ||
| 
						 | 
					936b88363c | ||
| 
						 | 
					38fec73a33 | ||
| 
						 | 
					43c2c8543e | ||
| 
						 | 
					e8e719e7f7 | ||
| 
						 | 
					26e70d82b7 | ||
| 
						 | 
					3a65f75d21 | ||
| 
						 | 
					51e4c0c836 | ||
| 
						 | 
					bb9c225f23 | ||
| 
						 | 
					19ec98af79 | ||
| 
						 | 
					23ead2ceaa | ||
| 
						 | 
					6a8f4b5d9c | ||
| 
						 | 
					464bb3b885 | ||
| 
						 | 
					4775c88909 | ||
| 
						 | 
					722e2e2bb1 | ||
| 
						 | 
					333aa1d6db | ||
| 
						 | 
					5e92c69fe2 | ||
| 
						 | 
					8ddba2b06f | ||
| 
						 | 
					6f9241c0b1 | ||
| 
						 | 
					d84808aa68 | 
@@ -22,7 +22,7 @@ commands:
 | 
			
		||||
          node-version: << parameters.node-version >>
 | 
			
		||||
      - node/install:
 | 
			
		||||
          install-npm: true
 | 
			
		||||
          node-version: lts/fermium
 | 
			
		||||
          node-version: << parameters.node-version >>
 | 
			
		||||
      - run: npm install
 | 
			
		||||
  restore_cache_cmd:
 | 
			
		||||
    description: "Custom command for restoring cache with the ability to bust cache. When BUST_CACHE is set to true, jobs will not restore cache"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/ISSUE_TEMPLATE/bug_report.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/ISSUE_TEMPLATE/bug_report.md
									
									
									
									
										vendored
									
									
								
							@@ -24,7 +24,7 @@ assignees: ''
 | 
			
		||||
- [ ] Regression? Did this used to work or has it always been broken?
 | 
			
		||||
- [ ] Is there a workaround available?
 | 
			
		||||
- [ ] Does this impact a critical component?
 | 
			
		||||
- [ ] Is this just a visual bug?
 | 
			
		||||
- [ ] Is this just a visual bug with no functional impact?
 | 
			
		||||
 | 
			
		||||
#### Steps to Reproduce
 | 
			
		||||
<!--- Provide a link to a live example, or an unambiguous set of steps to -->
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										11
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
								
							@@ -11,11 +11,12 @@ updates:
 | 
			
		||||
      - "dependencies"
 | 
			
		||||
      - "pr:e2e"
 | 
			
		||||
    allow:
 | 
			
		||||
      - dependency-name: "eslint*"
 | 
			
		||||
      - dependency-name: "karma*"
 | 
			
		||||
      - dependency-name: "jasmine*"
 | 
			
		||||
      - dependency-name: "playwright*"
 | 
			
		||||
      - dependency-name: "percy*"
 | 
			
		||||
      - dependency-name: "*eslint*"
 | 
			
		||||
      - dependency-name: "*karma*"
 | 
			
		||||
      - dependency-name: "*jasmine*"
 | 
			
		||||
      - dependency-name: "*playwright*"
 | 
			
		||||
      - dependency-name: "*percy*"
 | 
			
		||||
      - dependency-name: "*vue-loader*"
 | 
			
		||||
 | 
			
		||||
  - package-ecosystem: "github-actions"
 | 
			
		||||
    directory: "/"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								.npmrc
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								.npmrc
									
									
									
									
									
								
							@@ -1 +1,6 @@
 | 
			
		||||
loglevel=warn
 | 
			
		||||
 | 
			
		||||
# Temporary: istanbul-instrumenter-loader is working with webpack 5, but states
 | 
			
		||||
# webpack 4 being the latest version it supports, so this legacy-peer-deps
 | 
			
		||||
# allows us to install it anyway.
 | 
			
		||||
legacy-peer-deps=true
 | 
			
		||||
							
								
								
									
										20
									
								
								app.js
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								app.js
									
									
									
									
									
								
							@@ -7,7 +7,6 @@
 | 
			
		||||
 * node app.js [options]
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const options = require('minimist')(process.argv.slice(2));
 | 
			
		||||
const express = require('express');
 | 
			
		||||
const app = express();
 | 
			
		||||
@@ -40,10 +39,19 @@ app.use('/proxyUrl', function proxyRequest(req, res, next) {
 | 
			
		||||
    }).on('error', next)).pipe(res);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
class WatchRunPlugin {
 | 
			
		||||
    apply(compiler) {
 | 
			
		||||
        compiler.hooks.emit.tapAsync('WatchRunPlugin', (compilation, callback) => {
 | 
			
		||||
            console.log('Begin compile at ' + new Date());
 | 
			
		||||
            callback();
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const webpack = require('webpack');
 | 
			
		||||
const webpackConfig = require('./webpack.config.js');
 | 
			
		||||
const webpackConfig = require('./webpack.dev.js');
 | 
			
		||||
webpackConfig.plugins.push(new webpack.HotModuleReplacementPlugin());
 | 
			
		||||
webpackConfig.plugins.push(function() { this.plugin('watch-run', function(watching, callback) { console.log('Begin compile at ' + new Date()); callback(); }) });
 | 
			
		||||
webpackConfig.plugins.push(new WatchRunPlugin());
 | 
			
		||||
 | 
			
		||||
webpackConfig.entry.openmct = [
 | 
			
		||||
    'webpack-hot-middleware/client?reload=true',
 | 
			
		||||
@@ -62,9 +70,7 @@ app.use(require('webpack-dev-middleware')(
 | 
			
		||||
 | 
			
		||||
app.use(require('webpack-hot-middleware')(
 | 
			
		||||
    compiler,
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    {}
 | 
			
		||||
));
 | 
			
		||||
 | 
			
		||||
// Expose index.html for development users.
 | 
			
		||||
@@ -74,5 +80,5 @@ app.get('/', function (req, res) {
 | 
			
		||||
 | 
			
		||||
// Finally, open the HTTP server and log the instance to the console
 | 
			
		||||
app.listen(options.port, options.host, function() {
 | 
			
		||||
    console.log('Open MCT application running at %s:%s', options.host, options.port)
 | 
			
		||||
    console.log('Open MCT application running at %s:%s', options.host, options.port);
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -75,11 +75,6 @@
 | 
			
		||||
        const TWO_HOURS = ONE_HOUR * 2;
 | 
			
		||||
        const ONE_DAY = ONE_HOUR * 24;
 | 
			
		||||
 | 
			
		||||
        [
 | 
			
		||||
            'example/eventGenerator'
 | 
			
		||||
        ].forEach(
 | 
			
		||||
            openmct.legacyRegistry.enable.bind(openmct.legacyRegistry)
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        openmct.install(openmct.plugins.LocalStorage());
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,6 @@
 | 
			
		||||
 | 
			
		||||
/*global module,process*/
 | 
			
		||||
 | 
			
		||||
const devMode = process.env.NODE_ENV !== 'production';
 | 
			
		||||
const browsers = [process.env.NODE_ENV === 'debug' ? 'ChromeDebugging' : 'ChromeHeadless'];
 | 
			
		||||
const coverageEnabled = process.env.COVERAGE === 'true';
 | 
			
		||||
const reporters = ['spec', 'junit'];
 | 
			
		||||
@@ -32,10 +31,10 @@ if (coverageEnabled) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = (config) => {
 | 
			
		||||
    const webpackConfig = require('./webpack.config.js');
 | 
			
		||||
    const webpackConfig = require('./webpack.dev.js');
 | 
			
		||||
    delete webpackConfig.output;
 | 
			
		||||
 | 
			
		||||
    if (!devMode || coverageEnabled) {
 | 
			
		||||
    if (coverageEnabled) {
 | 
			
		||||
        webpackConfig.module.rules.push({
 | 
			
		||||
            test: /\.js$/,
 | 
			
		||||
            exclude: /node_modules|example|lib|dist/,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										38
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										38
									
								
								package.json
									
									
									
									
									
								
							@@ -1,6 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "openmct",
 | 
			
		||||
  "version": "1.8.2",
 | 
			
		||||
  "version": "1.8.3-SNAPSHOT",
 | 
			
		||||
  "description": "The Open MCT core platform",
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "@braintree/sanitize-url": "^5.0.2",
 | 
			
		||||
@@ -10,24 +10,23 @@
 | 
			
		||||
    "allure-playwright": "^2.0.0-beta.14",
 | 
			
		||||
    "angular": ">=1.8.0",
 | 
			
		||||
    "angular-route": "1.4.14",
 | 
			
		||||
    "babel-eslint": "10.0.3",
 | 
			
		||||
    "babel-eslint": "10.1.0",
 | 
			
		||||
    "comma-separated-values": "^3.6.4",
 | 
			
		||||
    "concurrently": "^3.6.1",
 | 
			
		||||
    "copy-webpack-plugin": "^4.5.2",
 | 
			
		||||
    "copy-webpack-plugin": "^9.0.0",
 | 
			
		||||
    "cross-env": "^6.0.3",
 | 
			
		||||
    "css-loader": "^1.0.0",
 | 
			
		||||
    "css-loader": "^4.0.0",
 | 
			
		||||
    "d3-axis": "1.0.x",
 | 
			
		||||
    "d3-scale": "1.0.x",
 | 
			
		||||
    "d3-selection": "1.3.x",
 | 
			
		||||
    "eslint": "7.0.0",
 | 
			
		||||
    "eslint-plugin-playwright": "0.7.0",
 | 
			
		||||
    "eslint-plugin-playwright": "0.7.1",
 | 
			
		||||
    "eslint-plugin-vue": "^7.5.0",
 | 
			
		||||
    "eslint-plugin-you-dont-need-lodash-underscore": "^6.10.0",
 | 
			
		||||
    "eventemitter3": "^1.2.0",
 | 
			
		||||
    "exports-loader": "^0.7.0",
 | 
			
		||||
    "express": "^4.13.1",
 | 
			
		||||
    "fast-sass-loader": "1.4.6",
 | 
			
		||||
    "file-loader": "^1.1.11",
 | 
			
		||||
    "file-loader": "^6.1.0",
 | 
			
		||||
    "file-saver": "^1.3.8",
 | 
			
		||||
    "git-rev-sync": "^1.4.0",
 | 
			
		||||
    "glob": ">= 3.0.0",
 | 
			
		||||
@@ -35,7 +34,7 @@
 | 
			
		||||
    "html2canvas": "^1.0.0-rc.7",
 | 
			
		||||
    "imports-loader": "^0.8.0",
 | 
			
		||||
    "istanbul-instrumenter-loader": "^3.0.1",
 | 
			
		||||
    "jasmine-core": "^3.7.1",
 | 
			
		||||
    "jasmine-core": "^4.0.0",
 | 
			
		||||
    "jsdoc": "^3.3.2",
 | 
			
		||||
    "karma": "6.3.9",
 | 
			
		||||
    "karma-chrome-launcher": "3.1.0",
 | 
			
		||||
@@ -47,18 +46,17 @@
 | 
			
		||||
    "karma-junit-reporter": "2.0.1",
 | 
			
		||||
    "karma-sourcemap-loader": "0.3.8",
 | 
			
		||||
    "karma-spec-reporter": "0.0.32",
 | 
			
		||||
    "karma-webpack": "4.0.2",
 | 
			
		||||
    "karma-webpack": "^5.0.0",
 | 
			
		||||
    "location-bar": "^3.0.1",
 | 
			
		||||
    "lodash": "^4.17.12",
 | 
			
		||||
    "markdown-toc": "^0.11.7",
 | 
			
		||||
    "marked": "^0.3.5",
 | 
			
		||||
    "mini-css-extract-plugin": "^0.4.1",
 | 
			
		||||
    "mini-css-extract-plugin": "^1.6.0",
 | 
			
		||||
    "minimist": "^1.2.5",
 | 
			
		||||
    "moment": "2.25.3",
 | 
			
		||||
    "moment-duration-format": "^2.2.2",
 | 
			
		||||
    "moment-timezone": "0.5.28",
 | 
			
		||||
    "node-bourbon": "^4.2.3",
 | 
			
		||||
    "node-sass": "^4.14.1",
 | 
			
		||||
    "painterro": "^1.2.56",
 | 
			
		||||
    "playwright": "^1.16.3",
 | 
			
		||||
    "plotly.js-basic-dist": "^2.5.0",
 | 
			
		||||
@@ -66,19 +64,23 @@
 | 
			
		||||
    "printj": "^1.2.1",
 | 
			
		||||
    "raw-loader": "^0.5.1",
 | 
			
		||||
    "request": "^2.69.0",
 | 
			
		||||
    "resolve-url-loader": "^4.0.0",
 | 
			
		||||
    "sass": "^1.42.1",
 | 
			
		||||
    "sass-loader": "^12.1.0",
 | 
			
		||||
    "sinon": "^12.0.1",
 | 
			
		||||
    "split": "^1.0.0",
 | 
			
		||||
    "style-loader": "^1.0.1",
 | 
			
		||||
    "uuid": "^3.3.3",
 | 
			
		||||
    "v8-compile-cache": "^1.1.0",
 | 
			
		||||
    "vue": "2.5.6",
 | 
			
		||||
    "vue-eslint-parser": "7.11.0",
 | 
			
		||||
    "vue-loader": "^15.2.6",
 | 
			
		||||
    "vue-eslint-parser": "8.0.1",
 | 
			
		||||
    "vue-loader": "15.9.8",
 | 
			
		||||
    "vue-template-compiler": "2.5.6",
 | 
			
		||||
    "webpack": "^4.16.2",
 | 
			
		||||
    "webpack-cli": "^3.1.0",
 | 
			
		||||
    "webpack": "^5.53.0",
 | 
			
		||||
    "webpack-cli": "^4.0.0",
 | 
			
		||||
    "webpack-dev-middleware": "^3.1.3",
 | 
			
		||||
    "webpack-hot-middleware": "^2.22.3",
 | 
			
		||||
    "webpack-merge": "^5.8.0",
 | 
			
		||||
    "zepto": "^1.2.0"
 | 
			
		||||
  },
 | 
			
		||||
  "scripts": {
 | 
			
		||||
@@ -87,9 +89,9 @@
 | 
			
		||||
    "start": "node app.js",
 | 
			
		||||
    "lint": "eslint platform example src --ext .js,.vue openmct.js",
 | 
			
		||||
    "lint:fix": "eslint platform example src --ext .js,.vue openmct.js --fix",
 | 
			
		||||
    "build:prod": "cross-env NODE_ENV=production webpack",
 | 
			
		||||
    "build:dev": "webpack",
 | 
			
		||||
    "build:watch": "webpack --watch",
 | 
			
		||||
    "build:prod": "cross-env webpack --config webpack.prod.js",
 | 
			
		||||
    "build:dev": "webpack --config webpack.dev.js",
 | 
			
		||||
    "build:watch": "webpack --config webpack.dev.js --watch",
 | 
			
		||||
    "test": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" karma start --single-run",
 | 
			
		||||
    "test:debug": "cross-env NODE_ENV=debug karma start --no-single-run",
 | 
			
		||||
    "test:coverage": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" COVERAGE=true karma start --single-run",
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										143
									
								
								src/MCT.js
									
									
									
									
									
								
							
							
						
						
									
										143
									
								
								src/MCT.js
									
									
									
									
									
								
							@@ -22,25 +22,17 @@
 | 
			
		||||
 | 
			
		||||
define([
 | 
			
		||||
    'EventEmitter',
 | 
			
		||||
    'uuid',
 | 
			
		||||
    './BundleRegistry',
 | 
			
		||||
    './installDefaultBundles',
 | 
			
		||||
    './api/api',
 | 
			
		||||
    './api/overlays/OverlayAPI',
 | 
			
		||||
    './selection/Selection',
 | 
			
		||||
    'objectUtils',
 | 
			
		||||
    './plugins/plugins',
 | 
			
		||||
    './adapter/indicators/legacy-indicators-plugin',
 | 
			
		||||
    './ui/registries/ViewRegistry',
 | 
			
		||||
    './plugins/imagery/plugin',
 | 
			
		||||
    './ui/registries/InspectorViewRegistry',
 | 
			
		||||
    './ui/registries/ToolbarRegistry',
 | 
			
		||||
    './ui/router/ApplicationRouter',
 | 
			
		||||
    './ui/router/Browse',
 | 
			
		||||
    '../platform/framework/src/Main',
 | 
			
		||||
    './ui/layout/Layout.vue',
 | 
			
		||||
    '../platform/core/src/objects/DomainObjectImpl',
 | 
			
		||||
    '../platform/core/src/capabilities/ContextualDomainObject',
 | 
			
		||||
    './ui/preview/plugin',
 | 
			
		||||
    './api/Branding',
 | 
			
		||||
    './plugins/licenses/plugin',
 | 
			
		||||
@@ -53,25 +45,17 @@ define([
 | 
			
		||||
    'vue'
 | 
			
		||||
], function (
 | 
			
		||||
    EventEmitter,
 | 
			
		||||
    uuid,
 | 
			
		||||
    BundleRegistry,
 | 
			
		||||
    installDefaultBundles,
 | 
			
		||||
    api,
 | 
			
		||||
    OverlayAPI,
 | 
			
		||||
    Selection,
 | 
			
		||||
    objectUtils,
 | 
			
		||||
    plugins,
 | 
			
		||||
    LegacyIndicatorsPlugin,
 | 
			
		||||
    ViewRegistry,
 | 
			
		||||
    ImageryPlugin,
 | 
			
		||||
    InspectorViewRegistry,
 | 
			
		||||
    ToolbarRegistry,
 | 
			
		||||
    ApplicationRouter,
 | 
			
		||||
    Browse,
 | 
			
		||||
    Main,
 | 
			
		||||
    Layout,
 | 
			
		||||
    DomainObjectImpl,
 | 
			
		||||
    ContextualDomainObject,
 | 
			
		||||
    PreviewPlugin,
 | 
			
		||||
    BrandingAPI,
 | 
			
		||||
    LicensesPlugin,
 | 
			
		||||
@@ -108,23 +92,6 @@ define([
 | 
			
		||||
            revision: __OPENMCT_REVISION__,
 | 
			
		||||
            branch: __OPENMCT_BUILD_BRANCH__
 | 
			
		||||
        };
 | 
			
		||||
        /* eslint-enable no-undef */
 | 
			
		||||
 | 
			
		||||
        this.legacyBundle = {
 | 
			
		||||
            extensions: {
 | 
			
		||||
                services: [
 | 
			
		||||
                    {
 | 
			
		||||
                        key: "openmct",
 | 
			
		||||
                        implementation: function ($injector) {
 | 
			
		||||
                            this.$injector = $injector;
 | 
			
		||||
 | 
			
		||||
                            return this;
 | 
			
		||||
                        }.bind(this),
 | 
			
		||||
                        depends: ['$injector']
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        this.destroy = this.destroy.bind(this);
 | 
			
		||||
        /**
 | 
			
		||||
@@ -264,16 +231,12 @@ define([
 | 
			
		||||
 | 
			
		||||
        this.branding = BrandingAPI.default;
 | 
			
		||||
 | 
			
		||||
        this.legacyRegistry = new BundleRegistry();
 | 
			
		||||
        installDefaultBundles(this.legacyRegistry);
 | 
			
		||||
 | 
			
		||||
        // Plugins that are installed by default
 | 
			
		||||
 | 
			
		||||
        this.install(this.plugins.Plot());
 | 
			
		||||
        this.install(this.plugins.Chart());
 | 
			
		||||
        this.install(this.plugins.TelemetryTable.default());
 | 
			
		||||
        this.install(PreviewPlugin.default());
 | 
			
		||||
        this.install(LegacyIndicatorsPlugin());
 | 
			
		||||
        this.install(LicensesPlugin.default());
 | 
			
		||||
        this.install(RemoveActionPlugin.default());
 | 
			
		||||
        this.install(MoveActionPlugin.default());
 | 
			
		||||
@@ -305,51 +268,6 @@ define([
 | 
			
		||||
 | 
			
		||||
    MCT.prototype.MCT = MCT;
 | 
			
		||||
 | 
			
		||||
    MCT.prototype.legacyExtension = function (category, extension) {
 | 
			
		||||
        this.legacyBundle.extensions[category] =
 | 
			
		||||
            this.legacyBundle.extensions[category] || [];
 | 
			
		||||
        this.legacyBundle.extensions[category].push(extension);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Return a legacy object, for compatibility purposes only.  This method
 | 
			
		||||
     * will be deprecated and removed in the future.
 | 
			
		||||
     * @private
 | 
			
		||||
     */
 | 
			
		||||
    MCT.prototype.legacyObject = function (domainObject) {
 | 
			
		||||
        let capabilityService = this.$injector.get('capabilityService');
 | 
			
		||||
 | 
			
		||||
        function instantiate(model, keyString) {
 | 
			
		||||
            const capabilities = capabilityService.getCapabilities(model, keyString);
 | 
			
		||||
            model.id = keyString;
 | 
			
		||||
 | 
			
		||||
            return new DomainObjectImpl(keyString, model, capabilities);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (Array.isArray(domainObject)) {
 | 
			
		||||
            // an array of domain objects. [object, ...ancestors] representing
 | 
			
		||||
            // a single object with a given chain of ancestors.  We instantiate
 | 
			
		||||
            // as a single contextual domain object.
 | 
			
		||||
            return domainObject
 | 
			
		||||
                .map((o) => {
 | 
			
		||||
                    let keyString = objectUtils.makeKeyString(o.identifier);
 | 
			
		||||
                    let oldModel = objectUtils.toOldFormat(o);
 | 
			
		||||
 | 
			
		||||
                    return instantiate(oldModel, keyString);
 | 
			
		||||
                })
 | 
			
		||||
                .reverse()
 | 
			
		||||
                .reduce((parent, child) => {
 | 
			
		||||
                    return new ContextualDomainObject(child, parent);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
        } else {
 | 
			
		||||
            let keyString = objectUtils.makeKeyString(domainObject.identifier);
 | 
			
		||||
            let oldModel = objectUtils.toOldFormat(domainObject);
 | 
			
		||||
 | 
			
		||||
            return instantiate(oldModel, keyString);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set path to where assets are hosted.  This should be the path to main.js.
 | 
			
		||||
     * @memberof module:openmct.MCT#
 | 
			
		||||
@@ -395,25 +313,6 @@ define([
 | 
			
		||||
 | 
			
		||||
        this.element = domElement;
 | 
			
		||||
 | 
			
		||||
        this.legacyExtension('runs', {
 | 
			
		||||
            depends: ['navigationService'],
 | 
			
		||||
            implementation: function (navigationService) {
 | 
			
		||||
                navigationService
 | 
			
		||||
                    .addListener(this.emit.bind(this, 'navigation'));
 | 
			
		||||
            }.bind(this)
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // TODO: remove with legacy types.
 | 
			
		||||
        this.types.listKeys().forEach(function (typeKey) {
 | 
			
		||||
            const type = this.types.get(typeKey);
 | 
			
		||||
            const legacyDefinition = type.toLegacyDefinition();
 | 
			
		||||
            legacyDefinition.key = typeKey;
 | 
			
		||||
            this.legacyExtension('types', legacyDefinition);
 | 
			
		||||
        }.bind(this));
 | 
			
		||||
 | 
			
		||||
        this.legacyRegistry.register('adapter', this.legacyBundle);
 | 
			
		||||
        this.legacyRegistry.enable('adapter');
 | 
			
		||||
 | 
			
		||||
        this.router.route(/^\/$/, () => {
 | 
			
		||||
            this.router.setPath('/browse/');
 | 
			
		||||
        });
 | 
			
		||||
@@ -424,35 +323,27 @@ define([
 | 
			
		||||
         * @event start
 | 
			
		||||
         * @memberof module:openmct.MCT~
 | 
			
		||||
         */
 | 
			
		||||
        const startPromise = new Main();
 | 
			
		||||
        startPromise.run(this)
 | 
			
		||||
            .then(function (angular) {
 | 
			
		||||
                this.$angular = angular;
 | 
			
		||||
                // OpenMCT Object provider doesn't operate properly unless
 | 
			
		||||
                // something has depended upon objectService.  Cool, right?
 | 
			
		||||
                this.$injector.get('objectService');
 | 
			
		||||
 | 
			
		||||
                if (!isHeadlessMode) {
 | 
			
		||||
                    const appLayout = new Vue({
 | 
			
		||||
                        components: {
 | 
			
		||||
                            'Layout': Layout.default
 | 
			
		||||
                        },
 | 
			
		||||
                        provide: {
 | 
			
		||||
                            openmct: this
 | 
			
		||||
                        },
 | 
			
		||||
                        template: '<Layout ref="layout"></Layout>'
 | 
			
		||||
                    });
 | 
			
		||||
                    domElement.appendChild(appLayout.$mount().$el);
 | 
			
		||||
        if (!isHeadlessMode) {
 | 
			
		||||
            const appLayout = new Vue({
 | 
			
		||||
                components: {
 | 
			
		||||
                    'Layout': Layout.default
 | 
			
		||||
                },
 | 
			
		||||
                provide: {
 | 
			
		||||
                    openmct: this
 | 
			
		||||
                },
 | 
			
		||||
                template: '<Layout ref="layout"></Layout>'
 | 
			
		||||
            });
 | 
			
		||||
            domElement.appendChild(appLayout.$mount().$el);
 | 
			
		||||
 | 
			
		||||
                    this.layout = appLayout.$refs.layout;
 | 
			
		||||
                    Browse(this);
 | 
			
		||||
                }
 | 
			
		||||
            this.layout = appLayout.$refs.layout;
 | 
			
		||||
            Browse(this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
                window.addEventListener('beforeunload', this.destroy);
 | 
			
		||||
        window.addEventListener('beforeunload', this.destroy);
 | 
			
		||||
 | 
			
		||||
                this.router.start();
 | 
			
		||||
                this.emit('start');
 | 
			
		||||
            }.bind(this));
 | 
			
		||||
        this.router.start();
 | 
			
		||||
        this.emit('start');
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    MCT.prototype.startHeadless = function () {
 | 
			
		||||
 
 | 
			
		||||
@@ -22,21 +22,18 @@
 | 
			
		||||
 | 
			
		||||
define([
 | 
			
		||||
    './plugins/plugins',
 | 
			
		||||
    'legacyRegistry',
 | 
			
		||||
    'utils/testing'
 | 
			
		||||
], function (plugins, legacyRegistry, testUtils) {
 | 
			
		||||
], function (plugins, testUtils) {
 | 
			
		||||
    describe("MCT", function () {
 | 
			
		||||
        let openmct;
 | 
			
		||||
        let mockPlugin;
 | 
			
		||||
        let mockPlugin2;
 | 
			
		||||
        let mockListener;
 | 
			
		||||
        let oldBundles;
 | 
			
		||||
 | 
			
		||||
        beforeEach(function () {
 | 
			
		||||
            mockPlugin = jasmine.createSpy('plugin');
 | 
			
		||||
            mockPlugin2 = jasmine.createSpy('plugin2');
 | 
			
		||||
            mockListener = jasmine.createSpy('listener');
 | 
			
		||||
            oldBundles = legacyRegistry.list();
 | 
			
		||||
 | 
			
		||||
            openmct = testUtils.createOpenMct();
 | 
			
		||||
 | 
			
		||||
@@ -47,12 +44,6 @@ define([
 | 
			
		||||
 | 
			
		||||
        // Clean up the dirty singleton.
 | 
			
		||||
        afterEach(function () {
 | 
			
		||||
            legacyRegistry.list().forEach(function (bundle) {
 | 
			
		||||
                if (oldBundles.indexOf(bundle) === -1) {
 | 
			
		||||
                    legacyRegistry.delete(bundle);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            return testUtils.resetApplicationState(openmct);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
@@ -111,10 +102,6 @@ define([
 | 
			
		||||
        describe("setAssetPath", function () {
 | 
			
		||||
            let testAssetPath;
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                openmct.legacyExtension = jasmine.createSpy('legacyExtension');
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("configures the path for assets", function () {
 | 
			
		||||
                testAssetPath = "some/path/";
 | 
			
		||||
                openmct.setAssetPath(testAssetPath);
 | 
			
		||||
 
 | 
			
		||||
@@ -133,5 +133,9 @@ define([
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    CompositionAPI.prototype.supportsComposition = function (domainObject) {
 | 
			
		||||
        return this.get(domainObject) !== undefined;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return CompositionAPI;
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -87,6 +87,12 @@ define([
 | 
			
		||||
                expect(composition).toEqual(jasmine.any(CompositionCollection));
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it('correctly reflects composability', function () {
 | 
			
		||||
                expect(compositionAPI.supportsComposition(domainObject)).toBe(true);
 | 
			
		||||
                delete domainObject.composition;
 | 
			
		||||
                expect(compositionAPI.supportsComposition(domainObject)).toBe(false);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it('loads composition from domain object', function () {
 | 
			
		||||
                const listener = jasmine.createSpy('addListener');
 | 
			
		||||
                composition.on('add', listener);
 | 
			
		||||
 
 | 
			
		||||
@@ -49,8 +49,10 @@ define([
 | 
			
		||||
        this.onMutation = this.onMutation.bind(this);
 | 
			
		||||
 | 
			
		||||
        this.cannotContainItself = this.cannotContainItself.bind(this);
 | 
			
		||||
        this.supportsComposition = this.supportsComposition.bind(this);
 | 
			
		||||
 | 
			
		||||
        compositionAPI.addPolicy(this.cannotContainItself);
 | 
			
		||||
        compositionAPI.addPolicy(this.supportsComposition);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -61,6 +63,13 @@ define([
 | 
			
		||||
            && parent.identifier.key === child.identifier.key);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @private
 | 
			
		||||
     */
 | 
			
		||||
    DefaultCompositionProvider.prototype.supportsComposition = function (parent, child) {
 | 
			
		||||
        return this.publicAPI.composition.supportsComposition(parent);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check if this provider should be used to load composition for a
 | 
			
		||||
     * particular domain object.
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
import AutoCompleteField from './components/controls/AutoCompleteField.vue';
 | 
			
		||||
import ClockDisplayFormatField from './components/controls/ClockDisplayFormatField.vue';
 | 
			
		||||
import CheckBoxField from './components/controls/CheckBoxField.vue';
 | 
			
		||||
import Datetime from './components/controls/Datetime.vue';
 | 
			
		||||
import FileInput from './components/controls/FileInput.vue';
 | 
			
		||||
import Locator from './components/controls/Locator.vue';
 | 
			
		||||
@@ -12,6 +13,7 @@ import Vue from 'vue';
 | 
			
		||||
 | 
			
		||||
export const DEFAULT_CONTROLS_MAP = {
 | 
			
		||||
    'autocomplete': AutoCompleteField,
 | 
			
		||||
    'checkbox': CheckBoxField,
 | 
			
		||||
    'composite': ClockDisplayFormatField,
 | 
			
		||||
    'datetime': Datetime,
 | 
			
		||||
    'file-input': FileInput,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
* Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
* Open MCT, Copyright (c) 2014-2022, United States Government
 | 
			
		||||
* as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
* Administration. All rights reserved.
 | 
			
		||||
*
 | 
			
		||||
@@ -24,49 +24,59 @@
 | 
			
		||||
<div class="c-form">
 | 
			
		||||
    <div class="c-overlay__top-bar c-form__top-bar">
 | 
			
		||||
        <div class="c-overlay__dialog-title">{{ model.title }}</div>
 | 
			
		||||
        <div class="c-overlay__dialog-hint hint">All fields marked <span class="req icon-asterisk"></span> are required.</div>
 | 
			
		||||
        <div
 | 
			
		||||
            v-if="hasRequiredFields"
 | 
			
		||||
            class="c-overlay__dialog-hint hint"
 | 
			
		||||
        >All fields marked <span class="req icon-asterisk"></span> are required.</div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <form name="mctForm"
 | 
			
		||||
          class="c-form__contents"
 | 
			
		||||
          autocomplete="off"
 | 
			
		||||
          @submit.prevent
 | 
			
		||||
    <form
 | 
			
		||||
        name="mctForm"
 | 
			
		||||
        class="c-form__contents"
 | 
			
		||||
        autocomplete="off"
 | 
			
		||||
        @submit.prevent
 | 
			
		||||
    >
 | 
			
		||||
        <div v-for="section in formSections"
 | 
			
		||||
             :key="section.id"
 | 
			
		||||
             class="c-form__section"
 | 
			
		||||
             :class="section.cssClass"
 | 
			
		||||
        <div
 | 
			
		||||
            v-for="section in formSections"
 | 
			
		||||
            :key="section.id"
 | 
			
		||||
            class="c-form__section"
 | 
			
		||||
            :class="section.cssClass"
 | 
			
		||||
        >
 | 
			
		||||
            <h2 v-if="section.name"
 | 
			
		||||
            <h2
 | 
			
		||||
                v-if="section.name"
 | 
			
		||||
                class="c-form__section-header"
 | 
			
		||||
            >
 | 
			
		||||
                {{ section.name }}
 | 
			
		||||
            </h2>
 | 
			
		||||
            <div v-for="(row, index) in section.rows"
 | 
			
		||||
                 :key="row.id"
 | 
			
		||||
                 class="u-contents"
 | 
			
		||||
            <div
 | 
			
		||||
                v-for="(row, index) in section.rows"
 | 
			
		||||
                :key="row.id"
 | 
			
		||||
                class="u-contents"
 | 
			
		||||
            >
 | 
			
		||||
                <FormRow :css-class="section.cssClass"
 | 
			
		||||
                         :first="index < 1"
 | 
			
		||||
                         :row="row"
 | 
			
		||||
                         @onChange="onChange"
 | 
			
		||||
                <FormRow
 | 
			
		||||
                    :css-class="section.cssClass"
 | 
			
		||||
                    :first="index < 1"
 | 
			
		||||
                    :row="row"
 | 
			
		||||
                    @onChange="onChange"
 | 
			
		||||
                />
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </form>
 | 
			
		||||
 | 
			
		||||
    <div class="mct-form__controls c-overlay__button-bar c-form__bottom-bar">
 | 
			
		||||
        <button tabindex="0"
 | 
			
		||||
                :disabled="isInvalid"
 | 
			
		||||
                class="c-button c-button--major"
 | 
			
		||||
                @click="onSave"
 | 
			
		||||
        <button
 | 
			
		||||
            tabindex="0"
 | 
			
		||||
            :disabled="isInvalid"
 | 
			
		||||
            class="c-button c-button--major"
 | 
			
		||||
            @click="onSave"
 | 
			
		||||
        >
 | 
			
		||||
            OK
 | 
			
		||||
            {{ submitLabel }}
 | 
			
		||||
        </button>
 | 
			
		||||
        <button tabindex="0"
 | 
			
		||||
                class="c-button"
 | 
			
		||||
                @click="onDismiss"
 | 
			
		||||
        <button
 | 
			
		||||
            tabindex="0"
 | 
			
		||||
            class="c-button"
 | 
			
		||||
            @click="onDismiss"
 | 
			
		||||
        >
 | 
			
		||||
            Cancel
 | 
			
		||||
            {{ cancelLabel }}
 | 
			
		||||
        </button>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
@@ -75,7 +85,6 @@
 | 
			
		||||
<script>
 | 
			
		||||
import FormRow from "@/api/forms/components/FormRow.vue";
 | 
			
		||||
import uuid from 'uuid';
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    components: {
 | 
			
		||||
        FormRow
 | 
			
		||||
@@ -100,30 +109,50 @@ export default {
 | 
			
		||||
        };
 | 
			
		||||
    },
 | 
			
		||||
    computed: {
 | 
			
		||||
        hasRequiredFields() {
 | 
			
		||||
            return this.model.sections.some(section =>
 | 
			
		||||
                section.rows.some(row => row.required));
 | 
			
		||||
        },
 | 
			
		||||
        isInvalid() {
 | 
			
		||||
            return Object.entries(this.invalidProperties)
 | 
			
		||||
                .some(([key, value]) => {
 | 
			
		||||
                    return value;
 | 
			
		||||
                });
 | 
			
		||||
        },
 | 
			
		||||
        submitLabel() {
 | 
			
		||||
            if (
 | 
			
		||||
                this.model.buttons
 | 
			
		||||
                && this.model.buttons.submit
 | 
			
		||||
                && this.model.buttons.submit.label
 | 
			
		||||
            ) {
 | 
			
		||||
                return this.model.buttons.submit.label;
 | 
			
		||||
            }
 | 
			
		||||
            return 'OK';
 | 
			
		||||
        },
 | 
			
		||||
        cancelLabel() {
 | 
			
		||||
            if (
 | 
			
		||||
                this.model.buttons
 | 
			
		||||
                && this.model.buttons.cancel
 | 
			
		||||
                && this.model.buttons.cancel.label
 | 
			
		||||
            ) {
 | 
			
		||||
                return this.model.buttons.submit.label;
 | 
			
		||||
            }
 | 
			
		||||
            return 'Cancel';
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    mounted() {
 | 
			
		||||
        this.formSections = this.model.sections.map(section => {
 | 
			
		||||
            section.id = uuid();
 | 
			
		||||
 | 
			
		||||
            section.rows = section.rows.map(row => {
 | 
			
		||||
                row.id = uuid();
 | 
			
		||||
 | 
			
		||||
                return row;
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            return section;
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
        onChange(data) {
 | 
			
		||||
            this.$set(this.invalidProperties, data.model.key, data.invalid);
 | 
			
		||||
 | 
			
		||||
            this.$emit('onChange', data);
 | 
			
		||||
        },
 | 
			
		||||
        onDismiss() {
 | 
			
		||||
 
 | 
			
		||||
@@ -75,10 +75,12 @@ export default {
 | 
			
		||||
        rowClass() {
 | 
			
		||||
            let cssClass = this.cssClass;
 | 
			
		||||
 | 
			
		||||
            if (this.row.required) {
 | 
			
		||||
                cssClass = `${cssClass} req`;
 | 
			
		||||
            if (!this.row.required) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            cssClass = `${cssClass} req`;
 | 
			
		||||
 | 
			
		||||
            if (this.visited && this.valid !== undefined) {
 | 
			
		||||
                if (this.valid === true) {
 | 
			
		||||
                    cssClass = `${cssClass} valid`;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										62
									
								
								src/api/forms/components/controls/CheckBoxField.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								src/api/forms/components/controls/CheckBoxField.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,62 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
* Open MCT, Copyright (c) 2014-2022, United States Government
 | 
			
		||||
* as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
* Administration. All rights reserved.
 | 
			
		||||
*
 | 
			
		||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
* "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
* You may obtain a copy of the License at
 | 
			
		||||
* http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
*
 | 
			
		||||
* Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
* License for the specific language governing permissions and limitations
 | 
			
		||||
* under the License.
 | 
			
		||||
*
 | 
			
		||||
* Open MCT includes source code licensed under additional open source
 | 
			
		||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
* this source code distribution or the Licensing information page available
 | 
			
		||||
* at runtime from the About dialog for additional information.
 | 
			
		||||
*****************************************************************************/
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
<span class="form-control shell">
 | 
			
		||||
    <span
 | 
			
		||||
        class="field control"
 | 
			
		||||
        :class="model.cssClass"
 | 
			
		||||
    >
 | 
			
		||||
        <input
 | 
			
		||||
            type="checkbox"
 | 
			
		||||
            :checked="isChecked"
 | 
			
		||||
            @input="toggleCheckBox"
 | 
			
		||||
        >
 | 
			
		||||
    </span>
 | 
			
		||||
</span>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
export default {
 | 
			
		||||
    props: {
 | 
			
		||||
        model: {
 | 
			
		||||
            type: Object,
 | 
			
		||||
            required: true
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    data() {
 | 
			
		||||
        return {
 | 
			
		||||
            isChecked: this.model.value
 | 
			
		||||
        };
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
        toggleCheckBox() {
 | 
			
		||||
            this.isChecked = !this.isChecked;
 | 
			
		||||
            const data = {
 | 
			
		||||
                model: this.model,
 | 
			
		||||
                value: this.isChecked
 | 
			
		||||
            };
 | 
			
		||||
            this.$emit('onChange', data);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
</script>
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2022, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
@@ -36,13 +36,14 @@ class InMemorySearchProvider {
 | 
			
		||||
         */
 | 
			
		||||
        this.MAX_CONCURRENT_REQUESTS = 100;
 | 
			
		||||
        /**
 | 
			
		||||
        * If max results is not specified in query, use this as default.
 | 
			
		||||
        */
 | 
			
		||||
         * If max results is not specified in query, use this as default.
 | 
			
		||||
         */
 | 
			
		||||
        this.DEFAULT_MAX_RESULTS = 100;
 | 
			
		||||
 | 
			
		||||
        this.openmct = openmct;
 | 
			
		||||
 | 
			
		||||
        this.indexedIds = {};
 | 
			
		||||
        this.indexedCompositions = {};
 | 
			
		||||
        this.idsToIndex = [];
 | 
			
		||||
        this.pendingIndex = {};
 | 
			
		||||
        this.pendingRequests = 0;
 | 
			
		||||
@@ -58,7 +59,6 @@ class InMemorySearchProvider {
 | 
			
		||||
        this.onWorkerMessageError = this.onWorkerMessageError.bind(this);
 | 
			
		||||
        this.onerror = this.onWorkerError.bind(this);
 | 
			
		||||
        this.startIndexing = this.startIndexing.bind(this);
 | 
			
		||||
        this.onMutationOfIndexedObject = this.onMutationOfIndexedObject.bind(this);
 | 
			
		||||
 | 
			
		||||
        this.openmct.on('start', this.startIndexing);
 | 
			
		||||
        this.openmct.on('destroy', () => {
 | 
			
		||||
@@ -68,6 +68,9 @@ class InMemorySearchProvider {
 | 
			
		||||
                this.worker.port.onmessageerror = null;
 | 
			
		||||
                this.worker.port.close();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.destroyObservers(this.indexedIds);
 | 
			
		||||
            this.destroyObservers(this.indexedCompositions);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -137,7 +140,7 @@ class InMemorySearchProvider {
 | 
			
		||||
        };
 | 
			
		||||
        modelResults.hits = await Promise.all(event.data.results.map(async (hit) => {
 | 
			
		||||
            const identifier = this.openmct.objects.parseKeyString(hit.keyString);
 | 
			
		||||
            const domainObject = await this.openmct.objects.get(identifier.key);
 | 
			
		||||
            const domainObject = await this.openmct.objects.get(identifier);
 | 
			
		||||
 | 
			
		||||
            return domainObject;
 | 
			
		||||
        }));
 | 
			
		||||
@@ -213,29 +216,52 @@ class InMemorySearchProvider {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    onMutationOfIndexedObject(domainObject) {
 | 
			
		||||
    onNameMutation(domainObject, name) {
 | 
			
		||||
        const provider = this;
 | 
			
		||||
        provider.index(domainObject.identifier, domainObject);
 | 
			
		||||
 | 
			
		||||
        domainObject.name = name;
 | 
			
		||||
        provider.index(domainObject);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    onCompositionMutation(domainObject, composition) {
 | 
			
		||||
        const provider = this;
 | 
			
		||||
        const indexedComposition = domainObject.composition;
 | 
			
		||||
        const identifiersToIndex = composition
 | 
			
		||||
            .filter(identifier => !indexedComposition
 | 
			
		||||
                .some(indexedIdentifier => this.openmct.objects
 | 
			
		||||
                    .areIdsEqual([identifier, indexedIdentifier])));
 | 
			
		||||
 | 
			
		||||
        identifiersToIndex.forEach(identifier => {
 | 
			
		||||
            this.openmct.objects.get(identifier).then(objectToIndex => provider.index(objectToIndex));
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Pass an id and model to the worker to be indexed.  If the model has
 | 
			
		||||
     * composition, schedule those ids for later indexing.
 | 
			
		||||
     * Pass a domainObject to the worker to be indexed.
 | 
			
		||||
     * If the object has composition, schedule those ids for later indexing.
 | 
			
		||||
     * Watch for object changes and re-index object and children if so
 | 
			
		||||
     *
 | 
			
		||||
     * @private
 | 
			
		||||
     * @param id a model id
 | 
			
		||||
     * @param model a model
 | 
			
		||||
     * @param domainObject a domainObject
 | 
			
		||||
     */
 | 
			
		||||
    async index(id, domainObject) {
 | 
			
		||||
    async index(domainObject) {
 | 
			
		||||
        const provider = this;
 | 
			
		||||
        const keyString = this.openmct.objects.makeKeyString(id);
 | 
			
		||||
        const keyString = this.openmct.objects.makeKeyString(domainObject.identifier);
 | 
			
		||||
 | 
			
		||||
        if (!this.indexedIds[keyString]) {
 | 
			
		||||
            this.openmct.objects.observe(domainObject, `*`, this.onMutationOfIndexedObject);
 | 
			
		||||
            this.indexedIds[keyString] = this.openmct.objects.observe(
 | 
			
		||||
                domainObject,
 | 
			
		||||
                'name',
 | 
			
		||||
                this.onNameMutation.bind(this, domainObject)
 | 
			
		||||
            );
 | 
			
		||||
            this.indexedCompositions[keyString] = this.openmct.objects.observe(
 | 
			
		||||
                domainObject,
 | 
			
		||||
                'composition',
 | 
			
		||||
                this.onCompositionMutation.bind(this, domainObject)
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.indexedIds[keyString] = true;
 | 
			
		||||
 | 
			
		||||
        if ((id.key !== 'ROOT')) {
 | 
			
		||||
        if ((keyString !== 'ROOT')) {
 | 
			
		||||
            if (this.worker) {
 | 
			
		||||
                this.worker.port.postMessage({
 | 
			
		||||
                    request: 'index',
 | 
			
		||||
@@ -247,15 +273,12 @@ class InMemorySearchProvider {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const composition = this.openmct.composition.registry.find(foundComposition => {
 | 
			
		||||
            return foundComposition.appliesTo(domainObject);
 | 
			
		||||
        });
 | 
			
		||||
        const composition = this.openmct.composition.get(domainObject);
 | 
			
		||||
 | 
			
		||||
        if (composition) {
 | 
			
		||||
            const childIdentifiers = await composition.load(domainObject);
 | 
			
		||||
            childIdentifiers.forEach(function (childIdentifier) {
 | 
			
		||||
                provider.scheduleForIndexing(childIdentifier);
 | 
			
		||||
            });
 | 
			
		||||
        if (composition !== undefined) {
 | 
			
		||||
            const children = await composition.load();
 | 
			
		||||
 | 
			
		||||
            children.forEach(child => provider.scheduleForIndexing(child.identifier));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -271,12 +294,12 @@ class InMemorySearchProvider {
 | 
			
		||||
        const provider = this;
 | 
			
		||||
 | 
			
		||||
        this.pendingRequests += 1;
 | 
			
		||||
        const identifier = await this.openmct.objects.parseKeyString(keyString);
 | 
			
		||||
        const domainObject = await this.openmct.objects.get(identifier.key);
 | 
			
		||||
        const domainObject = await this.openmct.objects.get(keyString);
 | 
			
		||||
        delete provider.pendingIndex[keyString];
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            if (domainObject) {
 | 
			
		||||
                await provider.index(identifier, domainObject);
 | 
			
		||||
                await provider.index(domainObject);
 | 
			
		||||
            }
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            console.warn('Failed to index domain object ' + keyString, error);
 | 
			
		||||
@@ -305,9 +328,9 @@ class InMemorySearchProvider {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
    * A local version of the same SharedWorker function
 | 
			
		||||
    * if we don't have SharedWorkers available (e.g., iOS)
 | 
			
		||||
    */
 | 
			
		||||
     * A local version of the same SharedWorker function
 | 
			
		||||
     * if we don't have SharedWorkers available (e.g., iOS)
 | 
			
		||||
     */
 | 
			
		||||
    localIndexItem(keyString, model) {
 | 
			
		||||
        this.localIndexedItems[keyString] = {
 | 
			
		||||
            type: model.type,
 | 
			
		||||
@@ -347,6 +370,16 @@ class InMemorySearchProvider {
 | 
			
		||||
        };
 | 
			
		||||
        this.onWorkerMessage(eventToReturn);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    destroyObservers(observers) {
 | 
			
		||||
        Object.entries(observers).forEach(([keyString, unobserve]) => {
 | 
			
		||||
            if (typeof unobserve === 'function') {
 | 
			
		||||
                unobserve();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            delete observers[keyString];
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default InMemorySearchProvider;
 | 
			
		||||
 
 | 
			
		||||
@@ -33,8 +33,10 @@
 | 
			
		||||
 | 
			
		||||
        port.onmessage = function (event) {
 | 
			
		||||
            if (event.data.request === 'index') {
 | 
			
		||||
                console.log('onmessage index: ', event.data);
 | 
			
		||||
                indexItem(event.data.keyString, event.data.model);
 | 
			
		||||
            } else if (event.data.request === 'search') {
 | 
			
		||||
                console.log('onmessage search: ', event.data);
 | 
			
		||||
                port.postMessage(search(event.data));
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
@@ -77,6 +79,8 @@
 | 
			
		||||
            queryId: data.queryId
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        console.log('indexed on search: ', indexedItems);
 | 
			
		||||
 | 
			
		||||
        results = Object.values(indexedItems).filter((indexedItem) => {
 | 
			
		||||
            return indexedItem.name.toLowerCase().includes(input);
 | 
			
		||||
        });
 | 
			
		||||
 
 | 
			
		||||
@@ -55,6 +55,11 @@ define([
 | 
			
		||||
     */
 | 
			
		||||
    function parseKeyString(keyString) {
 | 
			
		||||
        if (isIdentifier(keyString)) {
 | 
			
		||||
            // hack to workaround a bug mashing keyString into identifier.key
 | 
			
		||||
            if (!keyString.namespace && keyString.key.includes(':')) {
 | 
			
		||||
                return parseKeyString(keyString.key);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return keyString;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -25,6 +25,7 @@ import EventEmitter from 'EventEmitter';
 | 
			
		||||
 | 
			
		||||
const ERRORS = {
 | 
			
		||||
    TIMESYSTEM_KEY: 'All telemetry metadata must have a telemetry value with a key that matches the key of the active time system.',
 | 
			
		||||
    TIMESYSTEM_KEY_NOTIFICATION: 'Telemetry metadata does not match the active time system.',
 | 
			
		||||
    LOADED: 'Telemetry Collection has already been loaded.'
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -266,6 +267,10 @@ export class TelemetryCollection extends EventEmitter {
 | 
			
		||||
        this.lastBounds = bounds;
 | 
			
		||||
 | 
			
		||||
        if (isTick) {
 | 
			
		||||
            if (this.timeKey === undefined) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // need to check futureBuffer and need to check
 | 
			
		||||
            // if anything has fallen out of bounds
 | 
			
		||||
            let startIndex = 0;
 | 
			
		||||
@@ -305,7 +310,6 @@ export class TelemetryCollection extends EventEmitter {
 | 
			
		||||
            if (added.length > 0) {
 | 
			
		||||
                this.emit('add', added);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        } else {
 | 
			
		||||
            // user bounds change, reset
 | 
			
		||||
            this._reset();
 | 
			
		||||
@@ -325,12 +329,14 @@ export class TelemetryCollection extends EventEmitter {
 | 
			
		||||
        let domains = this.metadata.valuesForHints(['domain']);
 | 
			
		||||
        let domain = domains.find((d) => d.key === timeSystem.key);
 | 
			
		||||
 | 
			
		||||
        if (domain === undefined) {
 | 
			
		||||
            this._error(ERRORS.TIMESYSTEM_KEY);
 | 
			
		||||
        if (domain !== undefined) {
 | 
			
		||||
            // timeKey is used to create a dummy datum used for sorting
 | 
			
		||||
            this.timeKey = domain.source;
 | 
			
		||||
        } else {
 | 
			
		||||
            this._warn(ERRORS.TIMESYSTEM_KEY);
 | 
			
		||||
            this.openmct.notifications.alert(ERRORS.TIMESYSTEM_KEY_NOTIFICATION)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // timeKey is used to create a dummy datum used for sorting
 | 
			
		||||
        this.timeKey = domain.source; // this defaults to key if no source is set
 | 
			
		||||
        
 | 
			
		||||
        let metadataValue = this.metadata.value(timeSystem.key) || { format: timeSystem.key };
 | 
			
		||||
        let valueFormatter = this.openmct.telemetry.getValueFormatter(metadataValue);
 | 
			
		||||
 | 
			
		||||
@@ -400,4 +406,8 @@ export class TelemetryCollection extends EventEmitter {
 | 
			
		||||
    _error(message) {
 | 
			
		||||
        throw new Error(message);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _warn(message) {
 | 
			
		||||
        console.warn(message);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -31,7 +31,7 @@ export default class LADTableViewProvider {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    canView(domainObject) {
 | 
			
		||||
        const supportsComposition = this.openmct.composition.get(domainObject) !== undefined;
 | 
			
		||||
        const supportsComposition = this.openmct.composition.supportsComposition(domainObject);
 | 
			
		||||
        const providesTelemetry = this.openmct.telemetry.isTelemetryObject(domainObject);
 | 
			
		||||
 | 
			
		||||
        return domainObject.type === 'LadTable'
 | 
			
		||||
 
 | 
			
		||||
@@ -130,14 +130,6 @@ describe("the plugin", function () {
 | 
			
		||||
        let mockComposition;
 | 
			
		||||
 | 
			
		||||
        beforeEach(async () => {
 | 
			
		||||
            const getFunc = openmct.$injector.get;
 | 
			
		||||
            spyOn(openmct.$injector, "get")
 | 
			
		||||
                .withArgs("exportImageService").and.returnValue({
 | 
			
		||||
                    exportPNG: () => {},
 | 
			
		||||
                    exportJPG: () => {}
 | 
			
		||||
                })
 | 
			
		||||
                .and.callFake(getFunc);
 | 
			
		||||
 | 
			
		||||
            barGraphObject = {
 | 
			
		||||
                identifier: {
 | 
			
		||||
                    namespace: "",
 | 
			
		||||
 
 | 
			
		||||
@@ -87,6 +87,7 @@ describe("Clock plugin:", () => {
 | 
			
		||||
 | 
			
		||||
            spyOn(openmct.objects, 'get').and.returnValue(Promise.resolve(clockViewObject));
 | 
			
		||||
            spyOn(openmct.objects, 'save').and.returnValue(Promise.resolve(true));
 | 
			
		||||
            spyOn(openmct.objects, 'supportsMutation').and.returnValue(true);
 | 
			
		||||
 | 
			
		||||
            const applicableViews = openmct.objectViews.get(clockViewObject, [clockViewObject]);
 | 
			
		||||
            clockViewProvider = applicableViews.find(viewProvider => viewProvider.key === 'clock.view');
 | 
			
		||||
 
 | 
			
		||||
@@ -1,34 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, 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.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
function ConditionSetViewPolicy() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ConditionSetViewPolicy.prototype.allow = function (view, domainObject) {
 | 
			
		||||
    if (domainObject.getModel().type === 'conditionSet') {
 | 
			
		||||
        return view.key === 'conditionSet.view';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default ConditionSetViewPolicy;
 | 
			
		||||
@@ -39,8 +39,10 @@ export default class ConditionSetViewProvider {
 | 
			
		||||
        return isConditionSet && this.openmct.router.isNavigatedObject(objectPath);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    canEdit(domainObject) {
 | 
			
		||||
        return domainObject.type === 'conditionSet';
 | 
			
		||||
    canEdit(domainObject, objectPath) {
 | 
			
		||||
        const isConditionSet = domainObject.type === 'conditionSet';
 | 
			
		||||
 | 
			
		||||
        return isConditionSet && this.openmct.router.isNavigatedObject(objectPath);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    view(domainObject, objectPath) {
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,6 @@ import ConditionSetViewProvider from './ConditionSetViewProvider.js';
 | 
			
		||||
import ConditionSetCompositionPolicy from "./ConditionSetCompositionPolicy";
 | 
			
		||||
import ConditionSetMetadataProvider from './ConditionSetMetadataProvider';
 | 
			
		||||
import ConditionSetTelemetryProvider from './ConditionSetTelemetryProvider';
 | 
			
		||||
import ConditionSetViewPolicy from './ConditionSetViewPolicy';
 | 
			
		||||
import uuid from "uuid";
 | 
			
		||||
 | 
			
		||||
export default function ConditionPlugin() {
 | 
			
		||||
@@ -55,11 +54,8 @@ export default function ConditionPlugin() {
 | 
			
		||||
                domainObject.telemetry = {};
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        openmct.legacyExtension('policies', {
 | 
			
		||||
            category: 'view',
 | 
			
		||||
            implementation: ConditionSetViewPolicy
 | 
			
		||||
        });
 | 
			
		||||
        openmct.composition.addPolicy(new ConditionSetCompositionPolicy(openmct).allow);
 | 
			
		||||
        let compositionPolicy = new ConditionSetCompositionPolicy(openmct);
 | 
			
		||||
        openmct.composition.addPolicy(compositionPolicy.allow.bind(compositionPolicy));
 | 
			
		||||
        openmct.telemetry.addProvider(new ConditionSetMetadataProvider(openmct));
 | 
			
		||||
        openmct.telemetry.addProvider(new ConditionSetTelemetryProvider(openmct));
 | 
			
		||||
        openmct.objectViews.addProvider(new ConditionSetViewProvider(openmct));
 | 
			
		||||
 
 | 
			
		||||
@@ -56,7 +56,7 @@ a.c-condition-widget {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// When the widget is in the main view, center it in the space
 | 
			
		||||
.l-shell__main-container > .c-condition-widget {
 | 
			
		||||
.l-shell__main-container > * > .c-condition-widget {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    top: 50%;
 | 
			
		||||
    left: 50%;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
@use 'sass:math';
 | 
			
		||||
 | 
			
		||||
/******************* FRAME */
 | 
			
		||||
.c-frame {
 | 
			
		||||
    display: flex;
 | 
			
		||||
@@ -89,7 +91,7 @@
 | 
			
		||||
                        &:before {
 | 
			
		||||
                            // Grippy
 | 
			
		||||
                            $h: 4px;
 | 
			
		||||
                            $tbOffset: ($editFrameMovebarH - $h) / 2;
 | 
			
		||||
                            $tbOffset: math.div($editFrameMovebarH - $h, 2);
 | 
			
		||||
                            $lrOffset: 25%;
 | 
			
		||||
                            @include grippy($editFrameMovebarColorFg);
 | 
			
		||||
                            content: '';
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,6 @@
 | 
			
		||||
import JSONExporter from '/src/exporters/JSONExporter.js';
 | 
			
		||||
 | 
			
		||||
import _ from 'lodash';
 | 
			
		||||
import { saveAs } from 'saveAs';
 | 
			
		||||
import uuid from "uuid";
 | 
			
		||||
 | 
			
		||||
export default class ExportAsJSONAction {
 | 
			
		||||
@@ -41,7 +40,7 @@ export default class ExportAsJSONAction {
 | 
			
		||||
        this.calls = 0;
 | 
			
		||||
        this.idMap = {};
 | 
			
		||||
 | 
			
		||||
        this.JSONExportService = new JSONExporter(saveAs);
 | 
			
		||||
        this.JSONExportService = new JSONExporter();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Public
 | 
			
		||||
@@ -68,7 +67,6 @@ export default class ExportAsJSONAction {
 | 
			
		||||
 | 
			
		||||
        this._write(this.root);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @private
 | 
			
		||||
     * @param {object} domainObject
 | 
			
		||||
@@ -116,6 +114,7 @@ export default class ExportAsJSONAction {
 | 
			
		||||
            return _.isEqual(child.identifier, id);
 | 
			
		||||
        });
 | 
			
		||||
        const copyOfChild = JSON.parse(JSON.stringify(child));
 | 
			
		||||
 | 
			
		||||
        copyOfChild.identifier.key = uuid();
 | 
			
		||||
        const newIdString = this._getId(copyOfChild);
 | 
			
		||||
        const parentId = this._getId(parent);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,252 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, 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/ExportAsJSONAction",
 | 
			
		||||
        "../../../entanglement/test/DomainObjectFactory",
 | 
			
		||||
        "../../../../src/MCT",
 | 
			
		||||
        '../../../../src/adapter/capabilities/AdapterCapability'
 | 
			
		||||
    ],
 | 
			
		||||
    function (ExportAsJSONAction, domainObjectFactory, MCT, AdapterCapability) {
 | 
			
		||||
 | 
			
		||||
        describe("The export JSON action", function () {
 | 
			
		||||
 | 
			
		||||
            let context;
 | 
			
		||||
            let action;
 | 
			
		||||
            let exportService;
 | 
			
		||||
            let identifierService;
 | 
			
		||||
            let typeService;
 | 
			
		||||
            let openmct;
 | 
			
		||||
            let policyService;
 | 
			
		||||
            let mockType;
 | 
			
		||||
            let mockObjectProvider;
 | 
			
		||||
            let exportedTree;
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                openmct = new MCT();
 | 
			
		||||
                mockObjectProvider = {
 | 
			
		||||
                    objects: {},
 | 
			
		||||
                    get: function (id) {
 | 
			
		||||
                        return Promise.resolve(mockObjectProvider.objects[id.key]);
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
                openmct.objects.addProvider('', mockObjectProvider);
 | 
			
		||||
 | 
			
		||||
                exportService = jasmine.createSpyObj('exportService',
 | 
			
		||||
                    ['exportJSON']);
 | 
			
		||||
                identifierService = jasmine.createSpyObj('identifierService',
 | 
			
		||||
                    ['generate']);
 | 
			
		||||
                policyService = jasmine.createSpyObj('policyService',
 | 
			
		||||
                    ['allow']);
 | 
			
		||||
                mockType = jasmine.createSpyObj('type', ['hasFeature']);
 | 
			
		||||
                typeService = jasmine.createSpyObj('typeService', [
 | 
			
		||||
                    'getType'
 | 
			
		||||
                ]);
 | 
			
		||||
 | 
			
		||||
                mockType.hasFeature.and.callFake(function (feature) {
 | 
			
		||||
                    return feature === 'creation';
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                typeService.getType.and.returnValue(mockType);
 | 
			
		||||
 | 
			
		||||
                context = {};
 | 
			
		||||
                context.domainObject = domainObjectFactory(
 | 
			
		||||
                    {
 | 
			
		||||
                        name: 'test',
 | 
			
		||||
                        id: 'someID',
 | 
			
		||||
                        capabilities: {
 | 
			
		||||
                            'adapter': {
 | 
			
		||||
                                invoke: invokeAdapter
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    });
 | 
			
		||||
                identifierService.generate.and.returnValue('brandNewId');
 | 
			
		||||
                exportService.exportJSON.and.callFake(function (tree, options) {
 | 
			
		||||
                    exportedTree = tree;
 | 
			
		||||
                });
 | 
			
		||||
                policyService.allow.and.callFake(function (capability, type) {
 | 
			
		||||
                    return type.hasFeature(capability);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                action = new ExportAsJSONAction(openmct, exportService, policyService,
 | 
			
		||||
                    identifierService, typeService, context);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            function invokeAdapter() {
 | 
			
		||||
                let newStyleObject = new AdapterCapability(context.domainObject).invoke();
 | 
			
		||||
 | 
			
		||||
                return newStyleObject;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            it("initializes happily", function () {
 | 
			
		||||
                expect(action).toBeDefined();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            xit("doesn't export non-creatable objects in tree", function () {
 | 
			
		||||
                let nonCreatableType = {
 | 
			
		||||
                    hasFeature:
 | 
			
		||||
                        function (feature) {
 | 
			
		||||
                            return feature !== 'creation';
 | 
			
		||||
                        }
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                typeService.getType.and.returnValue(nonCreatableType);
 | 
			
		||||
 | 
			
		||||
                let parent = domainObjectFactory({
 | 
			
		||||
                    name: 'parent',
 | 
			
		||||
                    model: {
 | 
			
		||||
                        name: 'parent',
 | 
			
		||||
                        location: 'ROOT',
 | 
			
		||||
                        composition: ['childId']
 | 
			
		||||
                    },
 | 
			
		||||
                    id: 'parentId',
 | 
			
		||||
                    capabilities: {
 | 
			
		||||
                        'adapter': {
 | 
			
		||||
                            invoke: invokeAdapter
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                let child = {
 | 
			
		||||
                    identifier: {
 | 
			
		||||
                        namespace: '',
 | 
			
		||||
                        key: 'childId'
 | 
			
		||||
                    },
 | 
			
		||||
                    name: 'child',
 | 
			
		||||
                    location: 'parentId'
 | 
			
		||||
                };
 | 
			
		||||
                context.domainObject = parent;
 | 
			
		||||
                addChild(child);
 | 
			
		||||
 | 
			
		||||
                action.perform();
 | 
			
		||||
 | 
			
		||||
                return new Promise(function (resolve, reject) {
 | 
			
		||||
                    setTimeout(resolve, 100);
 | 
			
		||||
                }).then(function () {
 | 
			
		||||
                    expect(Object.keys(action.tree).length).toBe(1);
 | 
			
		||||
                    expect(Object.prototype.hasOwnProperty.call(action.tree, "parentId"))
 | 
			
		||||
                        .toBeTruthy();
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            xit("can export self-containing objects", function () {
 | 
			
		||||
                let parent = domainObjectFactory({
 | 
			
		||||
                    name: 'parent',
 | 
			
		||||
                    model: {
 | 
			
		||||
                        name: 'parent',
 | 
			
		||||
                        location: 'ROOT',
 | 
			
		||||
                        composition: ['infiniteChildId']
 | 
			
		||||
                    },
 | 
			
		||||
                    id: 'infiniteParentId',
 | 
			
		||||
                    capabilities: {
 | 
			
		||||
                        'adapter': {
 | 
			
		||||
                            invoke: invokeAdapter
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                let child = {
 | 
			
		||||
                    identifier: {
 | 
			
		||||
                        namespace: '',
 | 
			
		||||
                        key: 'infiniteChildId'
 | 
			
		||||
                    },
 | 
			
		||||
                    name: 'child',
 | 
			
		||||
                    location: 'infiniteParentId',
 | 
			
		||||
                    composition: ['infiniteParentId']
 | 
			
		||||
                };
 | 
			
		||||
                addChild(child);
 | 
			
		||||
 | 
			
		||||
                context.domainObject = parent;
 | 
			
		||||
 | 
			
		||||
                action.perform();
 | 
			
		||||
 | 
			
		||||
                return new Promise(function (resolve, reject) {
 | 
			
		||||
                    setTimeout(resolve, 100);
 | 
			
		||||
                }).then(function () {
 | 
			
		||||
                    expect(Object.keys(action.tree).length).toBe(2);
 | 
			
		||||
                    expect(Object.prototype.hasOwnProperty.call(action.tree, "infiniteParentId"))
 | 
			
		||||
                        .toBeTruthy();
 | 
			
		||||
                    expect(Object.prototype.hasOwnProperty.call(action.tree, "infiniteChildId"))
 | 
			
		||||
                        .toBeTruthy();
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            xit("exports links to external objects as new objects", function () {
 | 
			
		||||
                let parent = domainObjectFactory({
 | 
			
		||||
                    name: 'parent',
 | 
			
		||||
                    model: {
 | 
			
		||||
                        name: 'parent',
 | 
			
		||||
                        composition: ['externalId'],
 | 
			
		||||
                        location: 'ROOT'
 | 
			
		||||
                    },
 | 
			
		||||
                    id: 'parentId',
 | 
			
		||||
                    capabilities: {
 | 
			
		||||
                        'adapter': {
 | 
			
		||||
                            invoke: invokeAdapter
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                let externalObject = {
 | 
			
		||||
                    name: 'external',
 | 
			
		||||
                    location: 'outsideOfTree',
 | 
			
		||||
                    identifier: {
 | 
			
		||||
                        namespace: '',
 | 
			
		||||
                        key: 'externalId'
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
                addChild(externalObject);
 | 
			
		||||
 | 
			
		||||
                context.domainObject = parent;
 | 
			
		||||
 | 
			
		||||
                return new Promise (function (resolve) {
 | 
			
		||||
                    action.perform();
 | 
			
		||||
                    setTimeout(resolve, 100);
 | 
			
		||||
                }).then(function () {
 | 
			
		||||
                    expect(Object.keys(action.tree).length).toBe(2);
 | 
			
		||||
                    expect(Object.prototype.hasOwnProperty.call(action.tree, "parentId"))
 | 
			
		||||
                        .toBeTruthy();
 | 
			
		||||
                    expect(Object.prototype.hasOwnProperty.call(action.tree, "brandNewId"))
 | 
			
		||||
                        .toBeTruthy();
 | 
			
		||||
                    expect(action.tree.brandNewId.location).toBe('parentId');
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("exports object tree in the correct format", function () {
 | 
			
		||||
                action.perform();
 | 
			
		||||
 | 
			
		||||
                return new Promise(function (resolve, reject) {
 | 
			
		||||
                    setTimeout(resolve, 100);
 | 
			
		||||
                }).then(function () {
 | 
			
		||||
                    expect(Object.keys(exportedTree).length).toBe(2);
 | 
			
		||||
                    expect(Object.prototype.hasOwnProperty.call(exportedTree, "openmct")).toBeTruthy();
 | 
			
		||||
                    expect(Object.prototype.hasOwnProperty.call(exportedTree, "rootId")).toBeTruthy();
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            function addChild(object) {
 | 
			
		||||
                mockObjectProvider.objects[object.identifier.key] = object;
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
							
								
								
									
										305
									
								
								src/plugins/exportAsJSONAction/ExportAsJSONActionSpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										305
									
								
								src/plugins/exportAsJSONAction/ExportAsJSONActionSpec.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,305 @@
 | 
			
		||||
import {
 | 
			
		||||
    createOpenMct,
 | 
			
		||||
    resetApplicationState
 | 
			
		||||
} from 'utils/testing';
 | 
			
		||||
 | 
			
		||||
describe('Export as JSON plugin', () => {
 | 
			
		||||
    const ACTION_KEY = 'export.JSON';
 | 
			
		||||
 | 
			
		||||
    let openmct;
 | 
			
		||||
    let domainObject;
 | 
			
		||||
    let exportAsJSONAction;
 | 
			
		||||
 | 
			
		||||
    beforeEach((done) => {
 | 
			
		||||
        openmct = createOpenMct();
 | 
			
		||||
 | 
			
		||||
        openmct.on('start', done);
 | 
			
		||||
        openmct.startHeadless();
 | 
			
		||||
 | 
			
		||||
        exportAsJSONAction = openmct.actions.getAction(ACTION_KEY);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    afterEach(() => resetApplicationState(openmct));
 | 
			
		||||
 | 
			
		||||
    it('Export as JSON action exist', () => {
 | 
			
		||||
        expect(exportAsJSONAction.key).toEqual(ACTION_KEY);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('ExportAsJSONAction applies to folder', () => {
 | 
			
		||||
        domainObject = {
 | 
			
		||||
            composition: [],
 | 
			
		||||
            location: 'mine',
 | 
			
		||||
            modified: 1640115501237,
 | 
			
		||||
            name: 'Unnamed Folder',
 | 
			
		||||
            persisted: 1640115501237,
 | 
			
		||||
            type: 'folder'
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        expect(exportAsJSONAction.appliesTo([domainObject])).toEqual(true);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('ExportAsJSONAction applies to telemetry.plot.overlay', () => {
 | 
			
		||||
        domainObject = {
 | 
			
		||||
            composition: [],
 | 
			
		||||
            location: 'mine',
 | 
			
		||||
            modified: 1640115501237,
 | 
			
		||||
            name: 'Unnamed Plot',
 | 
			
		||||
            persisted: 1640115501237,
 | 
			
		||||
            type: 'telemetry.plot.overlay'
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        expect(exportAsJSONAction.appliesTo([domainObject])).toEqual(true);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('ExportAsJSONAction applies to telemetry.plot.stacked', () => {
 | 
			
		||||
        domainObject = {
 | 
			
		||||
            composition: [],
 | 
			
		||||
            location: 'mine',
 | 
			
		||||
            modified: 1640115501237,
 | 
			
		||||
            name: 'Unnamed Plot',
 | 
			
		||||
            persisted: 1640115501237,
 | 
			
		||||
            type: 'telemetry.plot.stacked'
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        expect(exportAsJSONAction.appliesTo([domainObject])).toEqual(true);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('ExportAsJSONAction applies does not applies to non-creatable objects', () => {
 | 
			
		||||
        domainObject = {
 | 
			
		||||
            composition: [],
 | 
			
		||||
            location: 'mine',
 | 
			
		||||
            modified: 1640115501237,
 | 
			
		||||
            name: 'Non Editable Folder',
 | 
			
		||||
            persisted: 1640115501237,
 | 
			
		||||
            type: 'noneditable.folder'
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        expect(exportAsJSONAction.appliesTo([domainObject])).toEqual(false);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('ExportAsJSONAction exports object from tree', (done) => {
 | 
			
		||||
        const parent = {
 | 
			
		||||
            composition: [{
 | 
			
		||||
                key: 'child',
 | 
			
		||||
                namespace: ''
 | 
			
		||||
            }],
 | 
			
		||||
            identifier: {
 | 
			
		||||
                key: 'parent',
 | 
			
		||||
                namespace: ''
 | 
			
		||||
            },
 | 
			
		||||
            name: 'Parent',
 | 
			
		||||
            type: 'folder',
 | 
			
		||||
            modified: 1503598129176,
 | 
			
		||||
            location: 'mine',
 | 
			
		||||
            persisted: 1503598129176
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const child = {
 | 
			
		||||
            composition: [],
 | 
			
		||||
            identifier: {
 | 
			
		||||
                key: 'child',
 | 
			
		||||
                namespace: ''
 | 
			
		||||
            },
 | 
			
		||||
            name: 'Child',
 | 
			
		||||
            type: 'folder',
 | 
			
		||||
            modified: 1503598132428,
 | 
			
		||||
            location: 'parent',
 | 
			
		||||
            persisted: 1503598132428
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        spyOn(openmct.composition, 'get').and.callFake(object => {
 | 
			
		||||
            return {
 | 
			
		||||
                load: () => {
 | 
			
		||||
                    if (object.name === 'Parent') {
 | 
			
		||||
                        return Promise.resolve([child]);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    return Promise.resolve([]);
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        spyOn(exportAsJSONAction, '_saveAs').and.callFake(completedTree => {
 | 
			
		||||
            expect(Object.keys(completedTree).length).toBe(2);
 | 
			
		||||
            expect(Object.prototype.hasOwnProperty.call(completedTree, 'openmct')).toBeTruthy();
 | 
			
		||||
            expect(Object.prototype.hasOwnProperty.call(completedTree, 'rootId')).toBeTruthy();
 | 
			
		||||
            expect(Object.prototype.hasOwnProperty.call(completedTree.openmct, 'parent')).toBeTruthy();
 | 
			
		||||
            expect(Object.prototype.hasOwnProperty.call(completedTree.openmct, 'child')).toBeTruthy();
 | 
			
		||||
 | 
			
		||||
            done();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        exportAsJSONAction.invoke([parent]);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('ExportAsJSONAction skips non-creatable objects from tree', (done) => {
 | 
			
		||||
        const parent = {
 | 
			
		||||
            composition: [{
 | 
			
		||||
                key: 'child',
 | 
			
		||||
                namespace: ''
 | 
			
		||||
            }],
 | 
			
		||||
            identifier: {
 | 
			
		||||
                key: 'parent',
 | 
			
		||||
                namespace: ''
 | 
			
		||||
            },
 | 
			
		||||
            name: 'Parent of Non Editable Child Folder',
 | 
			
		||||
            type: 'folder',
 | 
			
		||||
            modified: 1503598129176,
 | 
			
		||||
            location: 'mine',
 | 
			
		||||
            persisted: 1503598129176
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const child = {
 | 
			
		||||
            composition: [],
 | 
			
		||||
            identifier: {
 | 
			
		||||
                key: 'child',
 | 
			
		||||
                namespace: ''
 | 
			
		||||
            },
 | 
			
		||||
            name: 'Non Editable Child Folder',
 | 
			
		||||
            type: 'noneditable.folder',
 | 
			
		||||
            modified: 1503598132428,
 | 
			
		||||
            location: 'parent',
 | 
			
		||||
            persisted: 1503598132428
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        spyOn(openmct.composition, 'get').and.callFake(object => {
 | 
			
		||||
            return {
 | 
			
		||||
                load: () => {
 | 
			
		||||
                    if (object.identifier.key === 'parent') {
 | 
			
		||||
                        return Promise.resolve([child]);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    return Promise.resolve([]);
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        spyOn(exportAsJSONAction, '_saveAs').and.callFake(completedTree => {
 | 
			
		||||
            expect(Object.keys(completedTree).length).toBe(2);
 | 
			
		||||
            expect(Object.prototype.hasOwnProperty.call(completedTree, 'openmct')).toBeTruthy();
 | 
			
		||||
            expect(Object.prototype.hasOwnProperty.call(completedTree, 'rootId')).toBeTruthy();
 | 
			
		||||
            expect(Object.prototype.hasOwnProperty.call(completedTree.openmct, 'parent')).toBeTruthy();
 | 
			
		||||
            expect(Object.prototype.hasOwnProperty.call(completedTree.openmct, 'child')).not.toBeTruthy();
 | 
			
		||||
 | 
			
		||||
            done();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        exportAsJSONAction.invoke([parent]);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('can export self-containing objects', (done) => {
 | 
			
		||||
        const parent = {
 | 
			
		||||
            composition: [{
 | 
			
		||||
                key: 'infinteChild',
 | 
			
		||||
                namespace: ''
 | 
			
		||||
            }],
 | 
			
		||||
            identifier: {
 | 
			
		||||
                key: 'infiniteParent',
 | 
			
		||||
                namespace: ''
 | 
			
		||||
            },
 | 
			
		||||
            name: 'parent',
 | 
			
		||||
            type: 'folder',
 | 
			
		||||
            modified: 1503598129176,
 | 
			
		||||
            location: 'mine',
 | 
			
		||||
            persisted: 1503598129176
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const child = {
 | 
			
		||||
            composition: [{
 | 
			
		||||
                key: 'infiniteParent',
 | 
			
		||||
                namespace: ''
 | 
			
		||||
            }],
 | 
			
		||||
            identifier: {
 | 
			
		||||
                key: 'infinteChild',
 | 
			
		||||
                namespace: ''
 | 
			
		||||
            },
 | 
			
		||||
            name: 'child',
 | 
			
		||||
            type: 'folder',
 | 
			
		||||
            modified: 1503598132428,
 | 
			
		||||
            location: 'infiniteParent',
 | 
			
		||||
            persisted: 1503598132428
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        spyOn(openmct.composition, 'get').and.callFake(object => {
 | 
			
		||||
            return {
 | 
			
		||||
                load: () => {
 | 
			
		||||
                    if (object.name === 'parent') {
 | 
			
		||||
                        return Promise.resolve([child]);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    return Promise.resolve([]);
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        spyOn(exportAsJSONAction, '_saveAs').and.callFake(completedTree => {
 | 
			
		||||
            expect(Object.keys(completedTree).length).toBe(2);
 | 
			
		||||
            expect(Object.prototype.hasOwnProperty.call(completedTree, 'openmct')).toBeTruthy();
 | 
			
		||||
            expect(Object.prototype.hasOwnProperty.call(completedTree, 'rootId')).toBeTruthy();
 | 
			
		||||
            expect(Object.prototype.hasOwnProperty.call(completedTree.openmct, 'infiniteParent')).toBeTruthy();
 | 
			
		||||
            expect(Object.prototype.hasOwnProperty.call(completedTree.openmct, 'infinteChild')).toBeTruthy();
 | 
			
		||||
 | 
			
		||||
            done();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        exportAsJSONAction.invoke([parent]);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('exports links to external objects as new objects', function (done) {
 | 
			
		||||
        const parent = {
 | 
			
		||||
            composition: [{
 | 
			
		||||
                key: 'child',
 | 
			
		||||
                namespace: ''
 | 
			
		||||
            }],
 | 
			
		||||
            identifier: {
 | 
			
		||||
                key: 'parent',
 | 
			
		||||
                namespace: ''
 | 
			
		||||
            },
 | 
			
		||||
            name: 'Parent',
 | 
			
		||||
            type: 'folder',
 | 
			
		||||
            modified: 1503598129176,
 | 
			
		||||
            location: 'mine',
 | 
			
		||||
            persisted: 1503598129176
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const child = {
 | 
			
		||||
            composition: [],
 | 
			
		||||
            identifier: {
 | 
			
		||||
                key: 'child',
 | 
			
		||||
                namespace: ''
 | 
			
		||||
            },
 | 
			
		||||
            name: 'Child',
 | 
			
		||||
            type: 'folder',
 | 
			
		||||
            modified: 1503598132428,
 | 
			
		||||
            location: 'outsideOfTree',
 | 
			
		||||
            persisted: 1503598132428
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        spyOn(openmct.composition, 'get').and.callFake(object => {
 | 
			
		||||
            return {
 | 
			
		||||
                load: () => {
 | 
			
		||||
                    if (object.name === 'Parent') {
 | 
			
		||||
                        return Promise.resolve([child]);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    return Promise.resolve([]);
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        spyOn(exportAsJSONAction, '_saveAs').and.callFake(completedTree => {
 | 
			
		||||
            expect(Object.keys(completedTree).length).toBe(2);
 | 
			
		||||
            expect(Object.prototype.hasOwnProperty.call(completedTree, 'openmct')).toBeTruthy();
 | 
			
		||||
            expect(Object.prototype.hasOwnProperty.call(completedTree, 'rootId')).toBeTruthy();
 | 
			
		||||
            expect(Object.prototype.hasOwnProperty.call(completedTree.openmct, 'parent')).toBeTruthy();
 | 
			
		||||
 | 
			
		||||
            // parent and child objects as part of openmct but child with new id/key
 | 
			
		||||
            expect(Object.prototype.hasOwnProperty.call(completedTree.openmct, 'child')).not.toBeTruthy();
 | 
			
		||||
            expect(Object.keys(completedTree.openmct).length).toBe(2);
 | 
			
		||||
 | 
			
		||||
            done();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        exportAsJSONAction.invoke([parent]);
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
@@ -1,7 +1,9 @@
 | 
			
		||||
@use 'sass:math';
 | 
			
		||||
 | 
			
		||||
@mixin containerGrippy($headerSize, $dir) {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    $h: 6px;
 | 
			
		||||
    $minorOffset: ($headerSize - $h) / 2;
 | 
			
		||||
    $minorOffset: math.div($headerSize - $h, 2);
 | 
			
		||||
    $majorOffset: 35%;
 | 
			
		||||
    content: '';
 | 
			
		||||
    display: block;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
@use 'sass:math';
 | 
			
		||||
 | 
			
		||||
/******************************* GRID VIEW */
 | 
			
		||||
.l-grid-view {
 | 
			
		||||
    display: flex;
 | 
			
		||||
@@ -42,7 +44,7 @@
 | 
			
		||||
    &__type-icon {
 | 
			
		||||
        filter: $colorKeyFilter;
 | 
			
		||||
        flex: 0 0 $gridItemMobile;
 | 
			
		||||
        font-size: floor($gridItemMobile / 2);
 | 
			
		||||
        font-size: floor(math.div($gridItemMobile, 2));
 | 
			
		||||
        margin-right: $interiorMarginLg;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -166,7 +168,7 @@
 | 
			
		||||
 | 
			
		||||
        &__type-icon {
 | 
			
		||||
            flex: 1 1 auto;
 | 
			
		||||
            font-size: floor($gridItemDesk / 3);
 | 
			
		||||
            font-size: floor(math.div($gridItemDesk, 3));
 | 
			
		||||
            margin: $interiorMargin 22.5% $interiorMargin * 3 22.5%;
 | 
			
		||||
            order: 2;
 | 
			
		||||
            transform-origin: center;
 | 
			
		||||
 
 | 
			
		||||
@@ -66,10 +66,11 @@ export default class CreateAction extends PropertiesAction {
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        const parentDomainObject = parentDomainObjectPath[0];
 | 
			
		||||
        const namespace = parentDomainObject.identifier.namespace || parentDomainObject.key || '';
 | 
			
		||||
 | 
			
		||||
        this.domainObject.modified = Date.now();
 | 
			
		||||
        this.domainObject.location = this.openmct.objects.makeKeyString(parentDomainObject.identifier);
 | 
			
		||||
        this.domainObject.identifier.namespace = parentDomainObject.identifier.namespace;
 | 
			
		||||
        this.domainObject.identifier.namespace = namespace;
 | 
			
		||||
 | 
			
		||||
        // Show saving progress dialog
 | 
			
		||||
        let dialog = this.openmct.overlays.progressDialog({
 | 
			
		||||
@@ -99,6 +100,7 @@ export default class CreateAction extends PropertiesAction {
 | 
			
		||||
     */
 | 
			
		||||
    async _navigateAndEdit(domainObject, parentDomainObjectpath) {
 | 
			
		||||
        let objectPath;
 | 
			
		||||
        let self = this;
 | 
			
		||||
        if (parentDomainObjectpath) {
 | 
			
		||||
            objectPath = parentDomainObjectpath && [domainObject].concat(parentDomainObjectpath);
 | 
			
		||||
        } else {
 | 
			
		||||
@@ -106,17 +108,22 @@ export default class CreateAction extends PropertiesAction {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const url = '#/browse/' + objectPath
 | 
			
		||||
            .map(object => object && this.openmct.objects.makeKeyString(object.identifier.key))
 | 
			
		||||
            .map(object => object && this.openmct.objects.makeKeyString(object.identifier))
 | 
			
		||||
            .reverse()
 | 
			
		||||
            .join('/');
 | 
			
		||||
 | 
			
		||||
        this.openmct.router.navigate(url);
 | 
			
		||||
        function editObject() {
 | 
			
		||||
            const objectView = self.openmct.objectViews.get(domainObject, objectPath)[0];
 | 
			
		||||
            const canEdit = objectView && objectView.canEdit && objectView.canEdit(domainObject, objectPath);
 | 
			
		||||
 | 
			
		||||
        const objectView = this.openmct.objectViews.get(domainObject, objectPath)[0];
 | 
			
		||||
        const canEdit = objectView && objectView.canEdit && objectView.canEdit(domainObject, objectPath);
 | 
			
		||||
        if (canEdit) {
 | 
			
		||||
            this.openmct.editor.edit();
 | 
			
		||||
            if (canEdit) {
 | 
			
		||||
                self.openmct.editor.edit();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.openmct.router.once('afterNavigation', editObject);
 | 
			
		||||
 | 
			
		||||
        this.openmct.router.navigate(url);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
@@ -65,13 +65,8 @@ export default {
 | 
			
		||||
            keyString: undefined
 | 
			
		||||
        };
 | 
			
		||||
    },
 | 
			
		||||
    computed: {
 | 
			
		||||
        imageHistorySize() {
 | 
			
		||||
            return this.imageHistory.length;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    watch: {
 | 
			
		||||
        imageHistorySize(newSize, oldSize) {
 | 
			
		||||
        imageHistory(newHistory, oldHistory) {
 | 
			
		||||
            this.updatePlotImagery();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 
 | 
			
		||||
@@ -240,9 +240,6 @@ export default {
 | 
			
		||||
        };
 | 
			
		||||
    },
 | 
			
		||||
    computed: {
 | 
			
		||||
        imageHistorySize() {
 | 
			
		||||
            return this.imageHistory.length;
 | 
			
		||||
        },
 | 
			
		||||
        compassRoseSizingClasses() {
 | 
			
		||||
            let compassRoseSizingClasses = '';
 | 
			
		||||
            if (this.sizedImageDimensions.width < 300) {
 | 
			
		||||
@@ -409,19 +406,23 @@ export default {
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    watch: {
 | 
			
		||||
        imageHistorySize(newSize, oldSize) {
 | 
			
		||||
            let imageIndex;
 | 
			
		||||
            if (this.focusedImageTimestamp !== undefined) {
 | 
			
		||||
                const foundImageIndex = this.imageHistory.findIndex(image => {
 | 
			
		||||
                    return image.time === this.focusedImageTimestamp;
 | 
			
		||||
                });
 | 
			
		||||
                imageIndex = foundImageIndex > -1 ? foundImageIndex : newSize - 1;
 | 
			
		||||
            } else {
 | 
			
		||||
                imageIndex = newSize > 0 ? newSize - 1 : undefined;
 | 
			
		||||
            }
 | 
			
		||||
        imageHistory: {
 | 
			
		||||
            handler(newHistory, oldHistory) {
 | 
			
		||||
                const newSize = newHistory.length;
 | 
			
		||||
                let imageIndex;
 | 
			
		||||
                if (this.focusedImageTimestamp !== undefined) {
 | 
			
		||||
                    const foundImageIndex = this.imageHistory.findIndex(image => {
 | 
			
		||||
                        return image.time === this.focusedImageTimestamp;
 | 
			
		||||
                    });
 | 
			
		||||
                    imageIndex = foundImageIndex > -1 ? foundImageIndex : newSize - 1;
 | 
			
		||||
                } else {
 | 
			
		||||
                    imageIndex = newSize > 0 ? newSize - 1 : undefined;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            this.setFocusedImage(imageIndex, false);
 | 
			
		||||
            this.scrollToRight();
 | 
			
		||||
                this.setFocusedImage(imageIndex, false);
 | 
			
		||||
                this.scrollToRight();
 | 
			
		||||
            },
 | 
			
		||||
            deep: true
 | 
			
		||||
        },
 | 
			
		||||
        focusedImageIndex() {
 | 
			
		||||
            this.trackDuration();
 | 
			
		||||
@@ -510,12 +511,6 @@ export default {
 | 
			
		||||
                this.timeContext.off("clock", this.trackDuration);
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        boundsChange(bounds, isTick) {
 | 
			
		||||
            if (!isTick) {
 | 
			
		||||
                this.previousFocusedImage = this.focusedImage ? JSON.parse(JSON.stringify(this.focusedImage)) : undefined;
 | 
			
		||||
                this.requestHistory();
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        expand() {
 | 
			
		||||
            const actionCollection = this.openmct.actions.getActionsCollection(this.objectPath, this.currentView);
 | 
			
		||||
            const visibleActions = actionCollection.getVisibleActions();
 | 
			
		||||
@@ -690,22 +685,32 @@ export default {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (this.previousFocusedImage) {
 | 
			
		||||
                // determine if the previous image exists in the new bounds of imageHistory
 | 
			
		||||
                const matchIndex = this.matchIndexOfPreviousImage(
 | 
			
		||||
                    this.previousFocusedImage,
 | 
			
		||||
                    this.imageHistory
 | 
			
		||||
                );
 | 
			
		||||
                focusedIndex = matchIndex > -1 ? matchIndex : this.imageHistory.length - 1;
 | 
			
		||||
 | 
			
		||||
                delete this.previousFocusedImage;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (thumbnailClick) {
 | 
			
		||||
                //We use the props till the user changes what they want to see
 | 
			
		||||
                this.focusedImageTimestamp = undefined;
 | 
			
		||||
                //set the previousFocusedImage when a user chooses an image
 | 
			
		||||
                this.previousFocusedImage = this.imageHistory[focusedIndex] ? JSON.parse(JSON.stringify(this.imageHistory[focusedIndex])) : undefined;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (this.previousFocusedImage) {
 | 
			
		||||
                // determine if the previous image exists in the new bounds of imageHistory
 | 
			
		||||
                if (!thumbnailClick) {
 | 
			
		||||
                    const matchIndex = this.matchIndexOfPreviousImage(
 | 
			
		||||
                        this.previousFocusedImage,
 | 
			
		||||
                        this.imageHistory
 | 
			
		||||
                    );
 | 
			
		||||
                    focusedIndex = matchIndex > -1 ? matchIndex : this.imageHistory.length - 1;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (!(this.isPaused || thumbnailClick)
 | 
			
		||||
                    || focusedIndex === this.imageHistory.length - 1) {
 | 
			
		||||
                    delete this.previousFocusedImage;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.focusedImageIndex = focusedIndex;
 | 
			
		||||
 | 
			
		||||
            //TODO: do we even need this anymore?
 | 
			
		||||
            if (this.isPaused && !thumbnailClick && this.focusedImageTimestamp === undefined) {
 | 
			
		||||
                this.nextImageIndex = focusedIndex;
 | 
			
		||||
                //this could happen if bounds changes
 | 
			
		||||
@@ -716,8 +721,6 @@ export default {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.focusedImageIndex = focusedIndex;
 | 
			
		||||
 | 
			
		||||
            if (thumbnailClick && !this.isPaused) {
 | 
			
		||||
                this.paused(true);
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -120,9 +120,15 @@ export default {
 | 
			
		||||
            return this.timeFormatter.parse(datum);
 | 
			
		||||
        },
 | 
			
		||||
        boundsChange(bounds, isTick) {
 | 
			
		||||
            if (!isTick) {
 | 
			
		||||
                this.requestHistory();
 | 
			
		||||
            if (isTick) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // forcibly reset the imageContainer size to prevent an aspect ratio distortion
 | 
			
		||||
            delete this.imageContainerWidth;
 | 
			
		||||
            delete this.imageContainerHeight;
 | 
			
		||||
 | 
			
		||||
            return this.requestHistory();
 | 
			
		||||
        },
 | 
			
		||||
        async requestHistory() {
 | 
			
		||||
            let bounds = this.timeContext.bounds();
 | 
			
		||||
 
 | 
			
		||||
@@ -68,6 +68,7 @@ export default class ImportAsJSONAction {
 | 
			
		||||
     * @param {object} object
 | 
			
		||||
     * @param {object} changes
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    onSave(object, changes) {
 | 
			
		||||
        const selectFile = changes.selectFile;
 | 
			
		||||
        const objectTree = selectFile.body;
 | 
			
		||||
 
 | 
			
		||||
@@ -43,42 +43,42 @@ const DEFAULTS = [
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
define([
 | 
			
		||||
    '../src/adapter/bundle',
 | 
			
		||||
    '../example/eventGenerator/bundle',
 | 
			
		||||
    '../example/export/bundle',
 | 
			
		||||
    '../example/forms/bundle',
 | 
			
		||||
    '../example/identity/bundle',
 | 
			
		||||
    '../example/mobile/bundle',
 | 
			
		||||
    '../example/msl/bundle',
 | 
			
		||||
    '../example/notifications/bundle',
 | 
			
		||||
    '../example/persistence/bundle',
 | 
			
		||||
    '../example/policy/bundle',
 | 
			
		||||
    '../example/profiling/bundle',
 | 
			
		||||
    '../example/scratchpad/bundle',
 | 
			
		||||
    '../example/styleguide/bundle',
 | 
			
		||||
    '../platform/commonUI/browse/bundle',
 | 
			
		||||
    '../platform/commonUI/dialog/bundle',
 | 
			
		||||
    '../platform/commonUI/edit/bundle',
 | 
			
		||||
    '../platform/commonUI/general/bundle',
 | 
			
		||||
    '../platform/commonUI/inspect/bundle',
 | 
			
		||||
    '../platform/commonUI/mobile/bundle',
 | 
			
		||||
    '../platform/commonUI/notification/bundle',
 | 
			
		||||
    '../platform/commonUI/regions/bundle',
 | 
			
		||||
    '../platform/containment/bundle',
 | 
			
		||||
    '../platform/core/bundle',
 | 
			
		||||
    '../platform/entanglement/bundle',
 | 
			
		||||
    '../platform/exporters/bundle',
 | 
			
		||||
    '../platform/features/static-markup/bundle',
 | 
			
		||||
    '../platform/framework/bundle',
 | 
			
		||||
    '../platform/framework/src/load/Bundle',
 | 
			
		||||
    '../platform/identity/bundle',
 | 
			
		||||
    '../platform/persistence/aggregator/bundle',
 | 
			
		||||
    '../platform/persistence/elastic/bundle',
 | 
			
		||||
    '../platform/persistence/queue/bundle',
 | 
			
		||||
    '../platform/policy/bundle',
 | 
			
		||||
    '../platform/representation/bundle',
 | 
			
		||||
    '../platform/status/bundle',
 | 
			
		||||
    '../platform/telemetry/bundle'
 | 
			
		||||
    '../../adapter/bundle',
 | 
			
		||||
    '../../../example/eventGenerator/bundle',
 | 
			
		||||
    '../../../example/export/bundle',
 | 
			
		||||
    '../../../example/forms/bundle',
 | 
			
		||||
    '../../../example/identity/bundle',
 | 
			
		||||
    '../../../example/mobile/bundle',
 | 
			
		||||
    '../../../example/msl/bundle',
 | 
			
		||||
    '../../../example/notifications/bundle',
 | 
			
		||||
    '../../../example/persistence/bundle',
 | 
			
		||||
    '../../../example/policy/bundle',
 | 
			
		||||
    '../../../example/profiling/bundle',
 | 
			
		||||
    '../../../example/scratchpad/bundle',
 | 
			
		||||
    '../../../example/styleguide/bundle',
 | 
			
		||||
    '../../../platform/commonUI/browse/bundle',
 | 
			
		||||
    '../../../platform/commonUI/dialog/bundle',
 | 
			
		||||
    '../../../platform/commonUI/edit/bundle',
 | 
			
		||||
    '../../../platform/commonUI/general/bundle',
 | 
			
		||||
    '../../../platform/commonUI/inspect/bundle',
 | 
			
		||||
    '../../../platform/commonUI/mobile/bundle',
 | 
			
		||||
    '../../../platform/commonUI/notification/bundle',
 | 
			
		||||
    '../../../platform/commonUI/regions/bundle',
 | 
			
		||||
    '../../../platform/containment/bundle',
 | 
			
		||||
    '../../../platform/core/bundle',
 | 
			
		||||
    '../../../platform/entanglement/bundle',
 | 
			
		||||
    '../../../platform/exporters/bundle',
 | 
			
		||||
    '../../../platform/features/static-markup/bundle',
 | 
			
		||||
    '../../../platform/framework/bundle',
 | 
			
		||||
    '../../../platform/framework/src/load/Bundle',
 | 
			
		||||
    '../../../platform/identity/bundle',
 | 
			
		||||
    '../../../platform/persistence/aggregator/bundle',
 | 
			
		||||
    '../../../platform/persistence/elastic/bundle',
 | 
			
		||||
    '../../../platform/persistence/queue/bundle',
 | 
			
		||||
    '../../../platform/policy/bundle',
 | 
			
		||||
    '../../../platform/representation/bundle',
 | 
			
		||||
    '../../../platform/status/bundle',
 | 
			
		||||
    '../../../platform/telemetry/bundle'
 | 
			
		||||
], function () {
 | 
			
		||||
    const LEGACY_BUNDLES = Array.from(arguments);
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										126
									
								
								src/plugins/legacySupport/plugin.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								src/plugins/legacySupport/plugin.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,126 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
import installDefaultBundles from './installDefaultBundles';
 | 
			
		||||
import BundleRegistry from './BundleRegistry';
 | 
			
		||||
import Main from '../../../platform/framework/src/Main';
 | 
			
		||||
import objectUtils from '../../api/objects/object-utils';
 | 
			
		||||
import DomainObjectImpl from '../../../platform/core/src/objects/DomainObjectImpl';
 | 
			
		||||
import ContextualDomainObject from '../../../platform/core/src/capabilities/ContextualDomainObject';
 | 
			
		||||
 | 
			
		||||
export default function LegacySupportPlugin() {
 | 
			
		||||
    return function install(openmct) {
 | 
			
		||||
        openmct.legacyBundle = {
 | 
			
		||||
            extensions: {
 | 
			
		||||
                services: [
 | 
			
		||||
                    {
 | 
			
		||||
                        key: "openmct",
 | 
			
		||||
                        implementation: function ($injector) {
 | 
			
		||||
                            openmct.$injector = $injector;
 | 
			
		||||
 | 
			
		||||
                            return openmct;
 | 
			
		||||
                        },
 | 
			
		||||
                        depends: ['$injector']
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        openmct.legacyExtension = function (category, extension) {
 | 
			
		||||
            this.legacyBundle.extensions[category] =
 | 
			
		||||
                this.legacyBundle.extensions[category] || [];
 | 
			
		||||
            this.legacyBundle.extensions[category].push(extension);
 | 
			
		||||
        }.bind(openmct);
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Return a legacy object, for compatibility purposes only.  This method
 | 
			
		||||
         * will be deprecated and removed in the future.
 | 
			
		||||
         * @private
 | 
			
		||||
         */
 | 
			
		||||
        openmct.legacyObject = function (domainObject) {
 | 
			
		||||
            let capabilityService = this.$injector.get('capabilityService');
 | 
			
		||||
 | 
			
		||||
            function instantiate(model, keyString) {
 | 
			
		||||
                const capabilities = capabilityService.getCapabilities(model, keyString);
 | 
			
		||||
                model.id = keyString;
 | 
			
		||||
 | 
			
		||||
                return new DomainObjectImpl(keyString, model, capabilities);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (Array.isArray(domainObject)) {
 | 
			
		||||
                // an array of domain objects. [object, ...ancestors] representing
 | 
			
		||||
                // a single object with a given chain of ancestors.  We instantiate
 | 
			
		||||
                // as a single contextual domain object.
 | 
			
		||||
                return domainObject
 | 
			
		||||
                    .map((o) => {
 | 
			
		||||
                        let keyString = objectUtils.makeKeyString(o.identifier);
 | 
			
		||||
                        let oldModel = objectUtils.toOldFormat(o);
 | 
			
		||||
 | 
			
		||||
                        return instantiate(oldModel, keyString);
 | 
			
		||||
                    })
 | 
			
		||||
                    .reverse()
 | 
			
		||||
                    .reduce((parent, child) => {
 | 
			
		||||
                        return new ContextualDomainObject(child, parent);
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
            } else {
 | 
			
		||||
                let keyString = objectUtils.makeKeyString(domainObject.identifier);
 | 
			
		||||
                let oldModel = objectUtils.toOldFormat(domainObject);
 | 
			
		||||
 | 
			
		||||
                return instantiate(oldModel, keyString);
 | 
			
		||||
            }
 | 
			
		||||
        }.bind(openmct);
 | 
			
		||||
 | 
			
		||||
        openmct.legacyRegistry = new BundleRegistry();
 | 
			
		||||
        installDefaultBundles(openmct.legacyRegistry);
 | 
			
		||||
 | 
			
		||||
        const patchedStart = openmct.start.bind(openmct);
 | 
			
		||||
        openmct.start = async () => {
 | 
			
		||||
            openmct.legacyRegistry.register('adapter', openmct.legacyBundle);
 | 
			
		||||
            openmct.legacyRegistry.enable('adapter');
 | 
			
		||||
 | 
			
		||||
            openmct.legacyExtension('runs', {
 | 
			
		||||
                depends: ['navigationService'],
 | 
			
		||||
                implementation: function (navigationService) {
 | 
			
		||||
                    navigationService
 | 
			
		||||
                        .addListener(openmct.emit.bind(openmct, 'navigation'));
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            // TODO: remove with legacy types.
 | 
			
		||||
            openmct.types.listKeys().forEach(function (typeKey) {
 | 
			
		||||
                const type = openmct.types.get(typeKey);
 | 
			
		||||
                const legacyDefinition = type.toLegacyDefinition();
 | 
			
		||||
                legacyDefinition.key = typeKey;
 | 
			
		||||
                openmct.legacyExtension('types', legacyDefinition);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            const main = new Main();
 | 
			
		||||
            const angularInstance = await main.run(openmct);
 | 
			
		||||
 | 
			
		||||
            openmct.$angular = angularInstance;
 | 
			
		||||
            openmct.$injector.get('objectService');
 | 
			
		||||
 | 
			
		||||
            return patchedStart();
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
@@ -91,11 +91,11 @@ export default class LinkAction {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    validate(currentParent) {
 | 
			
		||||
        return (object, data) => {
 | 
			
		||||
            const parentCandidate = data.value;
 | 
			
		||||
        return (data) => {
 | 
			
		||||
            const parentCandidate = data.value[0];
 | 
			
		||||
            const currentParentKeystring = this.openmct.objects.makeKeyString(currentParent.identifier);
 | 
			
		||||
            const parentCandidateKeystring = this.openmct.objects.makeKeyString(parentCandidate.identifier);
 | 
			
		||||
            const objectKeystring = this.openmct.objects.makeKeyString(object.identifier);
 | 
			
		||||
            const objectKeystring = this.openmct.objects.makeKeyString(this.object.identifier);
 | 
			
		||||
 | 
			
		||||
            if (!parentCandidateKeystring || !currentParentKeystring) {
 | 
			
		||||
                return false;
 | 
			
		||||
@@ -114,7 +114,7 @@ export default class LinkAction {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return parentCandidate && this.openmct.composition.checkPolicy(parentCandidate, object);
 | 
			
		||||
            return parentCandidate && this.openmct.composition.checkPolicy(parentCandidate, this.object);
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -140,11 +140,13 @@ export default class MoveAction {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    validate(currentParent) {
 | 
			
		||||
        return (object, data) => {
 | 
			
		||||
            const parentCandidate = data.value;
 | 
			
		||||
        return (data) => {
 | 
			
		||||
            const parentCandidatePath = data.value;
 | 
			
		||||
            const parentCandidate = parentCandidatePath[0];
 | 
			
		||||
 | 
			
		||||
            let currentParentKeystring = this.openmct.objects.makeKeyString(currentParent.identifier);
 | 
			
		||||
            let parentCandidateKeystring = this.openmct.objects.makeKeyString(parentCandidate.identifier);
 | 
			
		||||
            let objectKeystring = this.openmct.objects.makeKeyString(object.identifier);
 | 
			
		||||
            let objectKeystring = this.openmct.objects.makeKeyString(this.object.identifier);
 | 
			
		||||
 | 
			
		||||
            if (!parentCandidateKeystring || !currentParentKeystring) {
 | 
			
		||||
                return false;
 | 
			
		||||
@@ -163,7 +165,7 @@ export default class MoveAction {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return parentCandidate && this.openmct.composition.checkPolicy(parentCandidate, object);
 | 
			
		||||
            return parentCandidate && this.openmct.composition.checkPolicy(parentCandidate, this.object);
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -22,14 +22,14 @@
 | 
			
		||||
 | 
			
		||||
import { MY_ITEMS_KEY } from "./createMyItemsIdentifier";
 | 
			
		||||
 | 
			
		||||
function myItemsInterceptor(identifierObject, openmct) {
 | 
			
		||||
function myItemsInterceptor(openmct, identifierObject, name) {
 | 
			
		||||
 | 
			
		||||
    const myItemsModel = {
 | 
			
		||||
        identifier: identifierObject,
 | 
			
		||||
        "name": "My Items",
 | 
			
		||||
        "type": "folder",
 | 
			
		||||
        "composition": [],
 | 
			
		||||
        "location": "ROOT"
 | 
			
		||||
        name,
 | 
			
		||||
        type: "folder",
 | 
			
		||||
        composition: [],
 | 
			
		||||
        location: "ROOT"
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
 
 | 
			
		||||
@@ -23,11 +23,13 @@
 | 
			
		||||
import { createMyItemsIdentifier } from "./createMyItemsIdentifier";
 | 
			
		||||
import myItemsInterceptor from "./myItemsInterceptor";
 | 
			
		||||
 | 
			
		||||
export default function MyItemsPlugin(namespace = '') {
 | 
			
		||||
const MY_ITEMS_DEFAULT_NAME = 'My Items';
 | 
			
		||||
 | 
			
		||||
export default function MyItemsPlugin(name = MY_ITEMS_DEFAULT_NAME, namespace = '') {
 | 
			
		||||
    return function install(openmct) {
 | 
			
		||||
        const identifier = createMyItemsIdentifier(namespace);
 | 
			
		||||
 | 
			
		||||
        openmct.objects.addGetInterceptor(myItemsInterceptor(identifier, openmct));
 | 
			
		||||
        openmct.objects.addGetInterceptor(myItemsInterceptor(openmct, identifier, name));
 | 
			
		||||
        openmct.objects.addRoot(identifier);
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -30,6 +30,8 @@ import {
 | 
			
		||||
} from './createMyItemsIdentifier';
 | 
			
		||||
 | 
			
		||||
const MISSING_NAME = `Missing: ${MY_ITEMS_KEY}`;
 | 
			
		||||
const DEFAULT_NAME = 'My Items';
 | 
			
		||||
const FANCY_NAME = 'Fancy Items';
 | 
			
		||||
const myItemsIdentifier = createMyItemsIdentifier();
 | 
			
		||||
 | 
			
		||||
describe("the plugin", () => {
 | 
			
		||||
@@ -40,53 +42,82 @@ describe("the plugin", () => {
 | 
			
		||||
        name: MISSING_NAME
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    beforeEach((done) => {
 | 
			
		||||
        openmct = createOpenMct();
 | 
			
		||||
    describe('with no arguments passed in', () => {
 | 
			
		||||
 | 
			
		||||
        openmct.install(openmct.plugins.MyItems());
 | 
			
		||||
        beforeEach((done) => {
 | 
			
		||||
            openmct = createOpenMct();
 | 
			
		||||
            openmct.install(openmct.plugins.MyItems());
 | 
			
		||||
 | 
			
		||||
        openmct.on('start', done);
 | 
			
		||||
        openmct.startHeadless();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    afterEach(() => {
 | 
			
		||||
        return resetApplicationState(openmct);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('when installed, adds "My Items" to the root', async () => {
 | 
			
		||||
        const root = await openmct.objects.get('ROOT');
 | 
			
		||||
        const rootCompostionCollection = openmct.composition.get(root);
 | 
			
		||||
        const rootCompostion = await rootCompostionCollection.load();
 | 
			
		||||
        let myItems = rootCompostion.filter((domainObject) => {
 | 
			
		||||
            return openmct.objects.areIdsEqual(domainObject.identifier, myItemsIdentifier);
 | 
			
		||||
        })[0];
 | 
			
		||||
 | 
			
		||||
        expect(myItems).toBeDefined();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    describe('adds an interceptor that returns a "My Items" model for', () => {
 | 
			
		||||
        let myItemsMissing;
 | 
			
		||||
        let mockMissingProvider;
 | 
			
		||||
        let activeProvider;
 | 
			
		||||
 | 
			
		||||
        beforeEach(async () => {
 | 
			
		||||
            mockMissingProvider = {
 | 
			
		||||
                get: () => Promise.resolve(missingObj),
 | 
			
		||||
                create: () => Promise.resolve(missingObj),
 | 
			
		||||
                update: () => Promise.resolve(missingObj)
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            activeProvider = mockMissingProvider;
 | 
			
		||||
            spyOn(openmct.objects, 'getProvider').and.returnValue(activeProvider);
 | 
			
		||||
            myItemsMissing = await openmct.objects.get(myItemsIdentifier);
 | 
			
		||||
            openmct.on('start', done);
 | 
			
		||||
            openmct.startHeadless();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('missing objects', () => {
 | 
			
		||||
            let idsMatchMissing = openmct.objects.areIdsEqual(myItemsMissing.identifier, myItemsIdentifier);
 | 
			
		||||
 | 
			
		||||
            expect(myItemsMissing).toBeDefined();
 | 
			
		||||
            expect(idsMatchMissing).toBeTrue();
 | 
			
		||||
        afterEach(() => {
 | 
			
		||||
            return resetApplicationState(openmct);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('when installed, adds "My Items" to the root', async () => {
 | 
			
		||||
            const root = await openmct.objects.get('ROOT');
 | 
			
		||||
            const rootCompostionCollection = openmct.composition.get(root);
 | 
			
		||||
            const rootCompostion = await rootCompostionCollection.load();
 | 
			
		||||
            let myItems = rootCompostion.filter((domainObject) => {
 | 
			
		||||
                return openmct.objects.areIdsEqual(domainObject.identifier, myItemsIdentifier);
 | 
			
		||||
            })[0];
 | 
			
		||||
 | 
			
		||||
            expect(myItems.name).toBe(DEFAULT_NAME);
 | 
			
		||||
            expect(myItems).toBeDefined();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        describe('adds an interceptor that returns a "My Items" model for', () => {
 | 
			
		||||
            let myItemsMissing;
 | 
			
		||||
            let mockMissingProvider;
 | 
			
		||||
            let activeProvider;
 | 
			
		||||
 | 
			
		||||
            beforeEach(async () => {
 | 
			
		||||
                mockMissingProvider = {
 | 
			
		||||
                    get: () => Promise.resolve(missingObj),
 | 
			
		||||
                    create: () => Promise.resolve(missingObj),
 | 
			
		||||
                    update: () => Promise.resolve(missingObj)
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                activeProvider = mockMissingProvider;
 | 
			
		||||
                spyOn(openmct.objects, 'getProvider').and.returnValue(activeProvider);
 | 
			
		||||
                myItemsMissing = await openmct.objects.get(myItemsIdentifier);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it('missing objects', () => {
 | 
			
		||||
                let idsMatchMissing = openmct.objects.areIdsEqual(myItemsMissing.identifier, myItemsIdentifier);
 | 
			
		||||
 | 
			
		||||
                expect(myItemsMissing).toBeDefined();
 | 
			
		||||
                expect(idsMatchMissing).toBeTrue();
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    describe('with a name argument passed in', () => {
 | 
			
		||||
 | 
			
		||||
        beforeEach((done) => {
 | 
			
		||||
            openmct = createOpenMct();
 | 
			
		||||
            openmct.install(openmct.plugins.MyItems(FANCY_NAME));
 | 
			
		||||
 | 
			
		||||
            spyOn(openmct.objects, 'isMissing').and.returnValue(true);
 | 
			
		||||
 | 
			
		||||
            openmct.on('start', done);
 | 
			
		||||
            openmct.startHeadless();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        afterEach(() => {
 | 
			
		||||
            return resetApplicationState(openmct);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('when installed, uses the passed in name', async () => {
 | 
			
		||||
            let myItems = await openmct.objects.get(myItemsIdentifier);
 | 
			
		||||
 | 
			
		||||
            expect(myItems.name).toBe(FANCY_NAME);
 | 
			
		||||
            expect(myItems).toBeDefined();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -142,7 +142,6 @@ import { clearDefaultNotebook, getDefaultNotebook, setDefaultNotebook, setDefaul
 | 
			
		||||
import { addNotebookEntry, createNewEmbed, getEntryPosById, getNotebookEntries, mutateObject } from '../utils/notebook-entries';
 | 
			
		||||
import { saveNotebookImageDomainObject, updateNamespaceOfDomainObject } from '../utils/notebook-image';
 | 
			
		||||
import { NOTEBOOK_VIEW_TYPE } from '../notebook-constants';
 | 
			
		||||
import objectUtils from 'objectUtils';
 | 
			
		||||
 | 
			
		||||
import { debounce } from 'lodash';
 | 
			
		||||
import objectLink from '../../../ui/mixins/object-link';
 | 
			
		||||
@@ -455,11 +454,6 @@ export default {
 | 
			
		||||
                ? getDefaultNotebook().defaultSectionId
 | 
			
		||||
                : undefined;
 | 
			
		||||
        },
 | 
			
		||||
        getDefaultNotebookObject() {
 | 
			
		||||
            const defaultNotebook = getDefaultNotebook();
 | 
			
		||||
 | 
			
		||||
            return defaultNotebook && this.openmct.objects.get(defaultNotebook.identifier);
 | 
			
		||||
        },
 | 
			
		||||
        getLinktoNotebook() {
 | 
			
		||||
            const objectPath = this.openmct.router.path;
 | 
			
		||||
            const link = objectLink.computed.objectLink.call({
 | 
			
		||||
@@ -619,12 +613,12 @@ export default {
 | 
			
		||||
 | 
			
		||||
            this.sectionsChanged({ sections });
 | 
			
		||||
        },
 | 
			
		||||
        removeDefaultClass(domainObject) {
 | 
			
		||||
            if (!domainObject) {
 | 
			
		||||
        removeDefaultClass(defaultNotebookIdentifier) {
 | 
			
		||||
            if (!defaultNotebookIdentifier) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.openmct.status.delete(domainObject.identifier);
 | 
			
		||||
            this.openmct.status.delete(defaultNotebookIdentifier);
 | 
			
		||||
        },
 | 
			
		||||
        resetSearch() {
 | 
			
		||||
            this.search = '';
 | 
			
		||||
@@ -633,15 +627,16 @@ export default {
 | 
			
		||||
        toggleNav() {
 | 
			
		||||
            this.showNav = !this.showNav;
 | 
			
		||||
        },
 | 
			
		||||
        async updateDefaultNotebook(notebookStorage) {
 | 
			
		||||
            const defaultNotebookObject = await this.getDefaultNotebookObject();
 | 
			
		||||
            const isSameNotebook = defaultNotebookObject
 | 
			
		||||
                && objectUtils.makeKeyString(defaultNotebookObject.identifier) === objectUtils.makeKeyString(notebookStorage.identifier);
 | 
			
		||||
        updateDefaultNotebook(notebookStorage) {
 | 
			
		||||
            const defaultNotebook = getDefaultNotebook();
 | 
			
		||||
            const defaultNotebookIdentifier = defaultNotebook && defaultNotebook.identifier;
 | 
			
		||||
            const isSameNotebook = defaultNotebookIdentifier
 | 
			
		||||
                && this.openmct.objects.areIdsEqual(defaultNotebookIdentifier, notebookStorage.identifier);
 | 
			
		||||
            if (!isSameNotebook) {
 | 
			
		||||
                this.removeDefaultClass(defaultNotebookObject);
 | 
			
		||||
                this.removeDefaultClass(defaultNotebookIdentifier);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!defaultNotebookObject || !isSameNotebook) {
 | 
			
		||||
            if (!defaultNotebookIdentifier || !isSameNotebook) {
 | 
			
		||||
                setDefaultNotebook(this.openmct, notebookStorage, this.domainObject);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -245,6 +245,7 @@ export default {
 | 
			
		||||
                element: this.snapshot.$el,
 | 
			
		||||
                onDestroy: () => this.snapshot.$destroy(true),
 | 
			
		||||
                size: 'large',
 | 
			
		||||
                autoHide: false,
 | 
			
		||||
                dismissable: true,
 | 
			
		||||
                buttons: [
 | 
			
		||||
                    {
 | 
			
		||||
 
 | 
			
		||||
@@ -110,7 +110,8 @@ export default class Snapshot {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return () => {
 | 
			
		||||
            window.location.href = window.location.origin + url;
 | 
			
		||||
            const path = window.location.href.split('#');
 | 
			
		||||
            window.location.href = path[0] + url;
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -105,11 +105,6 @@ export function addNotebookEntry(openmct, domainObject, notebookStorage, embed =
 | 
			
		||||
    const date = Date.now();
 | 
			
		||||
    const configuration = domainObject.configuration;
 | 
			
		||||
    const entries = configuration.entries || {};
 | 
			
		||||
 | 
			
		||||
    if (!entries) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const embeds = embed
 | 
			
		||||
        ? [embed]
 | 
			
		||||
        : [];
 | 
			
		||||
@@ -125,7 +120,8 @@ export function addNotebookEntry(openmct, domainObject, notebookStorage, embed =
 | 
			
		||||
    const newEntries = addEntryIntoPage(notebookStorage, entries, entry);
 | 
			
		||||
 | 
			
		||||
    addDefaultClass(domainObject, openmct);
 | 
			
		||||
    domainObject.configuration.entries = newEntries;
 | 
			
		||||
 | 
			
		||||
    mutateObject(openmct, domainObject, 'configuration.entries', newEntries);
 | 
			
		||||
 | 
			
		||||
    return id;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,6 @@ import {
 | 
			
		||||
describe('the plugin', () => {
 | 
			
		||||
    let notificationIndicatorPlugin;
 | 
			
		||||
    let openmct;
 | 
			
		||||
    let indicatorObject;
 | 
			
		||||
    let indicatorElement;
 | 
			
		||||
    let parentElement;
 | 
			
		||||
    let mockMessages = ['error', 'test', 'notifications'];
 | 
			
		||||
@@ -43,9 +42,6 @@ describe('the plugin', () => {
 | 
			
		||||
 | 
			
		||||
        parentElement = document.createElement('div');
 | 
			
		||||
 | 
			
		||||
        indicatorObject = openmct.indicators.indicatorObjects.find(indicator => indicator.key === 'notifications-indicator');
 | 
			
		||||
        indicatorElement = indicatorObject.element;
 | 
			
		||||
 | 
			
		||||
        openmct.on('start', () => {
 | 
			
		||||
            mockMessages.forEach(message => {
 | 
			
		||||
                openmct.notifications.error(message);
 | 
			
		||||
@@ -53,7 +49,7 @@ describe('the plugin', () => {
 | 
			
		||||
            done();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        openmct.startHeadless();
 | 
			
		||||
        openmct.start();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    afterEach(() => {
 | 
			
		||||
@@ -68,7 +64,7 @@ describe('the plugin', () => {
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('notifies the user of the number of notifications', () => {
 | 
			
		||||
            let notificationCountElement = parentElement.querySelector('.c-indicator__count');
 | 
			
		||||
            let notificationCountElement = document.querySelector('.c-indicator__count');
 | 
			
		||||
 | 
			
		||||
            expect(notificationCountElement.innerText).toEqual(mockMessages.length.toString());
 | 
			
		||||
        });
 | 
			
		||||
 
 | 
			
		||||
@@ -278,7 +278,7 @@ export default {
 | 
			
		||||
            // Have to throw away the old canvas elements and replace with new
 | 
			
		||||
            // canvas elements in order to get new drawing contexts.
 | 
			
		||||
            const div = document.createElement('div');
 | 
			
		||||
            div.innerHTML = this.TEMPLATE;
 | 
			
		||||
            div.innerHTML = this.canvasTemplate + this.canvasTemplate;
 | 
			
		||||
            const mainCanvas = div.querySelectorAll("canvas")[1];
 | 
			
		||||
            const overlayCanvas = div.querySelectorAll("canvas")[0];
 | 
			
		||||
            this.canvas.parentNode.replaceChild(mainCanvas, this.canvas);
 | 
			
		||||
 
 | 
			
		||||
@@ -34,7 +34,7 @@ export default function () {
 | 
			
		||||
            name: "Overlay Plot",
 | 
			
		||||
            cssClass: "icon-plot-overlay",
 | 
			
		||||
            description: "Combine multiple telemetry elements and view them together as a plot with common X and Y axes. Can be added to Display Layouts.",
 | 
			
		||||
            creatable: "true",
 | 
			
		||||
            creatable: true,
 | 
			
		||||
            initialize: function (domainObject) {
 | 
			
		||||
                domainObject.composition = [];
 | 
			
		||||
                domainObject.configuration = {
 | 
			
		||||
 
 | 
			
		||||
@@ -533,13 +533,6 @@ describe("the plugin", function () {
 | 
			
		||||
        let plotViewComponentObject;
 | 
			
		||||
 | 
			
		||||
        beforeEach(() => {
 | 
			
		||||
            const getFunc = openmct.$injector.get;
 | 
			
		||||
            spyOn(openmct.$injector, "get")
 | 
			
		||||
                .withArgs("exportImageService").and.returnValue({
 | 
			
		||||
                    exportPNG: () => {},
 | 
			
		||||
                    exportJPG: () => {}
 | 
			
		||||
                })
 | 
			
		||||
                .and.callFake(getFunc);
 | 
			
		||||
 | 
			
		||||
            stackedPlotObject = {
 | 
			
		||||
                identifier: {
 | 
			
		||||
 
 | 
			
		||||
@@ -74,7 +74,9 @@ define([
 | 
			
		||||
    './clock/plugin',
 | 
			
		||||
    './DeviceClassifier/plugin',
 | 
			
		||||
    './timer/plugin',
 | 
			
		||||
    './localStorage/plugin'
 | 
			
		||||
    './localStorage/plugin',
 | 
			
		||||
    './legacySupport/plugin.js',
 | 
			
		||||
    '../adapter/indicators/legacy-indicators-plugin'
 | 
			
		||||
], function (
 | 
			
		||||
    _,
 | 
			
		||||
    UTCTimeSystem,
 | 
			
		||||
@@ -129,7 +131,9 @@ define([
 | 
			
		||||
    Clock,
 | 
			
		||||
    DeviceClassifier,
 | 
			
		||||
    Timer,
 | 
			
		||||
    LocalStorage
 | 
			
		||||
    LocalStorage,
 | 
			
		||||
    LegacySupportPlugin,
 | 
			
		||||
    LegacyIndicatorsPlugin
 | 
			
		||||
) {
 | 
			
		||||
    const bundleMap = {
 | 
			
		||||
        Elasticsearch: 'platform/persistence/elastic'
 | 
			
		||||
@@ -237,6 +241,8 @@ define([
 | 
			
		||||
    plugins.Timer = Timer.default;
 | 
			
		||||
    plugins.DeviceClassifier = DeviceClassifier.default;
 | 
			
		||||
    plugins.LocalStorage = LocalStorage.default;
 | 
			
		||||
    plugins.LegacySupport = LegacySupportPlugin.default;
 | 
			
		||||
    plugins.LegacyIndicators = LegacyIndicatorsPlugin;
 | 
			
		||||
 | 
			
		||||
    return plugins;
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -29,10 +29,9 @@ define(
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        SummaryWidgetsCompositionPolicy.prototype.allow = function (parent, child) {
 | 
			
		||||
            const parentType = parent.getCapability('type');
 | 
			
		||||
            const newStyleChild = child.useCapability('adapter');
 | 
			
		||||
            const parentType = parent.type;
 | 
			
		||||
 | 
			
		||||
            if (parentType.instanceOf('summary-widget') && !this.openmct.telemetry.isTelemetryObject(newStyleChild)) {
 | 
			
		||||
            if (parentType === 'summary-widget' && !this.openmct.telemetry.isTelemetryObject(child)) {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -17,7 +17,6 @@ define([
 | 
			
		||||
        const widgetType = {
 | 
			
		||||
            name: 'Summary Widget',
 | 
			
		||||
            description: 'A compact status update for collections of telemetry-producing items',
 | 
			
		||||
            creatable: true,
 | 
			
		||||
            cssClass: 'icon-summary-widget',
 | 
			
		||||
            initialize: function (domainObject) {
 | 
			
		||||
                domainObject.composition = [];
 | 
			
		||||
@@ -85,16 +84,8 @@ define([
 | 
			
		||||
 | 
			
		||||
        return function install(openmct) {
 | 
			
		||||
            openmct.types.addType('summary-widget', widgetType);
 | 
			
		||||
            openmct.legacyExtension('policies', {
 | 
			
		||||
                category: 'composition',
 | 
			
		||||
                implementation: SummaryWidgetsCompositionPolicy,
 | 
			
		||||
                depends: ['openmct']
 | 
			
		||||
            });
 | 
			
		||||
            openmct.legacyExtension('policies', {
 | 
			
		||||
                category: 'view',
 | 
			
		||||
                implementation: SummaryWidgetViewPolicy,
 | 
			
		||||
                depends: ['openmct']
 | 
			
		||||
            });
 | 
			
		||||
            let compositionPolicy = new SummaryWidgetsCompositionPolicy(openmct);
 | 
			
		||||
            openmct.composition.addPolicy(compositionPolicy.allow.bind(compositionPolicy));
 | 
			
		||||
            openmct.telemetry.addProvider(new SummaryWidgetMetadataProvider(openmct));
 | 
			
		||||
            openmct.telemetry.addProvider(new SummaryWidgetTelemetryProvider(openmct));
 | 
			
		||||
            openmct.objectViews.addProvider(new SummaryWidgetViewProvider(openmct));
 | 
			
		||||
 
 | 
			
		||||
@@ -80,7 +80,6 @@ define([
 | 
			
		||||
        this.addHyperlink(domainObject.url, domainObject.openNewTab);
 | 
			
		||||
        this.watchForChanges(openmct, domainObject);
 | 
			
		||||
 | 
			
		||||
        const id = objectUtils.makeKeyString(this.domainObject.identifier);
 | 
			
		||||
        const self = this;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
@@ -105,8 +104,6 @@ define([
 | 
			
		||||
 | 
			
		||||
        this.listenTo(this.toggleRulesControl, 'click', toggleRules);
 | 
			
		||||
 | 
			
		||||
        openmct.$injector.get('objectService')
 | 
			
		||||
            .getObjects([id]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,8 @@
 | 
			
		||||
@use 'sass:math';
 | 
			
		||||
 | 
			
		||||
.c-conductor-axis {
 | 
			
		||||
    $h: 18px;
 | 
			
		||||
    $tickYPos: ($h / 2) + 12px;
 | 
			
		||||
    $tickYPos: math.div($h, 2) + 12px;
 | 
			
		||||
 | 
			
		||||
    @include userSelectNone();
 | 
			
		||||
    @include bgTicks($c: rgba($colorBodyFg, 0.4));
 | 
			
		||||
 
 | 
			
		||||
@@ -191,7 +191,7 @@ export default {
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
    destroyed() {
 | 
			
		||||
    beforeDestroy() {
 | 
			
		||||
        this.active = false;
 | 
			
		||||
        if (this.unlisten) {
 | 
			
		||||
            this.unlisten();
 | 
			
		||||
 
 | 
			
		||||
@@ -60,6 +60,8 @@ describe("Timer plugin:", () => {
 | 
			
		||||
            timerDefinition = openmct.types.get('timer').definition;
 | 
			
		||||
            timerDefinition.initialize(timerDomainObject);
 | 
			
		||||
 | 
			
		||||
            spyOn(openmct.objects, 'supportsMutation').and.returnValue(true);
 | 
			
		||||
 | 
			
		||||
            openmct.on('start', resolve);
 | 
			
		||||
            openmct.start(appHolder);
 | 
			
		||||
        });
 | 
			
		||||
@@ -93,6 +95,8 @@ describe("Timer plugin:", () => {
 | 
			
		||||
            const applicableViews = openmct.objectViews.get(timerViewObject, [timerViewObject]);
 | 
			
		||||
            timerViewProvider = applicableViews.find(viewProvider => viewProvider.key === 'timer.view');
 | 
			
		||||
 | 
			
		||||
            spyOn(openmct.objects, 'get').and.returnValue(Promise.resolve(timerViewObject));
 | 
			
		||||
 | 
			
		||||
            mutableTimerObject = await openmct.objects.getMutable(timerViewObject.identifier);
 | 
			
		||||
 | 
			
		||||
            timerObjectPath = [mutableTimerObject];
 | 
			
		||||
@@ -102,6 +106,10 @@ describe("Timer plugin:", () => {
 | 
			
		||||
            await Vue.nextTick();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        afterEach(() => {
 | 
			
		||||
            timerView.destroy();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it("should migrate old object properties to the configuration section", () => {
 | 
			
		||||
            openmct.objects.applyGetInterceptors(timerViewObject.identifier, timerViewObject);
 | 
			
		||||
            expect(timerViewObject.configuration.timerFormat).toBe('short');
 | 
			
		||||
 
 | 
			
		||||
@@ -21,14 +21,15 @@
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
/* REQUIRES /platform/commonUI/general/res/sass/_constants.scss */
 | 
			
		||||
@use 'sass:math';
 | 
			
		||||
 | 
			
		||||
/************************** MOBILE REPRESENTATION ITEMS DIMENSIONS */
 | 
			
		||||
$mobileListIconSize: 30px;
 | 
			
		||||
$mobileTitleDescH: 35px;
 | 
			
		||||
$mobileOverlayMargin: 20px;
 | 
			
		||||
$mobileMenuIconD: 25px;
 | 
			
		||||
$phoneItemH: floor($gridItemMobile / 4);
 | 
			
		||||
$tabletItemH: floor($gridItemMobile / 3);
 | 
			
		||||
$phoneItemH: floor(math.div($gridItemMobile, 4));
 | 
			
		||||
$tabletItemH: floor(math.div($gridItemMobile, 3));
 | 
			
		||||
 | 
			
		||||
/************************** MOBILE TREE MENU DIMENSIONS */
 | 
			
		||||
$mobileTreeItemH: 35px;
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,8 @@
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
@use 'sass:math';
 | 
			
		||||
 | 
			
		||||
/******************************************************** BUTTONS */
 | 
			
		||||
// Optionally can include icon in :before via markup
 | 
			
		||||
button {
 | 
			
		||||
@@ -785,7 +787,7 @@ select {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        &--swatched {
 | 
			
		||||
            padding-bottom: floor($pTB / 2);
 | 
			
		||||
            padding-bottom: floor(math.div($pTB, 2));
 | 
			
		||||
            width: 2em; // Standardize the width
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -935,7 +937,7 @@ select {
 | 
			
		||||
 | 
			
		||||
@mixin sliderTrack($bg: $scrollbarTrackColorBg, $knobH: 12px, $trackH: 3px) {
 | 
			
		||||
    border-radius: 2px;
 | 
			
		||||
    $breakPointPx: floor(($knobH - $trackH) / 2);
 | 
			
		||||
    $breakPointPx: floor(math.div($knobH - $trackH, 2));
 | 
			
		||||
    $bp1: $breakPointPx;
 | 
			
		||||
    $bp2: $breakPointPx + $trackH;
 | 
			
		||||
    box-sizing: border-box;
 | 
			
		||||
 
 | 
			
		||||
@@ -19,6 +19,9 @@
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
@use 'sass:math';
 | 
			
		||||
 | 
			
		||||
/******************************************************** RESETS */
 | 
			
		||||
*,
 | 
			
		||||
:before,
 | 
			
		||||
@@ -308,7 +311,7 @@ body.desktop .has-local-controls {
 | 
			
		||||
    }
 | 
			
		||||
    &.c-tree__item {
 | 
			
		||||
        $d: $waitSpinnerTreeD;
 | 
			
		||||
        $spinnerL: 19 + $d/2;
 | 
			
		||||
        $spinnerL: 19 + math.div($d, 2);
 | 
			
		||||
 | 
			
		||||
        display: flex;
 | 
			
		||||
        align-items: center;
 | 
			
		||||
 
 | 
			
		||||
@@ -19,6 +19,9 @@
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
@use 'sass:math';
 | 
			
		||||
 | 
			
		||||
/******************************************************************* MESSAGES */
 | 
			
		||||
.w-message-contents {
 | 
			
		||||
    flex: 1 1 auto;
 | 
			
		||||
@@ -120,7 +123,7 @@ body.desktop .t-message-list {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Alert elements in views
 | 
			
		||||
mixin sUnSynced() {
 | 
			
		||||
@mixin sUnSynced {
 | 
			
		||||
    $c: $colorPausedBg;
 | 
			
		||||
    border: 1px solid $c;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -19,6 +19,9 @@
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
@use 'sass:math';
 | 
			
		||||
 | 
			
		||||
/*********************************************************************** CLOCKS AND TIMERS */
 | 
			
		||||
.c-clock,
 | 
			
		||||
.c-timer {
 | 
			
		||||
@@ -788,7 +791,7 @@ body.desktop {
 | 
			
		||||
                //width: $splitterHandleD;
 | 
			
		||||
            }
 | 
			
		||||
            &.flush-right {
 | 
			
		||||
                width: ceil($splitterHandleD / 2);
 | 
			
		||||
                width: ceil(math.div($splitterHandleD, 2));
 | 
			
		||||
                &:after {
 | 
			
		||||
                    width: $splitterHandleD;
 | 
			
		||||
                    left: auto; right: 0;
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,8 @@
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
@use 'sass:math';
 | 
			
		||||
 | 
			
		||||
/************************** GLYPHS */
 | 
			
		||||
@mixin glyphBefore($unicode, $family: 'symbolsfont') {
 | 
			
		||||
    &:before {
 | 
			
		||||
@@ -202,7 +204,7 @@
 | 
			
		||||
 | 
			
		||||
@mixin bgCheckerboard($c: $colorBodyFg, $opacity: 0.3, $size: 32px, $imp: false) {
 | 
			
		||||
    $color: rgba($c, $opacity);
 | 
			
		||||
    $bgPos: floor($size / 2);
 | 
			
		||||
    $bgPos: floor(math.div($size, 2));
 | 
			
		||||
 | 
			
		||||
    $impStr: null;
 | 
			
		||||
    @if $imp {
 | 
			
		||||
@@ -288,7 +290,7 @@
 | 
			
		||||
@mixin triangle($dir: "left", $size: 5px, $ratio: 1, $color: red) {
 | 
			
		||||
    width: 0;
 | 
			
		||||
    height: 0;
 | 
			
		||||
    $slopedB: $size/$ratio solid transparent;
 | 
			
		||||
    $slopedB: math.div($size, $ratio) solid transparent;
 | 
			
		||||
    $straightB: $size solid $color;
 | 
			
		||||
    @if $dir == "up" {
 | 
			
		||||
        border-left: $slopedB;
 | 
			
		||||
@@ -780,7 +782,7 @@
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mixin sUnsynced() {
 | 
			
		||||
@mixin sUnsynced {
 | 
			
		||||
    $c: $colorPausedBg;
 | 
			
		||||
    border: 1px solid $c;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,7 @@
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
export const COLOR_PALETTE = [
 | 
			
		||||
    [0x00, 0x37, 0xFF],
 | 
			
		||||
    [0x43, 0xB0, 0xFF],
 | 
			
		||||
    [0xF0, 0x60, 0x00],
 | 
			
		||||
    [0x00, 0x70, 0x40],
 | 
			
		||||
    [0xFB, 0x49, 0x49],
 | 
			
		||||
@@ -30,25 +30,25 @@ export const COLOR_PALETTE = [
 | 
			
		||||
    [0xFF, 0xA6, 0x3D],
 | 
			
		||||
    [0x05, 0xA3, 0x00],
 | 
			
		||||
    [0xF0, 0x00, 0x6C],
 | 
			
		||||
    [0x77, 0x17, 0x7A],
 | 
			
		||||
    [0xAC, 0x54, 0xAE],
 | 
			
		||||
    [0x23, 0xA9, 0xDB],
 | 
			
		||||
    [0xFA, 0xF0, 0x6F],
 | 
			
		||||
    [0x4E, 0xF0, 0x48],
 | 
			
		||||
    [0xC7, 0xBE, 0x52],
 | 
			
		||||
    [0x5A, 0xBD, 0x56],
 | 
			
		||||
    [0xAD, 0x50, 0x72],
 | 
			
		||||
    [0x94, 0x25, 0xEA],
 | 
			
		||||
    [0x21, 0x87, 0x82],
 | 
			
		||||
    [0x8F, 0x6E, 0x47],
 | 
			
		||||
    [0xf0, 0x59, 0xcb],
 | 
			
		||||
    [0x34, 0xB6, 0x7D],
 | 
			
		||||
    [0x6A, 0x36, 0xFF],
 | 
			
		||||
    [0x56, 0xF0, 0xE8],
 | 
			
		||||
    [0x7F, 0x52, 0xFF],
 | 
			
		||||
    [0x46, 0xC7, 0xC0],
 | 
			
		||||
    [0xA1, 0x8C, 0x1C],
 | 
			
		||||
    [0xCB, 0xE1, 0x44],
 | 
			
		||||
    [0x95, 0xB1, 0x26],
 | 
			
		||||
    [0xFF, 0x84, 0x9E],
 | 
			
		||||
    [0xB7, 0x79, 0xE7],
 | 
			
		||||
    [0x8C, 0xC9, 0xFD],
 | 
			
		||||
    [0xDB, 0xAA, 0x6E],
 | 
			
		||||
    [0xB8, 0xDF, 0x97],
 | 
			
		||||
    [0x93, 0xB5, 0x77],
 | 
			
		||||
    [0xFF, 0xBC, 0xDA],
 | 
			
		||||
    [0xD3, 0xB6, 0xDE]
 | 
			
		||||
];
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@
 | 
			
		||||
        />
 | 
			
		||||
    </div>
 | 
			
		||||
    <div ref="objectViewWrapper"
 | 
			
		||||
         :class="objectViewStyle"
 | 
			
		||||
         class="c-object-view"
 | 
			
		||||
    ></div>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
@@ -64,13 +64,6 @@ export default {
 | 
			
		||||
        },
 | 
			
		||||
        font() {
 | 
			
		||||
            return this.objectFontStyle ? this.objectFontStyle.font : this.layoutFont;
 | 
			
		||||
        },
 | 
			
		||||
        objectViewStyle() {
 | 
			
		||||
            if (this.domainObject && this.domainObject.type === 'time-strip') {
 | 
			
		||||
                return 'l-shell__main-object-view';
 | 
			
		||||
            } else {
 | 
			
		||||
                return 'u-contents';
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    destroyed() {
 | 
			
		||||
@@ -148,11 +141,15 @@ export default {
 | 
			
		||||
            this.openmct.objectViews.off('clearData', this.clearData);
 | 
			
		||||
        },
 | 
			
		||||
        getStyleReceiver() {
 | 
			
		||||
            let styleReceiver = this.$refs.objectViewWrapper.querySelector('.js-style-receiver')
 | 
			
		||||
                || this.$refs.objectViewWrapper.querySelector(':first-child');
 | 
			
		||||
            let styleReceiver;
 | 
			
		||||
 | 
			
		||||
            if (styleReceiver === null) {
 | 
			
		||||
                styleReceiver = undefined;
 | 
			
		||||
            if (this.$refs.objectViewWrapper !== undefined) {
 | 
			
		||||
                styleReceiver = this.$refs.objectViewWrapper.querySelector('.js-style-receiver')
 | 
			
		||||
                  || this.$refs.objectViewWrapper.querySelector(':first-child');
 | 
			
		||||
 | 
			
		||||
                if (styleReceiver === null) {
 | 
			
		||||
                    styleReceiver = undefined;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return styleReceiver;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,9 @@
 | 
			
		||||
@use 'sass:math';
 | 
			
		||||
 | 
			
		||||
.c-toggle-switch {
 | 
			
		||||
    $d: 12px;
 | 
			
		||||
    $m: 2px;
 | 
			
		||||
    $br: $d/1.5;
 | 
			
		||||
    $br: math.div($d, 1.5);
 | 
			
		||||
    cursor: pointer;
 | 
			
		||||
    display: inline-flex;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
 
 | 
			
		||||
@@ -186,6 +186,10 @@ export default {
 | 
			
		||||
                    return {
 | 
			
		||||
                        name: field.name,
 | 
			
		||||
                        value: field.path.reduce((object, key) => {
 | 
			
		||||
                            if (object === undefined) {
 | 
			
		||||
                                return object;
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            return object[key];
 | 
			
		||||
                        }, this.domainObject)
 | 
			
		||||
                    };
 | 
			
		||||
 
 | 
			
		||||
@@ -233,7 +233,6 @@
 | 
			
		||||
    /******************************* MAIN AREA */
 | 
			
		||||
    &__main-container {
 | 
			
		||||
        // Wrapper for main views
 | 
			
		||||
        //display: flex; NEEDS REGRESSION TESTING!!!
 | 
			
		||||
        display: flex;
 | 
			
		||||
        flex-direction: column;
 | 
			
		||||
        flex: 1 1 auto !important;
 | 
			
		||||
@@ -243,11 +242,11 @@
 | 
			
		||||
        > * + * {
 | 
			
		||||
            margin-top: $interiorMargin;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__main-object-view {
 | 
			
		||||
        flex: 1 1 auto;
 | 
			
		||||
        overflow: auto;
 | 
			
		||||
        > .c-object-view {
 | 
			
		||||
            flex: 1 1 auto;
 | 
			
		||||
            overflow: auto;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__tree {
 | 
			
		||||
@@ -317,6 +316,12 @@
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.c-object-view {
 | 
			
		||||
    display: block;
 | 
			
		||||
    height: 100%;
 | 
			
		||||
    overflow: auto;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.is-editing {
 | 
			
		||||
    .l-shell__main-container {
 | 
			
		||||
        $m: 3px;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
@use 'sass:math';
 | 
			
		||||
 | 
			
		||||
.c-tree-and-search {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
@@ -136,7 +138,7 @@
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        > * + * {
 | 
			
		||||
            margin-left: ceil($interiorMarginSm / 2);
 | 
			
		||||
            margin-left: ceil(math.div($interiorMarginSm, 2));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @include hover {
 | 
			
		||||
 
 | 
			
		||||
@@ -445,6 +445,10 @@ export default {
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // sorting composition items
 | 
			
		||||
            if (!a.name || !b.name) {
 | 
			
		||||
                return 0;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (a.name.toLowerCase()
 | 
			
		||||
                > b.name.toLowerCase()) {
 | 
			
		||||
                return 1;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
@use 'sass:math';
 | 
			
		||||
 | 
			
		||||
/**************************** BASE - MOBILE AND DESKTOP */
 | 
			
		||||
.l-multipane {
 | 
			
		||||
    display: flex;
 | 
			
		||||
@@ -234,7 +236,7 @@
 | 
			
		||||
 | 
			
		||||
                > .l-pane__handle {
 | 
			
		||||
                    left: 0;
 | 
			
		||||
                    transform: translateX(floor($splitterHandleD / -2)); // Center over the pane edge
 | 
			
		||||
                    transform: translateX(floor(math.div($splitterHandleD, -2))); // Center over the pane edge
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                [class*="expand-button"] {
 | 
			
		||||
@@ -251,7 +253,7 @@
 | 
			
		||||
 | 
			
		||||
                > .l-pane__handle {
 | 
			
		||||
                    right: 0;
 | 
			
		||||
                    transform: translateX(floor($splitterHandleD / 2));
 | 
			
		||||
                    transform: translateX(floor(math.div($splitterHandleD, 2)));
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                [class*="expand-button"] {
 | 
			
		||||
@@ -287,7 +289,7 @@
 | 
			
		||||
                padding-top: $m;
 | 
			
		||||
                > .l-pane__handle {
 | 
			
		||||
                    top: 0;
 | 
			
		||||
                    transform: translateY(floor($splitterHandleD / -1));
 | 
			
		||||
                    transform: translateY(floor(math.div($splitterHandleD, -1)));
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                .l-pane__collapse-button:before {
 | 
			
		||||
@@ -306,7 +308,7 @@
 | 
			
		||||
            &[class*="-after"] {
 | 
			
		||||
                > .l-pane__handle {
 | 
			
		||||
                    bottom: 0;
 | 
			
		||||
                    transform: translateY(floor($splitterHandleD / 1));
 | 
			
		||||
                    transform: translateY(floor(math.div($splitterHandleD, 1)));
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                &:not(.l-pane--collapsed) > .l-pane__collapse-button {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import objectPathToUrl from '/src/tools/url';
 | 
			
		||||
import objectPathToUrl from '../../tools/url';
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    inject: ['openmct'],
 | 
			
		||||
 
 | 
			
		||||
@@ -24,8 +24,6 @@ class Ticker {
 | 
			
		||||
    constructor() {
 | 
			
		||||
        this.callbacks = [];
 | 
			
		||||
        this.last = new Date() - 1000;
 | 
			
		||||
 | 
			
		||||
        this.tick();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -47,7 +45,7 @@ class Ticker {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Try to update at exactly the next second
 | 
			
		||||
        setTimeout(() => {
 | 
			
		||||
        this.timeoutHandle = setTimeout(() => {
 | 
			
		||||
            this.tick();
 | 
			
		||||
        }, 1000 - millis, true);
 | 
			
		||||
    }
 | 
			
		||||
@@ -62,6 +60,10 @@ class Ticker {
 | 
			
		||||
     * @returns {Function} a function to unregister this listener
 | 
			
		||||
     */
 | 
			
		||||
    listen(callback) {
 | 
			
		||||
        if (this.callbacks.length === 0) {
 | 
			
		||||
            this.tick();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.callbacks.push(callback);
 | 
			
		||||
 | 
			
		||||
        // Provide immediate feedback
 | 
			
		||||
@@ -72,6 +74,10 @@ class Ticker {
 | 
			
		||||
            this.callbacks = this.callbacks.filter(function (cb) {
 | 
			
		||||
                return cb !== callback;
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            if (this.callbacks.length === 0) {
 | 
			
		||||
                clearTimeout(this.timeoutHandle);
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,25 +1,18 @@
 | 
			
		||||
const path = require('path');
 | 
			
		||||
const packageDefinition = require('./package.json');
 | 
			
		||||
 | 
			
		||||
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
 | 
			
		||||
const CopyWebpackPlugin = require('copy-webpack-plugin');
 | 
			
		||||
const webpack = require('webpack');
 | 
			
		||||
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
 | 
			
		||||
 | 
			
		||||
const devMode = process.env.NODE_ENV !== 'production';
 | 
			
		||||
const VueLoaderPlugin = require('vue-loader/lib/plugin');
 | 
			
		||||
// TODO: Build Constants w/ git-rev-sync
 | 
			
		||||
const gitRevision = require('child_process')
 | 
			
		||||
    .execSync('git rev-parse HEAD')
 | 
			
		||||
    .toString().trim();
 | 
			
		||||
const gitBranch = require('child_process')
 | 
			
		||||
    .execSync('git rev-parse --abbrev-ref HEAD')
 | 
			
		||||
    .toString().trim();
 | 
			
		||||
const vueFile = devMode
 | 
			
		||||
    ? path.join(__dirname, "node_modules/vue/dist/vue.js")
 | 
			
		||||
    : path.join(__dirname, "node_modules/vue/dist/vue.min.js");
 | 
			
		||||
 | 
			
		||||
const webpackConfig = {
 | 
			
		||||
    mode: devMode ? 'development' : 'production',
 | 
			
		||||
module.exports = {
 | 
			
		||||
    entry: {
 | 
			
		||||
        openmct: './openmct.js',
 | 
			
		||||
        couchDBChangesFeed: './src/plugins/persistence/couch/CouchChangesFeed.js',
 | 
			
		||||
@@ -33,7 +26,8 @@ const webpackConfig = {
 | 
			
		||||
        filename: '[name].js',
 | 
			
		||||
        library: '[name]',
 | 
			
		||||
        libraryTarget: 'umd',
 | 
			
		||||
        path: path.resolve(__dirname, 'dist')
 | 
			
		||||
        publicPath: '',
 | 
			
		||||
        clean: true
 | 
			
		||||
    },
 | 
			
		||||
    resolve: {
 | 
			
		||||
        alias: {
 | 
			
		||||
@@ -45,7 +39,6 @@ const webpackConfig = {
 | 
			
		||||
            "bourbon": "bourbon.scss",
 | 
			
		||||
            "plotly-basic": "plotly.js-basic-dist",
 | 
			
		||||
            "plotly-gl2d": "plotly.js-gl2d-dist",
 | 
			
		||||
            "vue": vueFile,
 | 
			
		||||
            "d3-scale": path.join(__dirname, "node_modules/d3-scale/build/d3-scale.min.js"),
 | 
			
		||||
            "printj": path.join(__dirname, "node_modules/printj/dist/printj.min.js"),
 | 
			
		||||
            "styles": path.join(__dirname, "src/styles"),
 | 
			
		||||
@@ -55,32 +48,32 @@ const webpackConfig = {
 | 
			
		||||
            "utils": path.join(__dirname, "src/utils")
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    devtool: devMode ? 'eval-source-map' : 'source-map',
 | 
			
		||||
    plugins: [
 | 
			
		||||
        new webpack.DefinePlugin({
 | 
			
		||||
            __OPENMCT_VERSION__: `'${packageDefinition.version}'`,
 | 
			
		||||
            __OPENMCT_BUILD_DATE__: `'${new Date()}'`,
 | 
			
		||||
            __OPENMCT_REVISION__: `'${gitRevision}'`,
 | 
			
		||||
            __OPENMCT_BUILD_BRANCH__: `'${gitBranch}'`,
 | 
			
		||||
            __OPENMCT_ROOT_RELATIVE__: `'${devMode ? 'dist/' : ''}'`
 | 
			
		||||
            __OPENMCT_BUILD_BRANCH__: `'${gitBranch}'`
 | 
			
		||||
        }),
 | 
			
		||||
        new VueLoaderPlugin(),
 | 
			
		||||
        new CopyWebpackPlugin({
 | 
			
		||||
            patterns: [
 | 
			
		||||
                {
 | 
			
		||||
                    from: 'src/images/favicons',
 | 
			
		||||
                    to: 'favicons'
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    from: './index.html',
 | 
			
		||||
                    transform: function (content) {
 | 
			
		||||
                        return content.toString().replace(/dist\//g, '');
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
        }),
 | 
			
		||||
        new MiniCssExtractPlugin({
 | 
			
		||||
            filename: '[name].css',
 | 
			
		||||
            chunkFilename: '[name].css'
 | 
			
		||||
        }),
 | 
			
		||||
        new CopyWebpackPlugin([
 | 
			
		||||
            {
 | 
			
		||||
                from: 'src/images/favicons',
 | 
			
		||||
                to: 'favicons'
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                from: './index.html',
 | 
			
		||||
                transform: function (content) {
 | 
			
		||||
                    return content.toString().replace(/dist\//g, '');
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        ])
 | 
			
		||||
        })
 | 
			
		||||
    ],
 | 
			
		||||
    module: {
 | 
			
		||||
        rules: [
 | 
			
		||||
@@ -88,10 +81,17 @@ const webpackConfig = {
 | 
			
		||||
                test: /\.(sc|sa|c)ss$/,
 | 
			
		||||
                use: [
 | 
			
		||||
                    MiniCssExtractPlugin.loader,
 | 
			
		||||
                    'css-loader',
 | 
			
		||||
                    'fast-sass-loader'
 | 
			
		||||
                    {
 | 
			
		||||
                        loader: 'css-loader'
 | 
			
		||||
                    },
 | 
			
		||||
                    'resolve-url-loader',
 | 
			
		||||
                    'sass-loader'
 | 
			
		||||
                ]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                test: /\.vue$/,
 | 
			
		||||
                use: 'vue-loader'
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                test: /\.html$/,
 | 
			
		||||
                use: 'html-loader'
 | 
			
		||||
@@ -104,7 +104,7 @@ const webpackConfig = {
 | 
			
		||||
                ]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                test: /\.(jpg|jpeg|png|svg|ico|woff2?|eot|ttf)$/,
 | 
			
		||||
                test: /\.(jpg|jpeg|png|svg|ico|woff|woff2?|eot|ttf)$/,
 | 
			
		||||
                loader: 'file-loader',
 | 
			
		||||
                options: {
 | 
			
		||||
                    name: '[name].[ext]',
 | 
			
		||||
@@ -117,26 +117,15 @@ const webpackConfig = {
 | 
			
		||||
                            return `icons/${url}`;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        if (/\.(woff2?|eot|ttf)$/.test(url)) {
 | 
			
		||||
                        if (/\.(woff|woff2?|eot|ttf)$/.test(url)) {
 | 
			
		||||
                            return `fonts/${url}`;
 | 
			
		||||
                        } else {
 | 
			
		||||
                            return `${url}`;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                test: /\.vue$/,
 | 
			
		||||
                use: 'vue-loader'
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    stats: {
 | 
			
		||||
        modules: false,
 | 
			
		||||
        timings: true,
 | 
			
		||||
        colors: true,
 | 
			
		||||
        warningsFilter: /asset size limit/g
 | 
			
		||||
    }
 | 
			
		||||
    stats: 'errors-warnings'
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module.exports = webpackConfig;
 | 
			
		||||
							
								
								
									
										20
									
								
								webpack.dev.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								webpack.dev.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
const { merge } = require('webpack-merge');
 | 
			
		||||
const common = require('./webpack.common');
 | 
			
		||||
 | 
			
		||||
const path = require('path');
 | 
			
		||||
const webpack = require('webpack');
 | 
			
		||||
 | 
			
		||||
module.exports = merge(common, {
 | 
			
		||||
    mode: 'development',
 | 
			
		||||
    resolve: {
 | 
			
		||||
        alias: {
 | 
			
		||||
            "vue": path.join(__dirname, "node_modules/vue/dist/vue.js")
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    plugins: [
 | 
			
		||||
        new webpack.DefinePlugin({
 | 
			
		||||
            __OPENMCT_ROOT_RELATIVE__: '"dist/"'
 | 
			
		||||
        })
 | 
			
		||||
    ],
 | 
			
		||||
    devtool: 'eval-source-map'
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										20
									
								
								webpack.prod.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								webpack.prod.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
const { merge } = require('webpack-merge');
 | 
			
		||||
const common = require('./webpack.common');
 | 
			
		||||
 | 
			
		||||
const path = require('path');
 | 
			
		||||
const webpack = require('webpack');
 | 
			
		||||
 | 
			
		||||
module.exports = merge(common, {
 | 
			
		||||
    mode: 'production',
 | 
			
		||||
    resolve: {
 | 
			
		||||
        alias: {
 | 
			
		||||
            "vue": path.join(__dirname, "node_modules/vue/dist/vue.min.js")
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    plugins: [
 | 
			
		||||
        new webpack.DefinePlugin({
 | 
			
		||||
            __OPENMCT_ROOT_RELATIVE__: '""'
 | 
			
		||||
        })
 | 
			
		||||
    ],
 | 
			
		||||
    devtool: 'source-map'
 | 
			
		||||
});
 | 
			
		||||
		Reference in New Issue
	
	Block a user