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