Compare commits

...

56 Commits

Author SHA1 Message Date
David Tsay
8780cc33d0 fix lint errors 2022-04-12 14:20:25 -07:00
David Tsay
dc62e56279 use merge sort instead of insertion sort 2022-04-12 13:57:51 -07:00
Andrew Henry
1c794bdece De-reactify tables 2022-04-11 14:26:01 -07:00
David Tsay
052ec19892 remove form element if not dialog 2022-03-27 20:08:34 -07:00
David Tsay
5f70ab7dd3 allow hiding cancel button 2022-03-25 19:14:54 -07:00
David Tsay
7f73e1e765 safety check for overlay 2022-03-25 19:14:40 -07:00
David Tsay
ea7cf080c3 only show required fields message if form has required fields 2022-03-25 14:01:10 -07:00
David Tsay
9b0762f201 all custom labels 2022-03-25 12:59:14 -07:00
David Tsay
31aa291672 add checkbox
(merged to master after this branch was cut)
2022-03-25 11:08:14 -07:00
David Tsay
65e8d8b23c warn should be alert 2022-03-22 22:43:52 -07:00
David Tsay
fdb35094f4 fix enum issue 2022-03-22 22:05:48 -07:00
David Tsay
3b99a12d7f use domain.source for sorting
do not add telemetry for incorrect timesystems
2022-03-22 10:54:45 -07:00
David Tsay
25ee2d8099 hack for vista timesystems 2022-03-16 10:29:37 -07:00
David Tsay
c14cc25977 clean up observers 2022-03-16 10:27:01 -07:00
David Tsay
50997270e9 separate name and composition listeners 2022-03-16 10:11:20 -07:00
David Tsay
67e3094c6c warn instead of error on missing timesystem so vista still functional 2022-03-14 16:10:18 -07:00
David Tsay
526e31d10c more identifier fixes to search 2022-03-14 16:01:23 -07:00
David Tsay
3b316ed491 fix identifier issues 2022-03-14 13:53:33 -07:00
David Tsay
282ead581a object get in onWorkerMessage call should use identifier 2022-03-14 11:21:04 -07:00
David Tsay
c4f18a4797 object get in indexing process should use identifier not key 2022-03-10 14:27:22 -08:00
David Tsay
c273e83093 debug for search 2022-03-10 14:26:40 -08:00
David Tsay
e4b9242864 hack to parse identifier if namespace and key was mashed 2022-03-01 16:10:53 -08:00
David Tsay
ceddadcac6 fix canvas fallback in plots
https://github.com/nasa/openmct/pull/4784
2022-01-26 14:27:33 -08:00
David Tsay
6e2437b09e fix navigate after create path 2022-01-18 11:19:35 -08:00
David Tsay
5a44931537 default to string not undefined 2022-01-13 17:15:24 -08:00
David Tsay
f165d9c064 Revert "bring back identifierService for getProvider"
This reverts commit 830f321f90.
2022-01-13 17:08:08 -08:00
David Tsay
613973d936 set namespace for malformed identifiers 2022-01-13 17:06:24 -08:00
David Tsay
830f321f90 bring back identifierService for getProvider 2022-01-13 14:16:28 -08:00
David Tsay
a14cd62878 Merge branch 'release/1.8.3' into vista-r4.9-release 2022-01-10 13:49:05 -08:00
Shefali Joshi
8314d03af5 Fix Link Action to accept the right input for validation (#4696) 2022-01-07 14:46:31 -08:00
Nikhil
187da3c462 Snapshot notice link not navigating as expected #4194 (#4686) 2022-01-07 11:51:11 -08:00
Shefali Joshi
e4f134ca59 Mct 4555 rebased v3 (#4689)
* Preserve the previousFocusedImage for subscription updates, bound change for local and fixed time

* Only preserve previous focused image if paused

* Forcibly reset imageContainer size to prevent aspect ratio distortion

* Remove unneccesary mixin invocation

* Use image history instead of imagehistory size for watcher. Revert other changes

* Added check if last image index is selected

* isPaused instead of paused

Co-authored-by: Michael Rogers <contact@mhrogers.com>
2022-01-07 11:09:09 -06:00
Shefali Joshi
76829ad252 Check for ref when trying to get the style receiver. (#4687)
Also ensure that the property being displayed in Properties is actually a part of the domainObject.
2022-01-07 10:57:10 -06:00
Nikhil
a8da0d5917 Notebook Snapshotting to the default Notebook isn't working (#4475)
* Notebook Snapshotting to the default Notebook isn't working #4469
2022-01-06 16:44:21 -08:00
David Tsay
488beb5b3f new colors show better on espresso and snow themes 2022-01-06 11:25:33 -08:00
David Tsay
2f63718385 use relative path (#4683) 2022-01-06 11:14:21 -08:00
Shefali Joshi
433f1bf28e Fix object creation (#4675)
* Save the object before adding it to the parent so that transaction committing works properly
* Fix object creation - composition policy changes
2022-01-05 11:05:02 -08:00
Andrew Henry
936b88363c Disable legacy support in openmct dev (#4660)
Co-authored-by: Shefali Joshi <simplyrender@gmail.com>
2022-01-04 08:32:47 -08:00
Michael Rogers
38fec73a33 4588 - Removed summary widget creatability and updated composition policy (#4609)
* Removed summary widget creatability and updated composition policy

Co-authored-by: John Hill <john.c.hill@nasa.gov>
2022-01-04 07:40:09 -08:00
Charles Hacskaylo
43c2c8543e Fixes for #4623 (#4624)
- New `c-object-view` class;
- Removed CSS class special-casing in ObjectView.vue;
- Removed unused `l-shell__main-object-view` class;
- Fixed CSS selector for Condition Widget display in main view;

Co-authored-by: John Hill <john.c.hill@nasa.gov>
Co-authored-by: Shefali Joshi <simplyrender@gmail.com>
2022-01-04 06:00:14 -08:00
John Hill
e8e719e7f7 Update Bug Report format to make visual bug distinction clearer (#4662)
Co-authored-by: Shefali Joshi <simplyrender@gmail.com>
2022-01-04 05:49:14 -08:00
Nikhil
26e70d82b7 Move action issue fix #4663 (#4664) 2022-01-03 17:27:14 -08:00
Andrew Henry
3a65f75d21 Move all support for the legacy API into a plugin (#4614)
* Make legacy support optional
* Fix order of legacy plugin registration
* Added 'supportComposition' function
* Add composition policy to check that parent supports composition
* Fix memory leaks in timer
2022-01-03 14:21:19 -08:00
John Hill
51e4c0c836 Actually install the correct version of node (#4655)
Co-authored-by: Joe Pea <trusktr@gmail.com>
2022-01-03 13:44:57 -08:00
Andrew Henry
bb9c225f23 Lock vue-loader to 15.9.8 to fix build issues (#4653)
* Use a fixed version number for vue-loader to avoid a webpack build issue in some system configurations

* Ask dependabot to keep an eye out for vue-loader updates

Co-authored-by: John Hill <john.c.hill@nasa.gov>
2022-01-03 13:39:51 -08:00
dependabot[bot]
19ec98af79 Bump jasmine-core from 3.99.0 to 4.0.0 (#4651)
Bumps [jasmine-core](https://github.com/jasmine/jasmine) from 3.99.0 to 4.0.0.
- [Release notes](https://github.com/jasmine/jasmine/releases)
- [Changelog](https://github.com/jasmine/jasmine/blob/main/RELEASE.md)
- [Commits](https://github.com/jasmine/jasmine/compare/v3.99.0...v4.0.0)

---
updated-dependencies:
- dependency-name: jasmine-core
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-01-03 13:20:45 -08:00
Jamie V
23ead2ceaa [My Items] Make folder name customizable (#4627)
* making my items folder name customizable

Co-authored-by: Nikhil <nikhil.k.mandlik@nasa.gov>
Co-authored-by: John Hill <john.c.hill@nasa.gov>
2021-12-30 16:27:51 -08:00
Nikhil
6a8f4b5d9c webpack stats changed to 'errors-warnings' (#4644) 2021-12-29 17:39:07 -08:00
Nikhil
464bb3b885 [Build] webpack5 upgrade (#4242) 2021-12-30 00:18:48 +00:00
Nikhil
4775c88909 [Snapshots] Blank PNG/JPG image generated for snapshots #4526 (#4612) 2021-12-29 06:41:01 -08:00
dependabot[bot]
722e2e2bb1 Bump babel-eslint from 10.0.3 to 10.1.0 (#4637)
Bumps [babel-eslint](https://github.com/babel/babel-eslint) from 10.0.3 to 10.1.0.
- [Release notes](https://github.com/babel/babel-eslint/releases)
- [Commits](https://github.com/babel/babel-eslint/compare/v10.0.3...v10.1.0)

---
updated-dependencies:
- dependency-name: babel-eslint
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: John Hill <john.c.hill@nasa.gov>
2021-12-28 21:47:05 +00:00
dependabot[bot]
333aa1d6db Bump vue-eslint-parser from 7.11.0 to 8.0.1 (#4636)
Bumps [vue-eslint-parser](https://github.com/vuejs/vue-eslint-parser) from 7.11.0 to 8.0.1.
- [Release notes](https://github.com/vuejs/vue-eslint-parser/releases)
- [Commits](https://github.com/vuejs/vue-eslint-parser/compare/v7.11.0...v8.0.1)

---
updated-dependencies:
- dependency-name: vue-eslint-parser
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-28 13:42:43 -08:00
John Hill
5e92c69fe2 Update the regex on dependabot (#4635) 2021-12-28 09:43:56 -08:00
Shefali Joshi
8ddba2b06f Prepare for sprint 1.8.3 (#4570)
Co-authored-by: John Hill <john.c.hill@nasa.gov>
2021-12-28 07:45:53 -08:00
Henry Hsu
6f9241c0b1 New import export json action test (#4225)
Co-authored-by: Henry Hsu <henry.hsu@nasa.gov>
Co-authored-by: Nikhil <nikhil.k.mandlik@nasa.gov>
Co-authored-by: Jamie V <jamie.j.vigliotta@nasa.gov>
Co-authored-by: charlesh88 <charles.f.hacskaylo@nasa.gov>
Co-authored-by: Andrew Henry <akhenry@gmail.com>
Co-authored-by: John Hill <john.c.hill@nasa.gov>
Co-authored-by: David Tsay <3614296+davetsay@users.noreply.github.com>
2021-12-27 12:29:57 -08:00
dependabot[bot]
d84808aa68 Bump eslint-plugin-playwright from 0.7.0 to 0.7.1 (#4633)
Bumps [eslint-plugin-playwright](https://github.com/playwright-community/eslint-plugin-playwright) from 0.7.0 to 0.7.1.
- [Release notes](https://github.com/playwright-community/eslint-plugin-playwright/releases)
- [Commits](https://github.com/playwright-community/eslint-plugin-playwright/compare/v0.7.0...v0.7.1)

---
updated-dependencies:
- dependency-name: eslint-plugin-playwright
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-24 10:27:19 -08:00
87 changed files with 1231 additions and 900 deletions

View File

@@ -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"

View File

@@ -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 -->

View File

@@ -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
View File

@@ -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
View File

@@ -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);
});

View File

@@ -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());

View File

@@ -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/,

View File

@@ -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",

View File

@@ -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 () {

View File

@@ -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);

View File

@@ -133,5 +133,9 @@ define([
});
};
CompositionAPI.prototype.supportsComposition = function (domainObject) {
return this.get(domainObject) !== undefined;
};
return CompositionAPI;
});

View File

@@ -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);

View File

@@ -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.

View File

@@ -1,5 +1,6 @@
import AutoCompleteField from './components/controls/AutoCompleteField.vue';
import ClockDisplayFormatField from './components/controls/ClockDisplayFormatField.vue';
import CheckBoxField from './components/controls/CheckBoxField.vue';
import Datetime from './components/controls/Datetime.vue';
import FileInput from './components/controls/FileInput.vue';
import Locator from './components/controls/Locator.vue';
@@ -12,6 +13,7 @@ import Vue from 'vue';
export const DEFAULT_CONTROLS_MAP = {
'autocomplete': AutoCompleteField,
'checkbox': CheckBoxField,
'composite': ClockDisplayFormatField,
'datetime': Datetime,
'file-input': FileInput,

View File

@@ -172,7 +172,11 @@ export default class FormsAPI {
function onFormSave(save) {
return () => {
overlay.dismiss();
if (element) {
formElement.remove();
} else {
overlay.dismiss();
}
if (save) {
save(changes);

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, United States Government
* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -24,49 +24,60 @@
<div class="c-form">
<div class="c-overlay__top-bar c-form__top-bar">
<div class="c-overlay__dialog-title">{{ model.title }}</div>
<div class="c-overlay__dialog-hint hint">All fields marked <span class="req icon-asterisk"></span> are required.</div>
<div
v-if="hasRequiredFields"
class="c-overlay__dialog-hint hint"
>All fields marked <span class="req icon-asterisk"></span> are required.</div>
</div>
<form name="mctForm"
class="c-form__contents"
autocomplete="off"
@submit.prevent
<form
name="mctForm"
class="c-form__contents"
autocomplete="off"
@submit.prevent
>
<div v-for="section in formSections"
:key="section.id"
class="c-form__section"
:class="section.cssClass"
<div
v-for="section in formSections"
:key="section.id"
class="c-form__section"
:class="section.cssClass"
>
<h2 v-if="section.name"
<h2
v-if="section.name"
class="c-form__section-header"
>
{{ section.name }}
</h2>
<div v-for="(row, index) in section.rows"
:key="row.id"
class="u-contents"
<div
v-for="(row, index) in section.rows"
:key="row.id"
class="u-contents"
>
<FormRow :css-class="section.cssClass"
:first="index < 1"
:row="row"
@onChange="onChange"
<FormRow
:css-class="section.cssClass"
:first="index < 1"
:row="row"
@onChange="onChange"
/>
</div>
</div>
</form>
<div class="mct-form__controls c-overlay__button-bar c-form__bottom-bar">
<button tabindex="0"
:disabled="isInvalid"
class="c-button c-button--major"
@click="onSave"
<button
tabindex="0"
:disabled="isInvalid"
class="c-button c-button--major"
@click="onSave"
>
OK
{{ submitLabel }}
</button>
<button tabindex="0"
class="c-button"
@click="onDismiss"
<button
v-if="!hideCancel"
tabindex="0"
class="c-button"
@click="onDismiss"
>
Cancel
{{ cancelLabel }}
</button>
</div>
</div>
@@ -100,11 +111,42 @@ export default {
};
},
computed: {
hasRequiredFields() {
return this.model.sections.some(section =>
section.rows.some(row => row.required));
},
isInvalid() {
return Object.entries(this.invalidProperties)
.some(([key, value]) => {
return value;
});
},
submitLabel() {
if (
this.model.buttons
&& this.model.buttons.submit
&& this.model.buttons.submit.label
) {
return this.model.buttons.submit.label;
}
return 'OK';
},
cancelLabel() {
if (
this.model.buttons
&& this.model.buttons.cancel
&& this.model.buttons.cancel.label
) {
return this.model.buttons.submit.label;
}
return 'Cancel';
},
hideCancel() {
return this.model.buttons
&& this.model.buttons.cancel
&& this.model.buttons.cancel.hide === true;
}
},
mounted() {

View File

@@ -75,10 +75,12 @@ export default {
rowClass() {
let cssClass = this.cssClass;
if (this.row.required) {
cssClass = `${cssClass} req`;
if (!this.row.required) {
return;
}
cssClass = `${cssClass} req`;
if (this.visited && this.valid !== undefined) {
if (this.valid === true) {
cssClass = `${cssClass} valid`;

View File

@@ -0,0 +1,62 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
<template>
<span class="form-control shell">
<span
class="field control"
:class="model.cssClass"
>
<input
type="checkbox"
:checked="isChecked"
@input="toggleCheckBox"
>
</span>
</span>
</template>
<script>
export default {
props: {
model: {
type: Object,
required: true
}
},
data() {
return {
isChecked: this.model.value
};
},
methods: {
toggleCheckBox() {
this.isChecked = !this.isChecked;
const data = {
model: this.model,
value: this.isChecked
};
this.$emit('onChange', data);
}
}
};
</script>

View File

@@ -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;

View File

@@ -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);
});

View File

@@ -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;
}

View File

@@ -25,6 +25,7 @@ import EventEmitter from 'EventEmitter';
const ERRORS = {
TIMESYSTEM_KEY: 'All telemetry metadata must have a telemetry value with a key that matches the key of the active time system.',
TIMESYSTEM_KEY_NOTIFICATION: 'Telemetry metadata does not match the active time system.',
LOADED: 'Telemetry Collection has already been loaded.'
};
@@ -266,6 +267,10 @@ export class TelemetryCollection extends EventEmitter {
this.lastBounds = bounds;
if (isTick) {
if (this.timeKey === undefined) {
return;
}
// need to check futureBuffer and need to check
// if anything has fallen out of bounds
let startIndex = 0;
@@ -305,7 +310,6 @@ export class TelemetryCollection extends EventEmitter {
if (added.length > 0) {
this.emit('add', added);
}
} else {
// user bounds change, reset
this._reset();
@@ -325,12 +329,14 @@ export class TelemetryCollection extends EventEmitter {
let domains = this.metadata.valuesForHints(['domain']);
let domain = domains.find((d) => d.key === timeSystem.key);
if (domain === undefined) {
this._error(ERRORS.TIMESYSTEM_KEY);
if (domain !== undefined) {
// timeKey is used to create a dummy datum used for sorting
this.timeKey = domain.source;
} else {
this._warn(ERRORS.TIMESYSTEM_KEY);
this.openmct.notifications.alert(ERRORS.TIMESYSTEM_KEY_NOTIFICATION);
}
// timeKey is used to create a dummy datum used for sorting
this.timeKey = domain.source; // this defaults to key if no source is set
let metadataValue = this.metadata.value(timeSystem.key) || { format: timeSystem.key };
let valueFormatter = this.openmct.telemetry.getValueFormatter(metadataValue);
@@ -400,4 +406,8 @@ export class TelemetryCollection extends EventEmitter {
_error(message) {
throw new Error(message);
}
_warn(message) {
console.warn(message);
}
}

View File

@@ -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'

View File

@@ -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: "",

View File

@@ -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');

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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));

View File

@@ -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%;

View File

@@ -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: '';

View File

@@ -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);

View File

@@ -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;
}
});
}
);

View 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]);
});
});

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);
}
/**

View File

@@ -65,13 +65,8 @@ export default {
keyString: undefined
};
},
computed: {
imageHistorySize() {
return this.imageHistory.length;
}
},
watch: {
imageHistorySize(newSize, oldSize) {
imageHistory(newHistory, oldHistory) {
this.updatePlotImagery();
}
},

View File

@@ -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);
}

View File

@@ -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();

View File

@@ -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;

View File

@@ -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);

View 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();
};
};
}

View File

@@ -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);
};
}
}

View File

@@ -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);
};
}

View File

@@ -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 {

View File

@@ -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);
};
}

View File

@@ -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();
});
});
});

View File

@@ -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);
}

View File

@@ -245,6 +245,7 @@ export default {
element: this.snapshot.$el,
onDestroy: () => this.snapshot.$destroy(true),
size: 'large',
autoHide: false,
dismissable: true,
buttons: [
{

View File

@@ -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;
};
}
}

View File

@@ -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;
}

View File

@@ -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());
});

View File

@@ -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);

View File

@@ -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 = {

View File

@@ -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: {

View File

@@ -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;
});

View File

@@ -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;
}

View File

@@ -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));

View File

@@ -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]);
}
/**

View File

@@ -9,7 +9,11 @@ export default class TelemetryTableView {
this.objectPath = objectPath;
this.component = undefined;
this.table = new TelemetryTable(domainObject, openmct);
Object.defineProperty(this, 'table', {
value: new TelemetryTable(domainObject, openmct),
enumerable: false,
configurable: false
});
}
getViewContext() {

View File

@@ -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.
*
@@ -29,10 +29,6 @@ define(
_,
EventEmitter
) {
const LESS_THAN = -1;
const EQUAL = 0;
const GREATER_THAN = 1;
/**
* @constructor
*/
@@ -80,10 +76,7 @@ define(
this.rows = [];
}
for (let row of rowsToAdd) {
let index = this.sortedIndex(this.rows, row);
this.rows.splice(index, 0, row);
}
this.sortAndMergeRows(rowsToAdd);
// we emit filter no matter what to trigger
// an update of visible rows
@@ -92,58 +85,85 @@ define(
}
}
sortedLastIndex(rows, testRow) {
return this.sortedIndex(rows, testRow, _.sortedLastIndex);
}
sortAndMergeRows(rows) {
const sortedRowsToAdd = this.sortCollection(rows);
/**
* Finds the correct insertion point for the given row.
* Leverages lodash's `sortedIndex` function which implements a binary search.
* @private
*/
sortedIndex(rows, testRow, lodashFunction = _.sortedIndexBy) {
if (this.rows.length === 0) {
return 0;
this.rows = sortedRowsToAdd;
return;
}
const testRowValue = this.getValueForSortColumn(testRow);
const firstValue = this.getValueForSortColumn(this.rows[0]);
const lastValue = this.getValueForSortColumn(this.rows[this.rows.length - 1]);
const firstIncomingRow = sortedRowsToAdd[0];
const lastIncomingRow = sortedRowsToAdd[sortedRowsToAdd.length - 1];
const firstExistingRow = this.rows[0];
const lastExistingRow = this.rows[this.rows.length - 1];
if (this.firstRowInSortOrder(lastIncomingRow, firstExistingRow)
=== lastIncomingRow
) {
this.rows = [...sortedRowsToAdd, ...this.rows];
} else if (this.firstRowInSortOrder(lastExistingRow, firstIncomingRow)
=== lastExistingRow
) {
this.rows = [...this.rows, ...sortedRowsToAdd];
} else {
this.mergeSortedRows(sortedRowsToAdd);
}
}
sortCollection(rows) {
const sortedRows = _.orderBy(
rows,
row => row.getParsedValue(this.sortOptions.key), this.sortOptions.direction
);
return sortedRows;
}
mergeSortedRows(rows) {
const mergedRows = [];
let i = 0;
let j = 0;
while (i < this.rows.length && j < rows.length) {
const iRow = this.rows[i];
const jRow = rows[j];
if (this.firstRowInSortOrder(iRow, jRow) === iRow) {
mergedRows.push(iRow);
i++;
} else {
mergedRows.push(jRow);
j++;
}
}
// tail of existing rows is all that is left to merge
if (i < this.rows.length) {
for (i; i < this.rows.length; i++) {
mergedRows.push(this.rows[i]);
}
}
// tail of incoming rows is all that is left to merge
if (j < rows.length) {
for (j; j < rows.length; j++) {
mergedRows.push(rows[j]);
}
}
this.rows = mergedRows;
}
firstRowInSortOrder(row1, row2) {
const val1 = this.getValueForSortColumn(row1);
const val2 = this.getValueForSortColumn(row2);
if (this.sortOptions.direction === 'asc') {
if (testRowValue > lastValue) {
return this.rows.length;
} else if (testRowValue === lastValue) {
// Maintain stable sort
return this.rows.length;
} else if (testRowValue <= firstValue) {
return 0;
} else {
return lodashFunction(rows, testRow, (thisRow) => {
return this.getValueForSortColumn(thisRow);
});
}
return val1 <= val2 ? row1 : row2;
} else {
if (testRowValue >= firstValue) {
return 0;
} else if (testRowValue < lastValue) {
return this.rows.length;
} else if (testRowValue === lastValue) {
// Maintain stable sort
return this.rows.length;
} else {
// Use a custom comparison function to support descending sort.
return lodashFunction(rows, testRow, (thisRow) => {
const thisRowValue = this.getValueForSortColumn(thisRow);
if (testRowValue === thisRowValue) {
return EQUAL;
} else if (testRowValue < thisRowValue) {
return LESS_THAN;
} else {
return GREATER_THAN;
}
});
}
return val1 >= val2 ? row1 : row2;
}
}

View File

@@ -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));

View File

@@ -191,7 +191,7 @@ export default {
});
});
},
destroyed() {
beforeDestroy() {
this.active = false;
if (this.unlisten) {
this.unlisten();

View File

@@ -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');

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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]
];

View File

@@ -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;

View File

@@ -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;

View File

@@ -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)
};

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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 {

View File

@@ -1,4 +1,4 @@
import objectPathToUrl from '/src/tools/url';
import objectPathToUrl from '../../tools/url';
export default {
inject: ['openmct'],

View File

@@ -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);
}
};
}
}

View File

@@ -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
View 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
View 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'
});