Compare commits
30 Commits
code-cover
...
plots-unde
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd3938433d | ||
|
|
88a94c80be | ||
|
|
2fc0d34b8f | ||
|
|
d53ca3ec9a | ||
|
|
86e5d10fc1 | ||
|
|
936b88363c | ||
|
|
38fec73a33 | ||
|
|
43c2c8543e | ||
|
|
e8e719e7f7 | ||
|
|
26e70d82b7 | ||
|
|
3a65f75d21 | ||
|
|
51e4c0c836 | ||
|
|
91b51aafa6 | ||
|
|
bb9c225f23 | ||
|
|
19ec98af79 | ||
|
|
23ead2ceaa | ||
|
|
6a8f4b5d9c | ||
|
|
464bb3b885 | ||
|
|
a6f873cc78 | ||
|
|
acdf8b86be | ||
|
|
ab09cdd3bd | ||
|
|
4775c88909 | ||
|
|
722e2e2bb1 | ||
|
|
333aa1d6db | ||
|
|
73260f17f2 | ||
|
|
5e92c69fe2 | ||
|
|
8ddba2b06f | ||
|
|
6f9241c0b1 | ||
|
|
d84808aa68 | ||
|
|
1fbfc7d0d7 |
@@ -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"
|
||||
@@ -153,7 +153,7 @@ workflows:
|
||||
post-steps:
|
||||
- upload_code_covio
|
||||
- e2e-test:
|
||||
name: e2e-smoke
|
||||
name: e2e-ci
|
||||
node-version: lts/fermium
|
||||
suite: ci
|
||||
the-nightly: #These jobs do not run on PRs, but against master at night
|
||||
|
||||
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
|
||||
46
API.md
46
API.md
@@ -52,6 +52,8 @@
|
||||
- [The URL Status Indicator](#the-url-status-indicator)
|
||||
- [Creating a Simple Indicator](#creating-a-simple-indicator)
|
||||
- [Custom Indicators](#custom-indicators)
|
||||
- [Priority API](#priority-api)
|
||||
- [Priority Types](#priority-types)
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
|
||||
@@ -247,16 +249,24 @@ To do so, use the `addRoot` method of the object API.
|
||||
eg.
|
||||
```javascript
|
||||
openmct.objects.addRoot({
|
||||
namespace: "example.namespace",
|
||||
key: "my-key"
|
||||
});
|
||||
namespace: "example.namespace",
|
||||
key: "my-key"
|
||||
},
|
||||
openmct.priority.HIGH);
|
||||
```
|
||||
|
||||
The `addRoot` function takes a single [object identifier](#domain-objects-and-identifiers)
|
||||
as an argument.
|
||||
The `addRoot` function takes a two arguments, the first can be an [object identifier](#domain-objects-and-identifiers) for a root level object, or an array of identifiers for root
|
||||
level objects, or a function that returns a promise for an identifier or an array of root level objects, the second is a [priority](#priority-api) or numeric value.
|
||||
|
||||
Root objects are loaded just like any other objects, i.e. via an object
|
||||
provider.
|
||||
When using the `getAll` method of the object API, they will be returned in order of priority.
|
||||
|
||||
eg.
|
||||
```javascript
|
||||
openmct.objects.addRoot(identifier, openmct.priority.LOW); // low = -1000, will appear last in composition or tree
|
||||
openmct.objects.addRoot(otherIdentifier, openmct.priority.HIGH); // high = 1000, will appear first in composition or tree
|
||||
```
|
||||
|
||||
Root objects are loaded just like any other objects, i.e. via an object provider.
|
||||
|
||||
## Object Providers
|
||||
|
||||
@@ -1051,3 +1061,25 @@ A completely custom indicator can be added by simply providing a DOM element to
|
||||
element: domNode
|
||||
});
|
||||
```
|
||||
|
||||
## Priority API
|
||||
|
||||
Open MCT provides some built-in priority values that can be used in the application for view providers, indicators, root object order, and more.
|
||||
|
||||
### Priority Types
|
||||
|
||||
Currently, the Open MCT Priority API provides (type: numeric value):
|
||||
- HIGH: 1000
|
||||
- Default: 0
|
||||
- LOW: -1000
|
||||
|
||||
View provider Example:
|
||||
|
||||
``` javascript
|
||||
class ViewProvider {
|
||||
...
|
||||
priority() {
|
||||
return openmct.priority.HIGH;
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -1,4 +1,4 @@
|
||||
# Open MCT [](http://www.apache.org/licenses/LICENSE-2.0) [](https://lgtm.com/projects/g/nasa/openmct/context:javascript) [](https://codecov.io/gh/nasa/openmct) [](https://percy.io/b2e34b17/openmct)
|
||||
# Open MCT [](http://www.apache.org/licenses/LICENSE-2.0) [](https://lgtm.com/projects/g/nasa/openmct/context:javascript) [](https://codecov.io/gh/nasa/openmct) [](https://percy.io/b2e34b17/openmct) [](https://www.npmjs.com/package/openmct)
|
||||
|
||||
Open MCT (Open Mission Control Technologies) is a next-generation mission control framework for visualization of data on desktop and mobile devices. It is developed at NASA's Ames Research Center, and is being used by NASA for data analysis of spacecraft missions, as well as planning and operation of experimental rover systems. As a generalizable and open source framework, Open MCT could be used as the basis for building applications for planning, operation, and analysis of any systems producing telemetry data.
|
||||
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
@@ -13,6 +13,7 @@ const config = {
|
||||
timeout: 200 * 1000,
|
||||
reuseExistingServer: !process.env.CI
|
||||
},
|
||||
workers: 2, //Limit to 2 for CircleCI Agent
|
||||
use: {
|
||||
browserName: "chromium",
|
||||
baseURL: 'http://localhost:8080/',
|
||||
|
||||
@@ -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/,
|
||||
@@ -104,7 +103,7 @@ module.exports = (config) => {
|
||||
reports: ['lcovonly', 'text-summary'],
|
||||
thresholds: {
|
||||
global: {
|
||||
lines: 70
|
||||
lines: 66
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
40
package.json
40
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,14 +89,14 @@
|
||||
"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",
|
||||
"test:coverage:firefox": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" karma start --single-run --browsers=FirefoxHeadless",
|
||||
"test:e2e:ci": "npx playwright test --config=e2e/playwright-ci.config.js smoke",
|
||||
"test:e2e:ci": "npx playwright test --config=e2e/playwright-ci.config.js smoke default condition.e2e",
|
||||
"test:e2e:local": "npx playwright test --config=e2e/playwright-local.config.js",
|
||||
"test:e2e:visual": "percy exec -- npx playwright test --config=e2e/playwright-visual.config.js default",
|
||||
"test:e2e:full": "npx playwright test --config=e2e/playwright-ci.config.js",
|
||||
|
||||
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.
|
||||
|
||||
@@ -41,7 +41,7 @@ function ObjectAPI(typeRegistry, openmct) {
|
||||
this.typeRegistry = typeRegistry;
|
||||
this.eventEmitter = new EventEmitter();
|
||||
this.providers = {};
|
||||
this.rootRegistry = new RootRegistry();
|
||||
this.rootRegistry = new RootRegistry(openmct);
|
||||
this.inMemorySearchProvider = new InMemorySearchProvider(openmct);
|
||||
|
||||
this.rootProvider = new RootObjectProvider(this.rootRegistry);
|
||||
@@ -367,14 +367,17 @@ ObjectAPI.prototype.endTransaction = function () {
|
||||
|
||||
/**
|
||||
* Add a root-level object.
|
||||
* @param {module:openmct.ObjectAPI~Identifier|function} an array of
|
||||
* identifiers for root level objects, or a function that returns a
|
||||
* @param {module:openmct.ObjectAPI~Identifier|array|function} identifier an identifier or
|
||||
* an array of identifiers for root level objects, or a function that returns a
|
||||
* promise for an identifier or an array of root level objects.
|
||||
* @param {module:openmct.PriorityAPI~priority|Number} priority a number representing
|
||||
* this item(s) position in the root object's composition (example: order in object tree).
|
||||
* For arrays, they are treated as blocks.
|
||||
* @method addRoot
|
||||
* @memberof module:openmct.ObjectAPI#
|
||||
*/
|
||||
ObjectAPI.prototype.addRoot = function (key) {
|
||||
this.rootRegistry.addRoot(key);
|
||||
ObjectAPI.prototype.addRoot = function (identifier, priority) {
|
||||
this.rootRegistry.addRoot(identifier, priority);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -20,39 +20,43 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define([
|
||||
'lodash'
|
||||
], function (
|
||||
_
|
||||
) {
|
||||
import utils from './object-utils';
|
||||
|
||||
function RootRegistry() {
|
||||
this.providers = [];
|
||||
export default class RootRegistry {
|
||||
|
||||
constructor(openmct) {
|
||||
this._rootItems = [];
|
||||
this._openmct = openmct;
|
||||
}
|
||||
|
||||
RootRegistry.prototype.getRoots = function () {
|
||||
const promises = this.providers.map(function (provider) {
|
||||
return provider();
|
||||
});
|
||||
getRoots() {
|
||||
const sortedItems = this._rootItems.sort((a, b) => b.priority - a.priority);
|
||||
const promises = sortedItems.map((rootItem) => rootItem.provider());
|
||||
|
||||
return Promise.all(promises)
|
||||
.then(_.flatten);
|
||||
};
|
||||
|
||||
function isKey(key) {
|
||||
return _.isObject(key) && _.has(key, 'key') && _.has(key, 'namespace');
|
||||
return Promise.all(promises).then(rootItems => rootItems.flat());
|
||||
}
|
||||
|
||||
RootRegistry.prototype.addRoot = function (key) {
|
||||
if (isKey(key) || (Array.isArray(key) && key.every(isKey))) {
|
||||
this.providers.push(function () {
|
||||
return key;
|
||||
});
|
||||
} else if (typeof key === "function") {
|
||||
this.providers.push(key);
|
||||
addRoot(rootItem, priority) {
|
||||
|
||||
if (!this._isValid(rootItem)) {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
return RootRegistry;
|
||||
this._rootItems.push({
|
||||
priority: priority || this._openmct.priority.DEFAULT,
|
||||
provider: typeof rootItem === 'function' ? rootItem : () => rootItem
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
_isValid(rootItem) {
|
||||
if (utils.isIdentifier(rootItem) || typeof rootItem === 'function') {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Array.isArray(rootItem)) {
|
||||
return rootItem.every(utils.isIdentifier);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,6 +172,7 @@ define([
|
||||
}
|
||||
|
||||
return {
|
||||
isIdentifier: isIdentifier,
|
||||
toOldFormat: toOldFormat,
|
||||
toNewFormat: toNewFormat,
|
||||
makeKeyString: makeKeyString,
|
||||
|
||||
@@ -19,83 +19,113 @@
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
define([
|
||||
'../RootRegistry'
|
||||
], function (
|
||||
RootRegistry
|
||||
) {
|
||||
describe('RootRegistry', function () {
|
||||
let idA;
|
||||
let idB;
|
||||
let idC;
|
||||
let registry;
|
||||
|
||||
beforeEach(function () {
|
||||
idA = {
|
||||
key: 'keyA',
|
||||
namespace: 'something'
|
||||
};
|
||||
idB = {
|
||||
key: 'keyB',
|
||||
namespace: 'something'
|
||||
};
|
||||
idC = {
|
||||
key: 'keyC',
|
||||
namespace: 'something'
|
||||
};
|
||||
registry = new RootRegistry();
|
||||
});
|
||||
import { createOpenMct, resetApplicationState } from '../../../utils/testing';
|
||||
|
||||
it('can register a root by key', function () {
|
||||
registry.addRoot(idA);
|
||||
describe('RootRegistry', () => {
|
||||
let openmct;
|
||||
let idA;
|
||||
let idB;
|
||||
let idC;
|
||||
let idD;
|
||||
|
||||
return registry.getRoots()
|
||||
.then(function (roots) {
|
||||
expect(roots).toEqual([idA]);
|
||||
});
|
||||
});
|
||||
beforeEach((done) => {
|
||||
openmct = createOpenMct();
|
||||
idA = {
|
||||
key: 'keyA',
|
||||
namespace: 'something'
|
||||
};
|
||||
idB = {
|
||||
key: 'keyB',
|
||||
namespace: 'something'
|
||||
};
|
||||
idC = {
|
||||
key: 'keyC',
|
||||
namespace: 'something'
|
||||
};
|
||||
idD = {
|
||||
key: 'keyD',
|
||||
namespace: 'something'
|
||||
};
|
||||
|
||||
it('can register multiple roots by key', function () {
|
||||
registry.addRoot([idA, idB]);
|
||||
openmct.on('start', done);
|
||||
openmct.startHeadless();
|
||||
});
|
||||
|
||||
return registry.getRoots()
|
||||
.then(function (roots) {
|
||||
expect(roots).toEqual([idA, idB]);
|
||||
});
|
||||
});
|
||||
afterEach(async () => {
|
||||
await resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
it('can register an asynchronous root ', function () {
|
||||
registry.addRoot(function () {
|
||||
return Promise.resolve(idA);
|
||||
it('can register a root by identifier', () => {
|
||||
openmct.objects.addRoot(idA);
|
||||
|
||||
return openmct.objects.getRoot()
|
||||
.then((rootObject) => {
|
||||
expect(rootObject.composition).toEqual([idA]);
|
||||
});
|
||||
});
|
||||
|
||||
return registry.getRoots()
|
||||
.then(function (roots) {
|
||||
expect(roots).toEqual([idA]);
|
||||
});
|
||||
});
|
||||
it('can register multiple roots by identifier', () => {
|
||||
openmct.objects.addRoot([idA, idB]);
|
||||
|
||||
it('can register multiple asynchronous roots', function () {
|
||||
registry.addRoot(function () {
|
||||
return Promise.resolve([idA, idB]);
|
||||
return openmct.objects.getRoot()
|
||||
.then((rootObject) => {
|
||||
expect(rootObject.composition).toEqual([idA, idB]);
|
||||
});
|
||||
});
|
||||
|
||||
return registry.getRoots()
|
||||
.then(function (roots) {
|
||||
expect(roots).toEqual([idA, idB]);
|
||||
});
|
||||
});
|
||||
it('can register an asynchronous root ', () => {
|
||||
openmct.objects.addRoot(() => Promise.resolve(idA));
|
||||
|
||||
it('can combine different types of registration', function () {
|
||||
registry.addRoot([idA, idB]);
|
||||
registry.addRoot(function () {
|
||||
return Promise.resolve([idC]);
|
||||
return openmct.objects.getRoot()
|
||||
.then((rootObject) => {
|
||||
expect(rootObject.composition).toEqual([idA]);
|
||||
});
|
||||
});
|
||||
|
||||
return registry.getRoots()
|
||||
.then(function (roots) {
|
||||
expect(roots).toEqual([idA, idB, idC]);
|
||||
});
|
||||
});
|
||||
it('can register multiple asynchronous roots', () => {
|
||||
openmct.objects.addRoot(() => Promise.resolve([idA, idB]));
|
||||
|
||||
return openmct.objects.getRoot()
|
||||
.then((rootObject) => {
|
||||
expect(rootObject.composition).toEqual([idA, idB]);
|
||||
});
|
||||
});
|
||||
|
||||
it('can combine different types of registration', () => {
|
||||
openmct.objects.addRoot([idA, idB]);
|
||||
openmct.objects.addRoot(() => Promise.resolve([idC]));
|
||||
|
||||
return openmct.objects.getRoot()
|
||||
.then((rootObject) => {
|
||||
expect(rootObject.composition).toEqual([idA, idB, idC]);
|
||||
});
|
||||
});
|
||||
|
||||
it('supports priority ordering for identifiers', () => {
|
||||
openmct.objects.addRoot(idA, openmct.priority.LOW);
|
||||
openmct.objects.addRoot(idB, openmct.priority.HIGH);
|
||||
openmct.objects.addRoot(idC); // DEFAULT
|
||||
|
||||
return openmct.objects.getRoot()
|
||||
.then((rootObject) => {
|
||||
expect(rootObject.composition[0]).toEqual(idB);
|
||||
expect(rootObject.composition[1]).toEqual(idC);
|
||||
expect(rootObject.composition[2]).toEqual(idA);
|
||||
});
|
||||
});
|
||||
|
||||
it('supports priority ordering for different types of registration', () => {
|
||||
openmct.objects.addRoot(() => Promise.resolve([idC]), openmct.priority.LOW);
|
||||
openmct.objects.addRoot(idB, openmct.priority.HIGH);
|
||||
openmct.objects.addRoot([idA, idD]); // default
|
||||
|
||||
return openmct.objects.getRoot()
|
||||
.then((rootObject) => {
|
||||
expect(rootObject.composition[0]).toEqual(idB);
|
||||
expect(rootObject.composition[1]).toEqual(idA);
|
||||
expect(rootObject.composition[2]).toEqual(idD);
|
||||
expect(rootObject.composition[3]).toEqual(idC);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
};
|
||||
}
|
||||
@@ -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,17 @@
|
||||
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 = '', priority = undefined) {
|
||||
return function install(openmct) {
|
||||
const identifier = createMyItemsIdentifier(namespace);
|
||||
|
||||
openmct.objects.addGetInterceptor(myItemsInterceptor(identifier, openmct));
|
||||
openmct.objects.addRoot(identifier);
|
||||
if (priority === undefined) {
|
||||
priority = openmct.priority.LOW;
|
||||
}
|
||||
|
||||
openmct.objects.addGetInterceptor(myItemsInterceptor(openmct, identifier, name));
|
||||
openmct.objects.addRoot(identifier, priority);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -245,6 +245,7 @@ export default {
|
||||
element: this.snapshot.$el,
|
||||
onDestroy: () => this.snapshot.$destroy(true),
|
||||
size: 'large',
|
||||
autoHide: false,
|
||||
dismissable: true,
|
||||
buttons: [
|
||||
{
|
||||
|
||||
@@ -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());
|
||||
});
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
import * as d3Scale from 'd3-scale';
|
||||
import TimelineAxis from "../../ui/components/TimeSystemAxis.vue";
|
||||
import SwimLane from "@/ui/components/swim-lane/SwimLane.vue";
|
||||
import { getValidatedPlan } from "./util";
|
||||
import { getValidatedData } from "./util";
|
||||
import Vue from "vue";
|
||||
|
||||
const PADDING = 1;
|
||||
@@ -159,7 +159,7 @@ export default {
|
||||
return clientWidth - 200;
|
||||
},
|
||||
getPlanData(domainObject) {
|
||||
this.planData = getValidatedPlan(domainObject);
|
||||
this.planData = getValidatedData(domainObject);
|
||||
},
|
||||
updateViewBounds(bounds) {
|
||||
if (bounds) {
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
export function getValidatedPlan(domainObject) {
|
||||
export function getValidatedData(domainObject) {
|
||||
let body = domainObject.selectFile.body;
|
||||
let json = {};
|
||||
if (typeof body === 'string') {
|
||||
|
||||
198
src/plugins/plot/UnderlayPlot.vue
Normal file
198
src/plugins/plot/UnderlayPlot.vue
Normal file
@@ -0,0 +1,198 @@
|
||||
<!--
|
||||
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>
|
||||
<div ref="plotWrapper"
|
||||
class="c-plot"
|
||||
>
|
||||
<div ref="underlay"
|
||||
class="c-bar-chart"
|
||||
></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Plotly from 'plotly-basic';
|
||||
import {getValidatedData} from "@/plugins/plan/util";
|
||||
|
||||
const PATH_COLORS = ['blue', 'red', 'green'];
|
||||
const MARKER_COLOR = 'white';
|
||||
|
||||
export default {
|
||||
inject: ['openmct', 'domainObject'],
|
||||
props: {
|
||||
data: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
data: {
|
||||
immediate: false,
|
||||
handler: 'updatePlot'
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getPlotData(this.domainObject);
|
||||
|
||||
Plotly.newPlot(this.$refs.underlay, Array.from(this.getShapes(this.shapesData)), this.getLayout(this.shapesData), {
|
||||
responsive: true,
|
||||
displayModeBar: false
|
||||
});
|
||||
this.registerListeners();
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.plotResizeObserver) {
|
||||
this.plotResizeObserver.unobserve(this.$refs.plotWrapper);
|
||||
clearTimeout(this.resizeTimer);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getPlotData(domainObject) {
|
||||
this.shapesData = getValidatedData(domainObject);
|
||||
},
|
||||
observeForChanges(mutatedObject) {
|
||||
this.getPlotData(mutatedObject);
|
||||
},
|
||||
registerListeners() {
|
||||
this.unlisten = this.openmct.objects.observe(this.domainObject, '*', this.observeForChanges);
|
||||
|
||||
this.resizeTimer = false;
|
||||
if (window.ResizeObserver) {
|
||||
this.plotResizeObserver = new ResizeObserver(() => {
|
||||
// debounce and trigger window resize so that plotly can resize the plot
|
||||
clearTimeout(this.resizeTimer);
|
||||
this.resizeTimer = setTimeout(() => {
|
||||
window.dispatchEvent(new Event('resize'));
|
||||
}, 250);
|
||||
});
|
||||
this.plotResizeObserver.observe(this.$refs.plotWrapper);
|
||||
}
|
||||
},
|
||||
getShapes(data) {
|
||||
let markerData = {
|
||||
x: [],
|
||||
y: []
|
||||
};
|
||||
const shapes = data.map((shapeData, index1) => {
|
||||
if (!shapeData.x || !shapeData.y
|
||||
|| !shapeData.x.length || !shapeData.y.length
|
||||
|| shapeData.x.length !== shapeData.y.length) {
|
||||
return "";
|
||||
}
|
||||
|
||||
let text = [];
|
||||
shapeData.x.forEach((point) => {
|
||||
text.push(`${parseFloat(point).toPrecision(2)}`);
|
||||
});
|
||||
|
||||
markerData.x = markerData.x.concat(shapeData.x);
|
||||
markerData.y = markerData.y.concat(shapeData.y);
|
||||
|
||||
return {
|
||||
x: shapeData.x,
|
||||
y: shapeData.y,
|
||||
mode: 'text',
|
||||
text,
|
||||
textfont: {
|
||||
family: 'Helvetica Neue, Helvetica, Arial, sans-serif',
|
||||
size: '12px',
|
||||
color: PATH_COLORS[index1]
|
||||
},
|
||||
opacity: 0.5
|
||||
};
|
||||
});
|
||||
|
||||
shapes.push({
|
||||
x: markerData.x,
|
||||
y: markerData.y,
|
||||
mode: "markers",
|
||||
marker: {
|
||||
size: 6,
|
||||
color: MARKER_COLOR
|
||||
}
|
||||
});
|
||||
|
||||
return shapes;
|
||||
},
|
||||
getLayout(data) {
|
||||
const shapes = data.map((shapeData, index1) => {
|
||||
if (!shapeData.x || !shapeData.y
|
||||
|| !shapeData.x.length || !shapeData.y.length
|
||||
|| shapeData.x.length !== shapeData.y.length) {
|
||||
return "";
|
||||
}
|
||||
|
||||
let path = `M ${shapeData.x[0]},${shapeData.y[0]}`;
|
||||
shapeData.x.forEach((point, index) => {
|
||||
if (index > 0) {
|
||||
path = `${path} L${point},${shapeData.y[index]}`;
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
path,
|
||||
type: 'path',
|
||||
line: {
|
||||
color: PATH_COLORS[index1]
|
||||
},
|
||||
opacity: 0.5
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
shapes,
|
||||
layer: 'below',
|
||||
paper_bgcolor: 'transparent',
|
||||
plot_bgcolor: 'transparent',
|
||||
showlegend: false,
|
||||
autosize: true,
|
||||
//TODO: Revisit rangemode here
|
||||
yaxis: {
|
||||
automargin: true,
|
||||
fixedrange: true,
|
||||
rangemode: 'tozero'
|
||||
},
|
||||
xaxis: {
|
||||
automargin: true,
|
||||
fixedrange: true,
|
||||
rangemode: 'tozero'
|
||||
},
|
||||
margin: {
|
||||
l: 5,
|
||||
r: 5,
|
||||
t: 5,
|
||||
b: 0
|
||||
}
|
||||
};
|
||||
},
|
||||
updatePlot() {
|
||||
if (!this.$refs || !this.$refs.underlay) {
|
||||
return;
|
||||
}
|
||||
|
||||
Plotly.react(this.$refs.underlay, Array.from(this.getShapes(this.shapesData)), this.getLayout(this.shapesData));
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
64
src/plugins/plot/UnderlayPlotViewProvider.js
Normal file
64
src/plugins/plot/UnderlayPlotViewProvider.js
Normal file
@@ -0,0 +1,64 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
import UnderlayPlot from './UnderlayPlot.vue';
|
||||
import Vue from 'vue';
|
||||
|
||||
export default function UnderlayPlotViewProvider(openmct) {
|
||||
|
||||
return {
|
||||
key: 'plot-underlay',
|
||||
name: 'Underlay Plot',
|
||||
cssClass: 'icon-telemetry',
|
||||
canView(domainObject) {
|
||||
return domainObject.type === 'telemetry.plot.underlay';
|
||||
},
|
||||
|
||||
canEdit(domainObject) {
|
||||
return domainObject.type === 'telemetry.plot.underlay';
|
||||
},
|
||||
|
||||
view: function (domainObject, objectPath) {
|
||||
let component;
|
||||
|
||||
return {
|
||||
show: function (element) {
|
||||
component = new Vue({
|
||||
el: element,
|
||||
components: {
|
||||
UnderlayPlot
|
||||
},
|
||||
provide: {
|
||||
openmct,
|
||||
domainObject
|
||||
},
|
||||
template: '<underlay-plot></underlay-plot>'
|
||||
});
|
||||
},
|
||||
destroy: function () {
|
||||
component.$destroy();
|
||||
component = undefined;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -20,6 +20,7 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
import PlotViewProvider from './PlotViewProvider';
|
||||
import UnderlayPlotViewProvider from './UnderlayPlotViewProvider';
|
||||
import OverlayPlotViewProvider from './overlayPlot/OverlayPlotViewProvider';
|
||||
import StackedPlotViewProvider from './stackedPlot/StackedPlotViewProvider';
|
||||
import PlotsInspectorViewProvider from './inspector/PlotsInspectorViewProvider';
|
||||
@@ -34,7 +35,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 = {
|
||||
@@ -59,9 +60,33 @@ export default function () {
|
||||
priority: 890
|
||||
});
|
||||
|
||||
openmct.types.addType('telemetry.plot.underlay', {
|
||||
name: 'Underlay Plot',
|
||||
key: 'telemetry.plot.underlay',
|
||||
description: 'A plot view for a compatible underlay plot file.',
|
||||
creatable: true,
|
||||
cssClass: 'icon-telemetry',
|
||||
form: [
|
||||
{
|
||||
name: 'Upload Telemetry (JSON File)',
|
||||
key: 'selectFile',
|
||||
control: 'file-input',
|
||||
required: true,
|
||||
text: 'Select File...',
|
||||
type: 'application/json',
|
||||
property: [
|
||||
"selectFile"
|
||||
]
|
||||
}
|
||||
],
|
||||
initialize: function (domainObject) {
|
||||
}
|
||||
});
|
||||
|
||||
openmct.objectViews.addProvider(new StackedPlotViewProvider(openmct));
|
||||
openmct.objectViews.addProvider(new OverlayPlotViewProvider(openmct));
|
||||
openmct.objectViews.addProvider(new PlotViewProvider(openmct));
|
||||
openmct.objectViews.addProvider(new UnderlayPlotViewProvider(openmct));
|
||||
|
||||
openmct.inspectorViews.addProvider(new PlotsInspectorViewProvider(openmct));
|
||||
|
||||
|
||||
@@ -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,7 @@ define(
|
||||
}
|
||||
|
||||
SummaryWidgetsCompositionPolicy.prototype.allow = function (parent, child) {
|
||||
const parentType = parent.getCapability('type');
|
||||
const newStyleChild = child.useCapability('adapter');
|
||||
|
||||
if (parentType.instanceOf('summary-widget') && !this.openmct.telemetry.isTelemetryObject(newStyleChild)) {
|
||||
if (parent.type === '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));
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
import TimelineObjectView from './TimelineObjectView.vue';
|
||||
import TimelineAxis from '../../ui/components/TimeSystemAxis.vue';
|
||||
import SwimLane from "@/ui/components/swim-lane/SwimLane.vue";
|
||||
import { getValidatedPlan } from "../plan/util";
|
||||
import { getValidatedData } from "../plan/util";
|
||||
|
||||
const unknownObjectType = {
|
||||
definition: {
|
||||
@@ -106,7 +106,7 @@ export default {
|
||||
let objectPath = [domainObject].concat(this.objectPath.slice());
|
||||
let rowCount = 0;
|
||||
if (domainObject.type === 'plan') {
|
||||
rowCount = Object.keys(getValidatedPlan(domainObject)).length;
|
||||
rowCount = Object.keys(getValidatedData(domainObject)).length;
|
||||
}
|
||||
|
||||
let height = domainObject.type === 'telemetry.plot.stacked' ? `${domainObject.composition.length * 100}px` : '100px';
|
||||
|
||||
@@ -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,
|
||||
@@ -226,7 +229,7 @@ body.desktop .has-local-controls {
|
||||
@include abs();
|
||||
}
|
||||
|
||||
.c-grid {
|
||||
.c-grid .c-grid {
|
||||
pointer-events: none;
|
||||
|
||||
&__x { @include bgTicks($editUIGridColorFg, 'x'); }
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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