Compare commits
11 Commits
a11y-impro
...
eslint-pla
Author | SHA1 | Date | |
---|---|---|---|
49cf23b7e9 | |||
37a2b05612 | |||
3c89236da2 | |||
827ec5690f | |||
5c8355c7f8 | |||
0f1d80c6b6 | |||
5af491382e | |||
1e8bb5fd8b | |||
e1fffaf39a | |||
6d47b5bc14 | |||
2e03bc394c |
@ -76,7 +76,8 @@ const config = {
|
||||
MCT: path.join(projectRootDir, 'src/MCT'),
|
||||
testUtils: path.join(projectRootDir, 'src/utils/testUtils.js'),
|
||||
objectUtils: path.join(projectRootDir, 'src/api/objects/object-utils.js'),
|
||||
utils: path.join(projectRootDir, 'src/utils')
|
||||
utils: path.join(projectRootDir, 'src/utils'),
|
||||
vue: 'vue/dist/vue.esm-bundler'
|
||||
}
|
||||
},
|
||||
plugins: [
|
||||
|
@ -2,13 +2,16 @@
|
||||
module.exports = {
|
||||
extends: ['plugin:playwright/playwright-test'],
|
||||
rules: {
|
||||
'playwright/max-nested-describe': ['error', { max: 1 }]
|
||||
'playwright/max-nested-describe': ['error', { max: 1 }],
|
||||
'playwright/no-nth-methods': 'error',
|
||||
'playwright/no-raw-locators': 'error'
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ['tests/visual/*.spec.js'],
|
||||
files: ['tests/visual-a11y/*.spec.js'],
|
||||
rules: {
|
||||
'playwright/no-wait-for-timeout': 'off'
|
||||
'playwright/no-wait-for-timeout': 'off',
|
||||
'playwright/expect-expect': 'off'
|
||||
}
|
||||
}
|
||||
]
|
||||
|
@ -574,6 +574,15 @@ A single e2e test in Open MCT is extended to run:
|
||||
- How is Open MCT extending default Playwright functionality?
|
||||
- What about Component Testing?
|
||||
|
||||
### Writing Tests
|
||||
|
||||
Playwright provides 3 supported methods of debugging and authoring tests:
|
||||
- A 'watch mode' for running tests locally and debugging on the fly
|
||||
- A 'debug mode' for debugging tests and writing assertions against tests
|
||||
- A 'VSCode plugin' for debugging tests within the VSCode IDE.
|
||||
|
||||
Generally, we encourage folks to use the watch mode and provide a script `npm run test:e2e:watch` which launches the launch mode ui and enables hot reloading on the dev server.
|
||||
|
||||
### e2e Troubleshooting
|
||||
|
||||
Please follow the general guide troubleshooting in [the general troubleshooting doc](../TESTING.md#troubleshooting-ci)
|
||||
|
@ -84,12 +84,10 @@ async function createDomainObjectWithDefaults(
|
||||
await page.getByRole('button', { name: 'Create' }).click();
|
||||
|
||||
// Click the object specified by 'type'
|
||||
await page.click(`li[role='menuitem']:text("${type}")`);
|
||||
await page.getByRole('menuitem', { name: new RegExp(`${type}`) }).click();
|
||||
|
||||
// Modify the name input field of the domain object to accept 'name'
|
||||
const nameInput = page.locator('form[name="mctForm"] .first input[type="text"]');
|
||||
await nameInput.fill('');
|
||||
await nameInput.fill(name);
|
||||
await page.getByLabel('Title', { exact: true }).fill(name);
|
||||
|
||||
if (page.testNotes) {
|
||||
// Fill the "Notes" section with information about the
|
||||
|
@ -35,7 +35,7 @@
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { test, expect } = require('./pluginFixtures');
|
||||
const { test, expect } = require('../pluginFixtures');
|
||||
const AxeBuilder = require('@axe-core/playwright').default;
|
||||
|
||||
// Constants for repeated values
|
@ -76,8 +76,7 @@ const config = {
|
||||
outputFolder: '../html-test-results' //Must be in different location due to https://github.com/microsoft/playwright/issues/12840
|
||||
}
|
||||
],
|
||||
['junit', { outputFile: '../test-results/results.xml' }],
|
||||
['@deploysentinel/playwright']
|
||||
['junit', { outputFile: '../test-results/results.xml' }]
|
||||
]
|
||||
};
|
||||
|
||||
|
54
e2e/playwright-watch.config.js
Normal file
@ -0,0 +1,54 @@
|
||||
/* eslint-disable no-undef */
|
||||
// playwright.config.js
|
||||
// @ts-check
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const { devices } = require('@playwright/test');
|
||||
const MAX_FAILURES = 5;
|
||||
const NUM_WORKERS = 2;
|
||||
|
||||
/** @type {import('@playwright/test').PlaywrightTestConfig} */
|
||||
const config = {
|
||||
retries: 0, //Retries 2 times for a total of 3 runs. When running sharded and with max-failures=5, this should ensure that flake is managed without failing the full suite
|
||||
testDir: 'tests',
|
||||
timeout: 60 * 1000,
|
||||
webServer: {
|
||||
command: 'npm run start', //Start in dev mode for hot reloading
|
||||
url: 'http://localhost:8080/#',
|
||||
timeout: 200 * 1000,
|
||||
reuseExistingServer: true //This was originally disabled to prevent differences in local debugging vs. CI. However, it significantly speeds up local debugging.
|
||||
},
|
||||
maxFailures: MAX_FAILURES, //Limits failures to 5 to reduce CI Waste
|
||||
workers: NUM_WORKERS, //Limit to 2 for CircleCI Agent
|
||||
use: {
|
||||
baseURL: 'http://localhost:8080/',
|
||||
headless: true,
|
||||
ignoreHTTPSErrors: true,
|
||||
screenshot: 'only-on-failure',
|
||||
trace: 'on-first-retry',
|
||||
video: 'off'
|
||||
},
|
||||
projects: [
|
||||
{
|
||||
name: 'chrome',
|
||||
testMatch: '**/*.spec.js', // run all tests
|
||||
use: {
|
||||
browserName: 'chromium'
|
||||
}
|
||||
}
|
||||
],
|
||||
reporter: [
|
||||
['list'],
|
||||
[
|
||||
'html',
|
||||
{
|
||||
open: 'never',
|
||||
outputFolder: '../html-test-results' //Must be in different location due to https://github.com/microsoft/playwright/issues/12840
|
||||
}
|
||||
],
|
||||
['junit', { outputFile: '../test-results/results.xml' }],
|
||||
['@deploysentinel/playwright']
|
||||
]
|
||||
};
|
||||
|
||||
module.exports = config;
|
@ -20,7 +20,7 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
const { test, expect } = require('../../pluginFixtures.js');
|
||||
const { test, expect } = require('../../fixtures/pluginFixtures');
|
||||
const {
|
||||
createDomainObjectWithDefaults,
|
||||
createNotification,
|
||||
|
@ -24,7 +24,7 @@
|
||||
This test suite is dedicated to tests which verify branding related components.
|
||||
*/
|
||||
|
||||
const { test, expect } = require('../../baseFixtures.js');
|
||||
const { test, expect } = require('../../../baseFixtures.js');
|
||||
|
||||
test.describe('Branding tests', () => {
|
||||
test('About Modal launches with basic branding properties', async ({ page }) => {
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
@ -33,8 +33,8 @@ comfortable running this test during a live mission?" Avoid creating or deleting
|
||||
Make no assumptions about the order that elements appear in the DOM.
|
||||
*/
|
||||
|
||||
const { test, expect } = require('../../pluginFixtures');
|
||||
const { createDomainObjectWithDefaults, expandEntireTree } = require('../../appActions');
|
||||
const { test, expect } = require('../../../pluginFixtures');
|
||||
const { createDomainObjectWithDefaults, expandEntireTree } = require('../../../appActions');
|
||||
|
||||
test.describe('Verify tooltips', () => {
|
||||
let folder1;
|
@ -20,8 +20,8 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
const { createDomainObjectWithDefaults, waitForPlotsToRender } = require('../../appActions');
|
||||
const { test, expect } = require('../../pluginFixtures');
|
||||
const { createDomainObjectWithDefaults, waitForPlotsToRender } = require('../../../appActions');
|
||||
const { test, expect } = require('../../../pluginFixtures');
|
||||
|
||||
test.describe('Tabs View', () => {
|
||||
test('Renders tabbed elements nicely', async ({ page }) => {
|
@ -25,8 +25,8 @@ Collection of Visual Tests set to run with browser clock manipulate made possibl
|
||||
clockOptions plugin fixture.
|
||||
*/
|
||||
|
||||
const { VISUAL_URL, MISSION_TIME } = require('../../constants');
|
||||
const { test, expect } = require('../../pluginFixtures');
|
||||
const { VISUAL_URL, MISSION_TIME } = require('../../../constants');
|
||||
const { test, expect } = require('../../../pluginFixtures');
|
||||
const percySnapshot = require('@percy/playwright');
|
||||
|
||||
test.describe('Visual - Controlled Clock', () => {
|
@ -34,14 +34,14 @@ test.describe("Visual - Check Notification Info Banner of 'Save successful' @a11
|
||||
await page.goto(VISUAL_URL, { waitUntil: 'domcontentloaded' });
|
||||
});
|
||||
|
||||
test("Create a clock, click on 'Save successful' banner and dismiss it", async ({
|
||||
test("Create a folder, click on 'Save successful' banner and dismiss it", async ({
|
||||
page,
|
||||
theme
|
||||
}) => {
|
||||
// Create a clock domain object
|
||||
await createDomainObjectWithDefaults(page, {
|
||||
type: 'Clock',
|
||||
name: 'Default Clock'
|
||||
type: 'Condition Widget',
|
||||
name: 'Visual Condition Widget'
|
||||
});
|
||||
// Click on the div with role="alert" that has "Save successful" text
|
||||
await page.locator('div[role="alert"]:has-text("Save successful")').click();
|
@ -1,138 +1,134 @@
|
||||
define(['lodash'], function (_) {
|
||||
var METADATA_BY_TYPE = {
|
||||
generator: {
|
||||
values: [
|
||||
{
|
||||
key: 'name',
|
||||
name: 'Name',
|
||||
format: 'string'
|
||||
},
|
||||
{
|
||||
key: 'utc',
|
||||
name: 'Time',
|
||||
format: 'utc',
|
||||
hints: {
|
||||
domain: 1
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'yesterday',
|
||||
name: 'Yesterday',
|
||||
format: 'utc',
|
||||
hints: {
|
||||
domain: 2
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'wavelengths',
|
||||
name: 'Wavelength',
|
||||
unit: 'nm',
|
||||
format: 'string[]',
|
||||
hints: {
|
||||
range: 4
|
||||
}
|
||||
},
|
||||
// Need to enable "LocalTimeSystem" plugin to make use of this
|
||||
// {
|
||||
// key: "local",
|
||||
// name: "Time",
|
||||
// format: "local-format",
|
||||
// source: "utc",
|
||||
// hints: {
|
||||
// domain: 3
|
||||
// }
|
||||
// },
|
||||
{
|
||||
key: 'sin',
|
||||
name: 'Sine',
|
||||
unit: 'Hz',
|
||||
formatString: '%0.2f',
|
||||
hints: {
|
||||
range: 1
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'cos',
|
||||
name: 'Cosine',
|
||||
unit: 'deg',
|
||||
formatString: '%0.2f',
|
||||
hints: {
|
||||
range: 2
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'intensities',
|
||||
name: 'Intensities',
|
||||
format: 'number[]',
|
||||
hints: {
|
||||
range: 3
|
||||
}
|
||||
const METADATA_BY_TYPE = {
|
||||
generator: {
|
||||
values: [
|
||||
{
|
||||
key: 'name',
|
||||
name: 'Name',
|
||||
format: 'string'
|
||||
},
|
||||
{
|
||||
key: 'utc',
|
||||
name: 'Time',
|
||||
format: 'utc',
|
||||
hints: {
|
||||
domain: 1
|
||||
}
|
||||
]
|
||||
},
|
||||
'example.state-generator': {
|
||||
values: [
|
||||
{
|
||||
key: 'name',
|
||||
name: 'Name',
|
||||
format: 'string'
|
||||
},
|
||||
{
|
||||
key: 'utc',
|
||||
name: 'Time',
|
||||
format: 'utc',
|
||||
hints: {
|
||||
domain: 1
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'local',
|
||||
name: 'Time',
|
||||
format: 'utc',
|
||||
source: 'utc',
|
||||
hints: {
|
||||
domain: 2
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'state',
|
||||
source: 'value',
|
||||
name: 'State',
|
||||
format: 'enum',
|
||||
enumerations: [
|
||||
{
|
||||
value: 0,
|
||||
string: 'OFF'
|
||||
},
|
||||
{
|
||||
value: 1,
|
||||
string: 'ON'
|
||||
}
|
||||
],
|
||||
hints: {
|
||||
range: 1
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'value',
|
||||
name: 'Value',
|
||||
hints: {
|
||||
range: 2
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'yesterday',
|
||||
name: 'Yesterday',
|
||||
format: 'utc',
|
||||
hints: {
|
||||
domain: 2
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
},
|
||||
{
|
||||
key: 'wavelengths',
|
||||
name: 'Wavelength',
|
||||
unit: 'nm',
|
||||
format: 'string[]',
|
||||
hints: {
|
||||
range: 4
|
||||
}
|
||||
},
|
||||
// Need to enable "LocalTimeSystem" plugin to make use of this
|
||||
// {
|
||||
// key: "local",
|
||||
// name: "Time",
|
||||
// format: "local-format",
|
||||
// source: "utc",
|
||||
// hints: {
|
||||
// domain: 3
|
||||
// }
|
||||
// },
|
||||
{
|
||||
key: 'sin',
|
||||
name: 'Sine',
|
||||
unit: 'Hz',
|
||||
formatString: '%0.2f',
|
||||
hints: {
|
||||
range: 1
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'cos',
|
||||
name: 'Cosine',
|
||||
unit: 'deg',
|
||||
formatString: '%0.2f',
|
||||
hints: {
|
||||
range: 2
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'intensities',
|
||||
name: 'Intensities',
|
||||
format: 'number[]',
|
||||
hints: {
|
||||
range: 3
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
'example.state-generator': {
|
||||
values: [
|
||||
{
|
||||
key: 'name',
|
||||
name: 'Name',
|
||||
format: 'string'
|
||||
},
|
||||
{
|
||||
key: 'utc',
|
||||
name: 'Time',
|
||||
format: 'utc',
|
||||
hints: {
|
||||
domain: 1
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'local',
|
||||
name: 'Time',
|
||||
format: 'utc',
|
||||
source: 'utc',
|
||||
hints: {
|
||||
domain: 2
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'state',
|
||||
source: 'value',
|
||||
name: 'State',
|
||||
format: 'enum',
|
||||
enumerations: [
|
||||
{
|
||||
value: 0,
|
||||
string: 'OFF'
|
||||
},
|
||||
{
|
||||
value: 1,
|
||||
string: 'ON'
|
||||
}
|
||||
],
|
||||
hints: {
|
||||
range: 1
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'value',
|
||||
name: 'Value',
|
||||
hints: {
|
||||
range: 2
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
function GeneratorMetadataProvider() {}
|
||||
export default function GeneratorMetadataProvider() {}
|
||||
|
||||
GeneratorMetadataProvider.prototype.supportsMetadata = function (domainObject) {
|
||||
return Object.prototype.hasOwnProperty.call(METADATA_BY_TYPE, domainObject.type);
|
||||
};
|
||||
GeneratorMetadataProvider.prototype.supportsMetadata = function (domainObject) {
|
||||
return Object.prototype.hasOwnProperty.call(METADATA_BY_TYPE, domainObject.type);
|
||||
};
|
||||
|
||||
GeneratorMetadataProvider.prototype.getMetadata = function (domainObject) {
|
||||
return Object.assign({}, domainObject.telemetry, METADATA_BY_TYPE[domainObject.type]);
|
||||
};
|
||||
|
||||
return GeneratorMetadataProvider;
|
||||
});
|
||||
GeneratorMetadataProvider.prototype.getMetadata = function (domainObject) {
|
||||
return Object.assign({}, domainObject.telemetry, METADATA_BY_TYPE[domainObject.type]);
|
||||
};
|
||||
|
@ -20,86 +20,84 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(['./WorkerInterface'], function (WorkerInterface) {
|
||||
var REQUEST_DEFAULTS = {
|
||||
amplitude: 1,
|
||||
period: 10,
|
||||
offset: 0,
|
||||
dataRateInHz: 1,
|
||||
randomness: 0,
|
||||
phase: 0,
|
||||
loadDelay: 0,
|
||||
infinityValues: false,
|
||||
exceedFloat32: false
|
||||
};
|
||||
import WorkerInterface from './WorkerInterface';
|
||||
|
||||
function GeneratorProvider(openmct, StalenessProvider) {
|
||||
this.openmct = openmct;
|
||||
this.workerInterface = new WorkerInterface(openmct, StalenessProvider);
|
||||
}
|
||||
const REQUEST_DEFAULTS = {
|
||||
amplitude: 1,
|
||||
period: 10,
|
||||
offset: 0,
|
||||
dataRateInHz: 1,
|
||||
randomness: 0,
|
||||
phase: 0,
|
||||
loadDelay: 0,
|
||||
infinityValues: false,
|
||||
exceedFloat32: false
|
||||
};
|
||||
|
||||
GeneratorProvider.prototype.canProvideTelemetry = function (domainObject) {
|
||||
return domainObject.type === 'generator';
|
||||
};
|
||||
export default function GeneratorProvider(openmct, StalenessProvider) {
|
||||
this.openmct = openmct;
|
||||
this.workerInterface = new WorkerInterface(openmct, StalenessProvider);
|
||||
}
|
||||
|
||||
GeneratorProvider.prototype.supportsRequest = GeneratorProvider.prototype.supportsSubscribe =
|
||||
GeneratorProvider.prototype.canProvideTelemetry;
|
||||
GeneratorProvider.prototype.canProvideTelemetry = function (domainObject) {
|
||||
return domainObject.type === 'generator';
|
||||
};
|
||||
|
||||
GeneratorProvider.prototype.makeWorkerRequest = function (domainObject, request) {
|
||||
var props = [
|
||||
'amplitude',
|
||||
'period',
|
||||
'offset',
|
||||
'dataRateInHz',
|
||||
'randomness',
|
||||
'phase',
|
||||
'loadDelay',
|
||||
'infinityValues',
|
||||
'exceedFloat32'
|
||||
];
|
||||
GeneratorProvider.prototype.supportsRequest = GeneratorProvider.prototype.supportsSubscribe =
|
||||
GeneratorProvider.prototype.canProvideTelemetry;
|
||||
|
||||
request = request || {};
|
||||
GeneratorProvider.prototype.makeWorkerRequest = function (domainObject, request) {
|
||||
var props = [
|
||||
'amplitude',
|
||||
'period',
|
||||
'offset',
|
||||
'dataRateInHz',
|
||||
'randomness',
|
||||
'phase',
|
||||
'loadDelay',
|
||||
'infinityValues',
|
||||
'exceedFloat32'
|
||||
];
|
||||
|
||||
var workerRequest = {};
|
||||
request = request || {};
|
||||
|
||||
props.forEach(function (prop) {
|
||||
if (
|
||||
domainObject.telemetry &&
|
||||
Object.prototype.hasOwnProperty.call(domainObject.telemetry, prop)
|
||||
) {
|
||||
workerRequest[prop] = domainObject.telemetry[prop];
|
||||
}
|
||||
var workerRequest = {};
|
||||
|
||||
if (request && Object.prototype.hasOwnProperty.call(request, prop)) {
|
||||
workerRequest[prop] = request[prop];
|
||||
}
|
||||
props.forEach(function (prop) {
|
||||
if (
|
||||
domainObject.telemetry &&
|
||||
Object.prototype.hasOwnProperty.call(domainObject.telemetry, prop)
|
||||
) {
|
||||
workerRequest[prop] = domainObject.telemetry[prop];
|
||||
}
|
||||
|
||||
if (!Object.prototype.hasOwnProperty.call(workerRequest, prop)) {
|
||||
workerRequest[prop] = REQUEST_DEFAULTS[prop];
|
||||
}
|
||||
if (request && Object.prototype.hasOwnProperty.call(request, prop)) {
|
||||
workerRequest[prop] = request[prop];
|
||||
}
|
||||
|
||||
workerRequest[prop] = Number(workerRequest[prop]);
|
||||
});
|
||||
if (!Object.prototype.hasOwnProperty.call(workerRequest, prop)) {
|
||||
workerRequest[prop] = REQUEST_DEFAULTS[prop];
|
||||
}
|
||||
|
||||
workerRequest.id = this.openmct.objects.makeKeyString(domainObject.identifier);
|
||||
workerRequest.name = domainObject.name;
|
||||
workerRequest[prop] = Number(workerRequest[prop]);
|
||||
});
|
||||
|
||||
return workerRequest;
|
||||
};
|
||||
workerRequest.id = this.openmct.objects.makeKeyString(domainObject.identifier);
|
||||
workerRequest.name = domainObject.name;
|
||||
|
||||
GeneratorProvider.prototype.request = function (domainObject, request) {
|
||||
var workerRequest = this.makeWorkerRequest(domainObject, request);
|
||||
workerRequest.start = request.start;
|
||||
workerRequest.end = request.end;
|
||||
return workerRequest;
|
||||
};
|
||||
|
||||
return this.workerInterface.request(workerRequest);
|
||||
};
|
||||
GeneratorProvider.prototype.request = function (domainObject, request) {
|
||||
var workerRequest = this.makeWorkerRequest(domainObject, request);
|
||||
workerRequest.start = request.start;
|
||||
workerRequest.end = request.end;
|
||||
|
||||
GeneratorProvider.prototype.subscribe = function (domainObject, callback) {
|
||||
var workerRequest = this.makeWorkerRequest(domainObject, {});
|
||||
return this.workerInterface.request(workerRequest);
|
||||
};
|
||||
|
||||
return this.workerInterface.subscribe(workerRequest, callback);
|
||||
};
|
||||
GeneratorProvider.prototype.subscribe = function (domainObject, callback) {
|
||||
var workerRequest = this.makeWorkerRequest(domainObject, {});
|
||||
|
||||
return GeneratorProvider;
|
||||
});
|
||||
return this.workerInterface.subscribe(workerRequest, callback);
|
||||
};
|
||||
|
@ -20,147 +20,143 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define([], function () {
|
||||
var PURPLE = {
|
||||
sin: 2.2,
|
||||
cos: 2.2
|
||||
var PURPLE = {
|
||||
sin: 2.2,
|
||||
cos: 2.2
|
||||
},
|
||||
RED = {
|
||||
sin: 0.9,
|
||||
cos: 0.9
|
||||
},
|
||||
ORANGE = {
|
||||
sin: 0.7,
|
||||
cos: 0.7
|
||||
},
|
||||
YELLOW = {
|
||||
sin: 0.5,
|
||||
cos: 0.5
|
||||
},
|
||||
CYAN = {
|
||||
sin: 0.45,
|
||||
cos: 0.45
|
||||
},
|
||||
LIMITS = {
|
||||
rh: {
|
||||
cssClass: 'is-limit--upr is-limit--red',
|
||||
low: RED,
|
||||
high: Number.POSITIVE_INFINITY,
|
||||
name: 'Red High'
|
||||
},
|
||||
RED = {
|
||||
sin: 0.9,
|
||||
cos: 0.9
|
||||
rl: {
|
||||
cssClass: 'is-limit--lwr is-limit--red',
|
||||
high: -RED,
|
||||
low: Number.NEGATIVE_INFINITY,
|
||||
name: 'Red Low'
|
||||
},
|
||||
ORANGE = {
|
||||
sin: 0.7,
|
||||
cos: 0.7
|
||||
yh: {
|
||||
cssClass: 'is-limit--upr is-limit--yellow',
|
||||
low: YELLOW,
|
||||
high: RED,
|
||||
name: 'Yellow High'
|
||||
},
|
||||
YELLOW = {
|
||||
sin: 0.5,
|
||||
cos: 0.5
|
||||
},
|
||||
CYAN = {
|
||||
sin: 0.45,
|
||||
cos: 0.45
|
||||
},
|
||||
LIMITS = {
|
||||
rh: {
|
||||
cssClass: 'is-limit--upr is-limit--red',
|
||||
low: RED,
|
||||
high: Number.POSITIVE_INFINITY,
|
||||
name: 'Red High'
|
||||
},
|
||||
rl: {
|
||||
cssClass: 'is-limit--lwr is-limit--red',
|
||||
high: -RED,
|
||||
low: Number.NEGATIVE_INFINITY,
|
||||
name: 'Red Low'
|
||||
},
|
||||
yh: {
|
||||
cssClass: 'is-limit--upr is-limit--yellow',
|
||||
low: YELLOW,
|
||||
high: RED,
|
||||
name: 'Yellow High'
|
||||
},
|
||||
yl: {
|
||||
cssClass: 'is-limit--lwr is-limit--yellow',
|
||||
low: -RED,
|
||||
high: -YELLOW,
|
||||
name: 'Yellow Low'
|
||||
}
|
||||
};
|
||||
|
||||
function SinewaveLimitProvider() {}
|
||||
|
||||
SinewaveLimitProvider.prototype.supportsLimits = function (domainObject) {
|
||||
return domainObject.type === 'generator';
|
||||
yl: {
|
||||
cssClass: 'is-limit--lwr is-limit--yellow',
|
||||
low: -RED,
|
||||
high: -YELLOW,
|
||||
name: 'Yellow Low'
|
||||
}
|
||||
};
|
||||
|
||||
SinewaveLimitProvider.prototype.getLimitEvaluator = function (domainObject) {
|
||||
return {
|
||||
evaluate: function (datum, valueMetadata) {
|
||||
var range = valueMetadata && valueMetadata.key;
|
||||
export default function SinewaveLimitProvider() {}
|
||||
|
||||
if (datum[range] > RED[range]) {
|
||||
return LIMITS.rh;
|
||||
}
|
||||
SinewaveLimitProvider.prototype.supportsLimits = function (domainObject) {
|
||||
return domainObject.type === 'generator';
|
||||
};
|
||||
|
||||
if (datum[range] < -RED[range]) {
|
||||
return LIMITS.rl;
|
||||
}
|
||||
SinewaveLimitProvider.prototype.getLimitEvaluator = function (domainObject) {
|
||||
return {
|
||||
evaluate: function (datum, valueMetadata) {
|
||||
var range = valueMetadata && valueMetadata.key;
|
||||
|
||||
if (datum[range] > YELLOW[range]) {
|
||||
return LIMITS.yh;
|
||||
}
|
||||
|
||||
if (datum[range] < -YELLOW[range]) {
|
||||
return LIMITS.yl;
|
||||
}
|
||||
if (datum[range] > RED[range]) {
|
||||
return LIMITS.rh;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
SinewaveLimitProvider.prototype.getLimits = function (domainObject) {
|
||||
return {
|
||||
limits: function () {
|
||||
return Promise.resolve({
|
||||
WATCH: {
|
||||
low: {
|
||||
color: 'cyan',
|
||||
sin: -CYAN.sin,
|
||||
cos: -CYAN.cos
|
||||
},
|
||||
high: {
|
||||
color: 'cyan',
|
||||
...CYAN
|
||||
}
|
||||
if (datum[range] < -RED[range]) {
|
||||
return LIMITS.rl;
|
||||
}
|
||||
|
||||
if (datum[range] > YELLOW[range]) {
|
||||
return LIMITS.yh;
|
||||
}
|
||||
|
||||
if (datum[range] < -YELLOW[range]) {
|
||||
return LIMITS.yl;
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
SinewaveLimitProvider.prototype.getLimits = function (domainObject) {
|
||||
return {
|
||||
limits: function () {
|
||||
return Promise.resolve({
|
||||
WATCH: {
|
||||
low: {
|
||||
color: 'cyan',
|
||||
sin: -CYAN.sin,
|
||||
cos: -CYAN.cos
|
||||
},
|
||||
WARNING: {
|
||||
low: {
|
||||
color: 'yellow',
|
||||
sin: -YELLOW.sin,
|
||||
cos: -YELLOW.cos
|
||||
},
|
||||
high: {
|
||||
color: 'yellow',
|
||||
...YELLOW
|
||||
}
|
||||
},
|
||||
DISTRESS: {
|
||||
low: {
|
||||
color: 'orange',
|
||||
sin: -ORANGE.sin,
|
||||
cos: -ORANGE.cos
|
||||
},
|
||||
high: {
|
||||
color: 'orange',
|
||||
...ORANGE
|
||||
}
|
||||
},
|
||||
CRITICAL: {
|
||||
low: {
|
||||
color: 'red',
|
||||
sin: -RED.sin,
|
||||
cos: -RED.cos
|
||||
},
|
||||
high: {
|
||||
color: 'red',
|
||||
...RED
|
||||
}
|
||||
},
|
||||
SEVERE: {
|
||||
low: {
|
||||
color: 'purple',
|
||||
sin: -PURPLE.sin,
|
||||
cos: -PURPLE.cos
|
||||
},
|
||||
high: {
|
||||
color: 'purple',
|
||||
...PURPLE
|
||||
}
|
||||
high: {
|
||||
color: 'cyan',
|
||||
...CYAN
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
},
|
||||
WARNING: {
|
||||
low: {
|
||||
color: 'yellow',
|
||||
sin: -YELLOW.sin,
|
||||
cos: -YELLOW.cos
|
||||
},
|
||||
high: {
|
||||
color: 'yellow',
|
||||
...YELLOW
|
||||
}
|
||||
},
|
||||
DISTRESS: {
|
||||
low: {
|
||||
color: 'orange',
|
||||
sin: -ORANGE.sin,
|
||||
cos: -ORANGE.cos
|
||||
},
|
||||
high: {
|
||||
color: 'orange',
|
||||
...ORANGE
|
||||
}
|
||||
},
|
||||
CRITICAL: {
|
||||
low: {
|
||||
color: 'red',
|
||||
sin: -RED.sin,
|
||||
cos: -RED.cos
|
||||
},
|
||||
high: {
|
||||
color: 'red',
|
||||
...RED
|
||||
}
|
||||
},
|
||||
SEVERE: {
|
||||
low: {
|
||||
color: 'purple',
|
||||
sin: -PURPLE.sin,
|
||||
cos: -PURPLE.cos
|
||||
},
|
||||
high: {
|
||||
color: 'purple',
|
||||
...PURPLE
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return SinewaveLimitProvider;
|
||||
});
|
||||
};
|
||||
|
@ -20,56 +20,52 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define([], function () {
|
||||
function StateGeneratorProvider() {}
|
||||
export default function StateGeneratorProvider() {}
|
||||
|
||||
function pointForTimestamp(timestamp, duration, name) {
|
||||
return {
|
||||
name: name,
|
||||
utc: Math.floor(timestamp / duration) * duration,
|
||||
value: Math.floor(timestamp / duration) % 2
|
||||
};
|
||||
function pointForTimestamp(timestamp, duration, name) {
|
||||
return {
|
||||
name: name,
|
||||
utc: Math.floor(timestamp / duration) * duration,
|
||||
value: Math.floor(timestamp / duration) % 2
|
||||
};
|
||||
}
|
||||
|
||||
StateGeneratorProvider.prototype.supportsSubscribe = function (domainObject) {
|
||||
return domainObject.type === 'example.state-generator';
|
||||
};
|
||||
|
||||
StateGeneratorProvider.prototype.subscribe = function (domainObject, callback) {
|
||||
var duration = domainObject.telemetry.duration * 1000;
|
||||
|
||||
var interval = setInterval(function () {
|
||||
var now = Date.now();
|
||||
var datum = pointForTimestamp(now, duration, domainObject.name);
|
||||
datum.value = String(datum.value);
|
||||
callback(datum);
|
||||
}, duration);
|
||||
|
||||
return function () {
|
||||
clearInterval(interval);
|
||||
};
|
||||
};
|
||||
|
||||
StateGeneratorProvider.prototype.supportsRequest = function (domainObject, options) {
|
||||
return domainObject.type === 'example.state-generator';
|
||||
};
|
||||
|
||||
StateGeneratorProvider.prototype.request = function (domainObject, options) {
|
||||
var start = options.start;
|
||||
var end = Math.min(Date.now(), options.end); // no future values
|
||||
var duration = domainObject.telemetry.duration * 1000;
|
||||
if (options.strategy === 'latest' || options.size === 1) {
|
||||
start = end;
|
||||
}
|
||||
|
||||
StateGeneratorProvider.prototype.supportsSubscribe = function (domainObject) {
|
||||
return domainObject.type === 'example.state-generator';
|
||||
};
|
||||
var data = [];
|
||||
while (start <= end && data.length < 5000) {
|
||||
data.push(pointForTimestamp(start, duration, domainObject.name));
|
||||
start += duration;
|
||||
}
|
||||
|
||||
StateGeneratorProvider.prototype.subscribe = function (domainObject, callback) {
|
||||
var duration = domainObject.telemetry.duration * 1000;
|
||||
|
||||
var interval = setInterval(function () {
|
||||
var now = Date.now();
|
||||
var datum = pointForTimestamp(now, duration, domainObject.name);
|
||||
datum.value = String(datum.value);
|
||||
callback(datum);
|
||||
}, duration);
|
||||
|
||||
return function () {
|
||||
clearInterval(interval);
|
||||
};
|
||||
};
|
||||
|
||||
StateGeneratorProvider.prototype.supportsRequest = function (domainObject, options) {
|
||||
return domainObject.type === 'example.state-generator';
|
||||
};
|
||||
|
||||
StateGeneratorProvider.prototype.request = function (domainObject, options) {
|
||||
var start = options.start;
|
||||
var end = Math.min(Date.now(), options.end); // no future values
|
||||
var duration = domainObject.telemetry.duration * 1000;
|
||||
if (options.strategy === 'latest' || options.size === 1) {
|
||||
start = end;
|
||||
}
|
||||
|
||||
var data = [];
|
||||
while (start <= end && data.length < 5000) {
|
||||
data.push(pointForTimestamp(start, duration, domainObject.name));
|
||||
start += duration;
|
||||
}
|
||||
|
||||
return Promise.resolve(data);
|
||||
};
|
||||
|
||||
return StateGeneratorProvider;
|
||||
});
|
||||
return Promise.resolve(data);
|
||||
};
|
||||
|
@ -20,88 +20,86 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(['uuid'], function ({ v4: uuid }) {
|
||||
function WorkerInterface(openmct, StalenessProvider) {
|
||||
// eslint-disable-next-line no-undef
|
||||
const workerUrl = `${openmct.getAssetPath()}${__OPENMCT_ROOT_RELATIVE__}generatorWorker.js`;
|
||||
this.StalenessProvider = StalenessProvider;
|
||||
this.worker = new Worker(workerUrl);
|
||||
this.worker.onmessage = this.onMessage.bind(this);
|
||||
this.callbacks = {};
|
||||
this.staleTelemetryIds = {};
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
this.watchStaleness();
|
||||
export default function WorkerInterface(openmct, StalenessProvider) {
|
||||
// eslint-disable-next-line no-undef
|
||||
const workerUrl = `${openmct.getAssetPath()}${__OPENMCT_ROOT_RELATIVE__}generatorWorker.js`;
|
||||
this.StalenessProvider = StalenessProvider;
|
||||
this.worker = new Worker(workerUrl);
|
||||
this.worker.onmessage = this.onMessage.bind(this);
|
||||
this.callbacks = {};
|
||||
this.staleTelemetryIds = {};
|
||||
|
||||
this.watchStaleness();
|
||||
}
|
||||
|
||||
WorkerInterface.prototype.watchStaleness = function () {
|
||||
this.StalenessProvider.on('stalenessEvent', ({ id, isStale }) => {
|
||||
this.staleTelemetryIds[id] = isStale;
|
||||
});
|
||||
};
|
||||
|
||||
WorkerInterface.prototype.onMessage = function (message) {
|
||||
message = message.data;
|
||||
var callback = this.callbacks[message.id];
|
||||
if (callback) {
|
||||
callback(message);
|
||||
}
|
||||
};
|
||||
|
||||
WorkerInterface.prototype.dispatch = function (request, data, callback) {
|
||||
var message = {
|
||||
request: request,
|
||||
data: data,
|
||||
id: uuid()
|
||||
};
|
||||
|
||||
if (callback) {
|
||||
this.callbacks[message.id] = callback;
|
||||
}
|
||||
|
||||
WorkerInterface.prototype.watchStaleness = function () {
|
||||
this.StalenessProvider.on('stalenessEvent', ({ id, isStale }) => {
|
||||
this.staleTelemetryIds[id] = isStale;
|
||||
});
|
||||
};
|
||||
this.worker.postMessage(message);
|
||||
|
||||
WorkerInterface.prototype.onMessage = function (message) {
|
||||
message = message.data;
|
||||
var callback = this.callbacks[message.id];
|
||||
if (callback) {
|
||||
callback(message);
|
||||
}
|
||||
};
|
||||
return message.id;
|
||||
};
|
||||
|
||||
WorkerInterface.prototype.dispatch = function (request, data, callback) {
|
||||
var message = {
|
||||
request: request,
|
||||
data: data,
|
||||
id: uuid()
|
||||
};
|
||||
WorkerInterface.prototype.request = function (request) {
|
||||
var deferred = {};
|
||||
var promise = new Promise(function (resolve, reject) {
|
||||
deferred.resolve = resolve;
|
||||
deferred.reject = reject;
|
||||
});
|
||||
var messageId;
|
||||
|
||||
if (callback) {
|
||||
this.callbacks[message.id] = callback;
|
||||
let self = this;
|
||||
function callback(message) {
|
||||
if (message.error) {
|
||||
deferred.reject(message.error);
|
||||
} else {
|
||||
deferred.resolve(message.data);
|
||||
}
|
||||
|
||||
this.worker.postMessage(message);
|
||||
delete self.callbacks[messageId];
|
||||
}
|
||||
|
||||
return message.id;
|
||||
};
|
||||
messageId = this.dispatch('request', request, callback.bind(this));
|
||||
|
||||
WorkerInterface.prototype.request = function (request) {
|
||||
var deferred = {};
|
||||
var promise = new Promise(function (resolve, reject) {
|
||||
deferred.resolve = resolve;
|
||||
deferred.reject = reject;
|
||||
});
|
||||
var messageId;
|
||||
return promise;
|
||||
};
|
||||
|
||||
let self = this;
|
||||
function callback(message) {
|
||||
if (message.error) {
|
||||
deferred.reject(message.error);
|
||||
} else {
|
||||
deferred.resolve(message.data);
|
||||
}
|
||||
|
||||
delete self.callbacks[messageId];
|
||||
WorkerInterface.prototype.subscribe = function (request, cb) {
|
||||
const { id, loadDelay } = request;
|
||||
const messageId = this.dispatch('subscribe', request, (message) => {
|
||||
if (!this.staleTelemetryIds[id]) {
|
||||
setTimeout(() => cb(message.data), Math.max(loadDelay, 0));
|
||||
}
|
||||
});
|
||||
|
||||
messageId = this.dispatch('request', request, callback.bind(this));
|
||||
|
||||
return promise;
|
||||
};
|
||||
|
||||
WorkerInterface.prototype.subscribe = function (request, cb) {
|
||||
const { id, loadDelay } = request;
|
||||
const messageId = this.dispatch('subscribe', request, (message) => {
|
||||
if (!this.staleTelemetryIds[id]) {
|
||||
setTimeout(() => cb(message.data), Math.max(loadDelay, 0));
|
||||
}
|
||||
return function () {
|
||||
this.dispatch('unsubscribe', {
|
||||
id: messageId
|
||||
});
|
||||
|
||||
return function () {
|
||||
this.dispatch('unsubscribe', {
|
||||
id: messageId
|
||||
});
|
||||
delete this.callbacks[messageId];
|
||||
}.bind(this);
|
||||
};
|
||||
|
||||
return WorkerInterface;
|
||||
});
|
||||
delete this.callbacks[messageId];
|
||||
}.bind(this);
|
||||
};
|
||||
|
@ -75,7 +75,7 @@ if (document.currentScript) {
|
||||
* @property {OpenMCTComponent[]} components
|
||||
*/
|
||||
|
||||
const MCT = require('./src/MCT');
|
||||
const { MCT } = require('./src/MCT');
|
||||
|
||||
/** @type {OpenMCT} */
|
||||
const openmct = new MCT();
|
||||
|
@ -32,7 +32,7 @@
|
||||
"eslint-config-prettier": "9.0.0",
|
||||
"eslint-plugin-compat": "4.2.0",
|
||||
"eslint-plugin-no-unsanitized": "4.0.2",
|
||||
"eslint-plugin-playwright": "0.12.0",
|
||||
"eslint-plugin-playwright": "0.20.0",
|
||||
"eslint-plugin-prettier": "4.2.1",
|
||||
"eslint-plugin-simple-import-sort": "10.0.0",
|
||||
"eslint-plugin-unicorn": "49.0.0",
|
||||
@ -115,7 +115,7 @@
|
||||
"test:e2e:visual:ci": "percy exec --config ./e2e/.percy.ci.yml --partial -- npx playwright test --config=e2e/playwright-visual-a11y.config.js --project=chrome --grep-invert @unstable",
|
||||
"test:e2e:visual:full": "percy exec --config ./e2e/.percy.nightly.yml -- npx playwright test --config=e2e/playwright-visual-a11y.config.js --grep-invert @unstable",
|
||||
"test:e2e:full": "npx playwright test --config=e2e/playwright-ci.config.js --grep-invert @couchdb",
|
||||
"test:e2e:watch": "npx playwright test --ui --config=e2e/playwright-ci.config.js",
|
||||
"test:e2e:watch": "npx playwright test --ui --config=e2e/playwright-watch.config.js",
|
||||
"test:perf:contract": "npx playwright test --config=e2e/playwright-performance-dev.config.js",
|
||||
"test:perf:localhost": "npx playwright test --config=e2e/playwright-performance-prod.config.js --project=chrome",
|
||||
"test:perf:memory": "npx playwright test --config=e2e/playwright-performance-prod.config.js --project=chrome-memory",
|
||||
|
471
src/MCT.js
@ -20,72 +20,66 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
/* eslint-disable no-undef */
|
||||
define([
|
||||
'EventEmitter',
|
||||
'./api/api',
|
||||
'./api/overlays/OverlayAPI',
|
||||
'./api/tooltips/ToolTipAPI',
|
||||
'./selection/Selection',
|
||||
'./plugins/plugins',
|
||||
'./ui/registries/ViewRegistry',
|
||||
'./plugins/imagery/plugin',
|
||||
'./ui/registries/InspectorViewRegistry',
|
||||
'./ui/registries/ToolbarRegistry',
|
||||
'./ui/router/ApplicationRouter',
|
||||
'./ui/router/Browse',
|
||||
'./ui/layout/AppLayout.vue',
|
||||
'./ui/preview/plugin',
|
||||
'./api/Branding',
|
||||
'./plugins/licenses/plugin',
|
||||
'./plugins/remove/plugin',
|
||||
'./plugins/move/plugin',
|
||||
'./plugins/linkAction/plugin',
|
||||
'./plugins/duplicate/plugin',
|
||||
'./plugins/importFromJSONAction/plugin',
|
||||
'./plugins/exportAsJSONAction/plugin',
|
||||
'vue'
|
||||
], function (
|
||||
EventEmitter,
|
||||
api,
|
||||
OverlayAPI,
|
||||
ToolTipAPI,
|
||||
Selection,
|
||||
plugins,
|
||||
ViewRegistry,
|
||||
ImageryPlugin,
|
||||
InspectorViewRegistry,
|
||||
ToolbarRegistry,
|
||||
ApplicationRouter,
|
||||
Browse,
|
||||
Layout,
|
||||
PreviewPlugin,
|
||||
BrandingAPI,
|
||||
LicensesPlugin,
|
||||
RemoveActionPlugin,
|
||||
MoveActionPlugin,
|
||||
LinkActionPlugin,
|
||||
DuplicateActionPlugin,
|
||||
ImportFromJSONAction,
|
||||
ExportAsJSONAction,
|
||||
Vue
|
||||
) {
|
||||
/**
|
||||
* Open MCT is an extensible web application for building mission
|
||||
* control user interfaces. This module is itself an instance of
|
||||
* [MCT]{@link module:openmct.MCT}, which provides an interface for
|
||||
* configuring and executing the application.
|
||||
*
|
||||
* @exports openmct
|
||||
*/
|
||||
import EventEmitter from 'EventEmitter';
|
||||
import { createApp, markRaw } from 'vue';
|
||||
|
||||
/**
|
||||
* The Open MCT application. This may be configured by installing plugins
|
||||
* or registering extensions before the application is started.
|
||||
* @constructor
|
||||
* @memberof module:openmct
|
||||
*/
|
||||
function MCT() {
|
||||
import ActionsAPI from './api/actions/ActionsAPI';
|
||||
import AnnotationAPI from './api/annotation/AnnotationAPI';
|
||||
import BrandingAPI from './api/Branding';
|
||||
import CompositionAPI from './api/composition/CompositionAPI';
|
||||
import EditorAPI from './api/Editor';
|
||||
import FaultManagementAPI from './api/faultmanagement/FaultManagementAPI';
|
||||
import FormsAPI from './api/forms/FormsAPI';
|
||||
import IndicatorAPI from './api/indicators/IndicatorAPI';
|
||||
import MenuAPI from './api/menu/MenuAPI';
|
||||
import NotificationAPI from './api/notifications/NotificationAPI';
|
||||
import ObjectAPI from './api/objects/ObjectAPI';
|
||||
import OverlayAPI from './api/overlays/OverlayAPI';
|
||||
import PriorityAPI from './api/priority/PriorityAPI';
|
||||
import StatusAPI from './api/status/StatusAPI';
|
||||
import TelemetryAPI from './api/telemetry/TelemetryAPI';
|
||||
import TimeAPI from './api/time/TimeAPI';
|
||||
import ToolTipAPI from './api/tooltips/ToolTipAPI';
|
||||
import TypeRegistry from './api/types/TypeRegistry';
|
||||
import UserAPI from './api/user/UserAPI';
|
||||
import DuplicateActionPlugin from './plugins/duplicate/plugin';
|
||||
import ExportAsJSONAction from './plugins/exportAsJSONAction/plugin';
|
||||
import ImageryPlugin from './plugins/imagery/plugin';
|
||||
import ImportFromJSONAction from './plugins/importFromJSONAction/plugin';
|
||||
import LicensesPlugin from './plugins/licenses/plugin';
|
||||
import LinkActionPlugin from './plugins/linkAction/plugin';
|
||||
import MoveActionPlugin from './plugins/move/plugin';
|
||||
import plugins from './plugins/plugins';
|
||||
import RemoveActionPlugin from './plugins/remove/plugin';
|
||||
import Selection from './selection/Selection';
|
||||
import Layout from './ui/layout/AppLayout.vue';
|
||||
import PreviewPlugin from './ui/preview/plugin';
|
||||
import InspectorViewRegistry from './ui/registries/InspectorViewRegistry';
|
||||
import ToolbarRegistry from './ui/registries/ToolbarRegistry';
|
||||
import ViewRegistry from './ui/registries/ViewRegistry';
|
||||
import ApplicationRouter from './ui/router/ApplicationRouter';
|
||||
import Browse from './ui/router/Browse';
|
||||
|
||||
/**
|
||||
* Open MCT is an extensible web application for building mission
|
||||
* control user interfaces. This module is itself an instance of
|
||||
* [MCT]{@link module:openmct.MCT}, which provides an interface for
|
||||
* configuring and executing the application.
|
||||
*
|
||||
* @exports openmct
|
||||
*/
|
||||
|
||||
/**
|
||||
* The Open MCT application. This may be configured by installing plugins
|
||||
* or registering extensions before the application is started.
|
||||
* @constructor
|
||||
* @memberof module:openmct
|
||||
*/
|
||||
export class MCT extends EventEmitter {
|
||||
constructor() {
|
||||
super();
|
||||
EventEmitter.call(this);
|
||||
|
||||
this.buildInfo = {
|
||||
version: __OPENMCT_VERSION__,
|
||||
buildDate: __OPENMCT_BUILD_DATE__,
|
||||
@ -95,169 +89,140 @@ define([
|
||||
|
||||
this.destroy = this.destroy.bind(this);
|
||||
this.defaultClock = 'local';
|
||||
[
|
||||
/**
|
||||
* Tracks current selection state of the application.
|
||||
* @private
|
||||
*/
|
||||
['selection', () => new Selection.default(this)],
|
||||
|
||||
/**
|
||||
* MCT's time conductor, which may be used to synchronize view contents
|
||||
* for telemetry- or time-based views.
|
||||
* @type {module:openmct.TimeConductor}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name conductor
|
||||
*/
|
||||
['time', () => new api.TimeAPI(this)],
|
||||
this.plugins = plugins;
|
||||
|
||||
/**
|
||||
* An interface for interacting with the composition of domain objects.
|
||||
* The composition of a domain object is the list of other domain
|
||||
* objects it "contains" (for instance, that should be displayed
|
||||
* beneath it in the tree.)
|
||||
*
|
||||
* `composition` may be called as a function, in which case it acts
|
||||
* as [`composition.get`]{@link module:openmct.CompositionAPI#get}.
|
||||
*
|
||||
* @type {module:openmct.CompositionAPI}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name composition
|
||||
*/
|
||||
['composition', () => new api.CompositionAPI.default(this)],
|
||||
/**
|
||||
* Tracks current selection state of the application.
|
||||
* @private
|
||||
*/
|
||||
this.selection = new Selection(this);
|
||||
|
||||
/**
|
||||
* Registry for views of domain objects which should appear in the
|
||||
* main viewing area.
|
||||
*
|
||||
* @type {module:openmct.ViewRegistry}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name objectViews
|
||||
*/
|
||||
['objectViews', () => new ViewRegistry()],
|
||||
/**
|
||||
* MCT's time conductor, which may be used to synchronize view contents
|
||||
* for telemetry- or time-based views.
|
||||
* @type {module:openmct.TimeConductor}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name conductor
|
||||
*/
|
||||
this.time = new TimeAPI(this);
|
||||
|
||||
/**
|
||||
* Registry for views which should appear in the Inspector area.
|
||||
* These views will be chosen based on the selection state.
|
||||
*
|
||||
* @type {module:openmct.InspectorViewRegistry}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name inspectorViews
|
||||
*/
|
||||
['inspectorViews', () => new InspectorViewRegistry.default()],
|
||||
/**
|
||||
* An interface for interacting with the composition of domain objects.
|
||||
* The composition of a domain object is the list of other domain
|
||||
* objects it "contains" (for instance, that should be displayed
|
||||
* beneath it in the tree.)
|
||||
*
|
||||
* `composition` may be called as a function, in which case it acts
|
||||
* as [`composition.get`]{@link module:openmct.CompositionAPI#get}.
|
||||
*
|
||||
* @type {module:openmct.CompositionAPI}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name composition
|
||||
*/
|
||||
this.composition = new CompositionAPI(this);
|
||||
|
||||
/**
|
||||
* Registry for views which should appear in Edit Properties
|
||||
* dialogs, and similar user interface elements used for
|
||||
* modifying domain objects external to its regular views.
|
||||
*
|
||||
* @type {module:openmct.ViewRegistry}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name propertyEditors
|
||||
*/
|
||||
['propertyEditors', () => new ViewRegistry()],
|
||||
/**
|
||||
* Registry for views of domain objects which should appear in the
|
||||
* main viewing area.
|
||||
*
|
||||
* @type {module:openmct.ViewRegistry}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name objectViews
|
||||
*/
|
||||
this.objectViews = new ViewRegistry();
|
||||
|
||||
/**
|
||||
* Registry for views which should appear in the toolbar area while
|
||||
* editing. These views will be chosen based on the selection state.
|
||||
*
|
||||
* @type {module:openmct.ToolbarRegistry}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name toolbars
|
||||
*/
|
||||
['toolbars', () => new ToolbarRegistry()],
|
||||
/**
|
||||
* Registry for views which should appear in the Inspector area.
|
||||
* These views will be chosen based on the selection state.
|
||||
*
|
||||
* @type {module:openmct.InspectorViewRegistry}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name inspectorViews
|
||||
*/
|
||||
this.inspectorViews = new InspectorViewRegistry();
|
||||
|
||||
/**
|
||||
* Registry for domain object types which may exist within this
|
||||
* instance of Open MCT.
|
||||
*
|
||||
* @type {module:openmct.TypeRegistry}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name types
|
||||
*/
|
||||
['types', () => new api.TypeRegistry()],
|
||||
/**
|
||||
* Registry for views which should appear in Edit Properties
|
||||
* dialogs, and similar user interface elements used for
|
||||
* modifying domain objects external to its regular views.
|
||||
*
|
||||
* @type {module:openmct.ViewRegistry}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name propertyEditors
|
||||
*/
|
||||
this.propertyEditors = new ViewRegistry();
|
||||
|
||||
/**
|
||||
* An interface for interacting with domain objects and the domain
|
||||
* object hierarchy.
|
||||
*
|
||||
* @type {module:openmct.ObjectAPI}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name objects
|
||||
*/
|
||||
['objects', () => new api.ObjectAPI.default(this.types, this)],
|
||||
/**
|
||||
* Registry for views which should appear in the toolbar area while
|
||||
* editing. These views will be chosen based on the selection state.
|
||||
*
|
||||
* @type {module:openmct.ToolbarRegistry}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name toolbars
|
||||
*/
|
||||
this.toolbars = new ToolbarRegistry();
|
||||
|
||||
/**
|
||||
* An interface for retrieving and interpreting telemetry data associated
|
||||
* with a domain object.
|
||||
*
|
||||
* @type {module:openmct.TelemetryAPI}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name telemetry
|
||||
*/
|
||||
['telemetry', () => new api.TelemetryAPI.default(this)],
|
||||
/**
|
||||
* Registry for domain object types which may exist within this
|
||||
* instance of Open MCT.
|
||||
*
|
||||
* @type {module:openmct.TypeRegistry}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name types
|
||||
*/
|
||||
this.types = new TypeRegistry();
|
||||
|
||||
/**
|
||||
* An interface for creating new indicators and changing them dynamically.
|
||||
*
|
||||
* @type {module:openmct.IndicatorAPI}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name indicators
|
||||
*/
|
||||
['indicators', () => new api.IndicatorAPI(this)],
|
||||
/**
|
||||
* An interface for interacting with domain objects and the domain
|
||||
* object hierarchy.
|
||||
*
|
||||
* @type {module:openmct.ObjectAPI}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name objects
|
||||
*/
|
||||
this.objects = new ObjectAPI(this.types, this);
|
||||
|
||||
/**
|
||||
* MCT's user awareness management, to enable user and
|
||||
* role specific functionality.
|
||||
* @type {module:openmct.UserAPI}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name user
|
||||
*/
|
||||
['user', () => new api.UserAPI(this)],
|
||||
/**
|
||||
* An interface for retrieving and interpreting telemetry data associated
|
||||
* with a domain object.
|
||||
*
|
||||
* @type {module:openmct.TelemetryAPI}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name telemetry
|
||||
*/
|
||||
this.telemetry = new TelemetryAPI(this);
|
||||
|
||||
['notifications', () => new api.NotificationAPI()],
|
||||
/**
|
||||
* An interface for creating new indicators and changing them dynamically.
|
||||
*
|
||||
* @type {module:openmct.IndicatorAPI}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name indicators
|
||||
*/
|
||||
this.indicators = new IndicatorAPI(this);
|
||||
|
||||
['editor', () => new api.EditorAPI.default(this)],
|
||||
/**
|
||||
* MCT's user awareness management, to enable user and
|
||||
* role specific functionality.
|
||||
* @type {module:openmct.UserAPI}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name user
|
||||
*/
|
||||
this.user = new UserAPI(this);
|
||||
|
||||
['overlays', () => new OverlayAPI.default()],
|
||||
|
||||
['tooltips', () => new ToolTipAPI.default()],
|
||||
|
||||
['menus', () => new api.MenuAPI(this)],
|
||||
|
||||
['actions', () => new api.ActionsAPI(this)],
|
||||
|
||||
['status', () => new api.StatusAPI(this)],
|
||||
|
||||
['priority', () => api.PriorityAPI],
|
||||
|
||||
['router', () => new ApplicationRouter(this)],
|
||||
|
||||
['faults', () => new api.FaultManagementAPI.default(this)],
|
||||
|
||||
['forms', () => new api.FormsAPI.default(this)],
|
||||
|
||||
['branding', () => BrandingAPI.default],
|
||||
|
||||
/**
|
||||
* MCT's annotation API that enables
|
||||
* human-created comments and categorization linked to data products
|
||||
* @type {module:openmct.AnnotationAPI}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name annotation
|
||||
*/
|
||||
['annotation', () => new api.AnnotationAPI(this)]
|
||||
].forEach((apiEntry) => {
|
||||
const apiName = apiEntry[0];
|
||||
const apiObject = apiEntry[1]();
|
||||
|
||||
Object.defineProperty(this, apiName, {
|
||||
value: apiObject,
|
||||
enumerable: false,
|
||||
configurable: false,
|
||||
writable: true
|
||||
});
|
||||
});
|
||||
this.notifications = new NotificationAPI();
|
||||
this.editor = new EditorAPI(this);
|
||||
this.overlays = new OverlayAPI();
|
||||
this.tooltips = new ToolTipAPI();
|
||||
this.menus = new MenuAPI(this);
|
||||
this.actions = new ActionsAPI(this);
|
||||
this.status = new StatusAPI(this);
|
||||
this.priority = PriorityAPI;
|
||||
this.router = new ApplicationRouter(this);
|
||||
this.faults = new FaultManagementAPI(this);
|
||||
this.forms = new FormsAPI(this);
|
||||
this.branding = BrandingAPI;
|
||||
|
||||
/**
|
||||
* MCT's annotation API that enables
|
||||
@ -266,23 +231,23 @@ define([
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name annotation
|
||||
*/
|
||||
this.annotation = new api.AnnotationAPI(this);
|
||||
this.annotation = new AnnotationAPI(this);
|
||||
|
||||
// Plugins that are installed by default
|
||||
this.install(this.plugins.Plot());
|
||||
this.install(this.plugins.TelemetryTable.default());
|
||||
this.install(PreviewPlugin.default());
|
||||
this.install(LicensesPlugin.default());
|
||||
this.install(RemoveActionPlugin.default());
|
||||
this.install(MoveActionPlugin.default());
|
||||
this.install(LinkActionPlugin.default());
|
||||
this.install(DuplicateActionPlugin.default());
|
||||
this.install(ExportAsJSONAction.default());
|
||||
this.install(ImportFromJSONAction.default());
|
||||
this.install(this.plugins.FormActions.default());
|
||||
this.install(this.plugins.TelemetryTable());
|
||||
this.install(PreviewPlugin());
|
||||
this.install(LicensesPlugin());
|
||||
this.install(RemoveActionPlugin());
|
||||
this.install(MoveActionPlugin());
|
||||
this.install(LinkActionPlugin());
|
||||
this.install(DuplicateActionPlugin());
|
||||
this.install(ExportAsJSONAction());
|
||||
this.install(ImportFromJSONAction());
|
||||
this.install(this.plugins.FormActions());
|
||||
this.install(this.plugins.FolderView());
|
||||
this.install(this.plugins.Tabs());
|
||||
this.install(ImageryPlugin.default());
|
||||
this.install(ImageryPlugin());
|
||||
this.install(this.plugins.FlexibleLayout());
|
||||
this.install(this.plugins.GoToOriginalAction());
|
||||
this.install(this.plugins.OpenInNewTabAction());
|
||||
@ -300,26 +265,20 @@ define([
|
||||
this.install(this.plugins.Gauge());
|
||||
this.install(this.plugins.InspectorViews());
|
||||
}
|
||||
|
||||
MCT.prototype = Object.create(EventEmitter.prototype);
|
||||
|
||||
MCT.prototype.MCT = MCT;
|
||||
|
||||
/**
|
||||
* Set path to where assets are hosted. This should be the path to main.js.
|
||||
* @memberof module:openmct.MCT#
|
||||
* @method setAssetPath
|
||||
*/
|
||||
MCT.prototype.setAssetPath = function (assetPath) {
|
||||
setAssetPath(assetPath) {
|
||||
this._assetPath = assetPath;
|
||||
};
|
||||
|
||||
}
|
||||
/**
|
||||
* Get path to where assets are hosted.
|
||||
* @memberof module:openmct.MCT#
|
||||
* @method getAssetPath
|
||||
*/
|
||||
MCT.prototype.getAssetPath = function () {
|
||||
getAssetPath() {
|
||||
const assetPathLength = this._assetPath && this._assetPath.length;
|
||||
if (!assetPathLength) {
|
||||
return '/';
|
||||
@ -330,8 +289,7 @@ define([
|
||||
}
|
||||
|
||||
return this._assetPath;
|
||||
};
|
||||
|
||||
}
|
||||
/**
|
||||
* Start running Open MCT. This should be called only after any plugins
|
||||
* have been installed.
|
||||
@ -341,10 +299,7 @@ define([
|
||||
* @param {HTMLElement} [domElement] the DOM element in which to run
|
||||
* MCT; if undefined, MCT will be run in the body of the document
|
||||
*/
|
||||
MCT.prototype.start = function (
|
||||
domElement = document.body.firstElementChild,
|
||||
isHeadlessMode = false
|
||||
) {
|
||||
start(domElement = document.body.firstElementChild, isHeadlessMode = false) {
|
||||
// Create element to mount Layout if it doesn't exist
|
||||
if (domElement === null) {
|
||||
domElement = document.createElement('div');
|
||||
@ -376,20 +331,12 @@ define([
|
||||
* @event start
|
||||
* @memberof module:openmct.MCT~
|
||||
*/
|
||||
|
||||
if (!isHeadlessMode) {
|
||||
const appLayout = Vue.createApp({
|
||||
components: {
|
||||
Layout: Layout.default
|
||||
},
|
||||
provide: {
|
||||
openmct: Vue.markRaw(this)
|
||||
},
|
||||
template: '<Layout ref="layout"></Layout>'
|
||||
});
|
||||
const appLayout = createApp(Layout);
|
||||
appLayout.provide('openmct', markRaw(this));
|
||||
const component = appLayout.mount(domElement);
|
||||
component.$nextTick(() => {
|
||||
this.layout = component.$refs.layout;
|
||||
this.layout = component;
|
||||
this.app = appLayout;
|
||||
Browse(this);
|
||||
window.addEventListener('beforeunload', this.destroy);
|
||||
@ -402,14 +349,12 @@ define([
|
||||
this.router.start();
|
||||
this.emit('start');
|
||||
}
|
||||
};
|
||||
|
||||
MCT.prototype.startHeadless = function () {
|
||||
}
|
||||
startHeadless() {
|
||||
let unreachableNode = document.createElement('div');
|
||||
|
||||
return this.start(unreachableNode, true);
|
||||
};
|
||||
|
||||
}
|
||||
/**
|
||||
* Install a plugin in MCT.
|
||||
*
|
||||
@ -417,17 +362,13 @@ define([
|
||||
* invoked with the mct instance.
|
||||
* @memberof module:openmct.MCT#
|
||||
*/
|
||||
MCT.prototype.install = function (plugin) {
|
||||
install(plugin) {
|
||||
plugin(this);
|
||||
};
|
||||
}
|
||||
|
||||
MCT.prototype.destroy = function () {
|
||||
destroy() {
|
||||
window.removeEventListener('beforeunload', this.destroy);
|
||||
this.emit('destroy');
|
||||
this.router.destroy();
|
||||
};
|
||||
|
||||
MCT.prototype.plugins = plugins;
|
||||
|
||||
return MCT;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
148
src/MCTSpec.js
@ -20,96 +20,98 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(['./plugins/plugins', 'utils/testing'], function (plugins, testUtils) {
|
||||
describe('MCT', function () {
|
||||
let openmct;
|
||||
let mockPlugin;
|
||||
let mockPlugin2;
|
||||
let mockListener;
|
||||
import * as testUtils from 'utils/testing';
|
||||
|
||||
beforeEach(function () {
|
||||
mockPlugin = jasmine.createSpy('plugin');
|
||||
mockPlugin2 = jasmine.createSpy('plugin2');
|
||||
mockListener = jasmine.createSpy('listener');
|
||||
import plugins from './plugins/plugins';
|
||||
|
||||
openmct = testUtils.createOpenMct();
|
||||
describe('MCT', function () {
|
||||
let openmct;
|
||||
let mockPlugin;
|
||||
let mockPlugin2;
|
||||
let mockListener;
|
||||
|
||||
openmct.install(mockPlugin);
|
||||
openmct.install(mockPlugin2);
|
||||
openmct.on('start', mockListener);
|
||||
beforeEach(function () {
|
||||
mockPlugin = jasmine.createSpy('plugin');
|
||||
mockPlugin2 = jasmine.createSpy('plugin2');
|
||||
mockListener = jasmine.createSpy('listener');
|
||||
|
||||
openmct = testUtils.createOpenMct();
|
||||
|
||||
openmct.install(mockPlugin);
|
||||
openmct.install(mockPlugin2);
|
||||
openmct.on('start', mockListener);
|
||||
});
|
||||
|
||||
// Clean up the dirty singleton.
|
||||
afterEach(function () {
|
||||
return testUtils.resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
it('exposes plugins', function () {
|
||||
expect(openmct.plugins).toEqual(plugins);
|
||||
});
|
||||
|
||||
it('does not issue a start event before started', function () {
|
||||
expect(mockListener).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('start', function () {
|
||||
let appHolder;
|
||||
beforeEach(function (done) {
|
||||
appHolder = document.createElement('div');
|
||||
openmct.on('start', done);
|
||||
openmct.start(appHolder);
|
||||
});
|
||||
|
||||
// Clean up the dirty singleton.
|
||||
afterEach(function () {
|
||||
return testUtils.resetApplicationState(openmct);
|
||||
it('calls plugins for configuration', function () {
|
||||
expect(mockPlugin).toHaveBeenCalledWith(openmct);
|
||||
expect(mockPlugin2).toHaveBeenCalledWith(openmct);
|
||||
});
|
||||
|
||||
it('exposes plugins', function () {
|
||||
expect(openmct.plugins).toEqual(plugins);
|
||||
it('emits a start event', function () {
|
||||
expect(mockListener).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not issue a start event before started', function () {
|
||||
expect(mockListener).not.toHaveBeenCalled();
|
||||
it('Renders the application into the provided container element', function () {
|
||||
let openMctShellElements = appHolder.querySelectorAll('div.l-shell');
|
||||
expect(openMctShellElements.length).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('startHeadless', function () {
|
||||
beforeEach(function (done) {
|
||||
openmct.on('start', done);
|
||||
openmct.startHeadless();
|
||||
});
|
||||
|
||||
describe('start', function () {
|
||||
let appHolder;
|
||||
beforeEach(function (done) {
|
||||
appHolder = document.createElement('div');
|
||||
openmct.on('start', done);
|
||||
openmct.start(appHolder);
|
||||
});
|
||||
|
||||
it('calls plugins for configuration', function () {
|
||||
expect(mockPlugin).toHaveBeenCalledWith(openmct);
|
||||
expect(mockPlugin2).toHaveBeenCalledWith(openmct);
|
||||
});
|
||||
|
||||
it('emits a start event', function () {
|
||||
expect(mockListener).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('Renders the application into the provided container element', function () {
|
||||
let openMctShellElements = appHolder.querySelectorAll('div.l-shell');
|
||||
expect(openMctShellElements.length).toBe(1);
|
||||
});
|
||||
it('calls plugins for configuration', function () {
|
||||
expect(mockPlugin).toHaveBeenCalledWith(openmct);
|
||||
expect(mockPlugin2).toHaveBeenCalledWith(openmct);
|
||||
});
|
||||
|
||||
describe('startHeadless', function () {
|
||||
beforeEach(function (done) {
|
||||
openmct.on('start', done);
|
||||
openmct.startHeadless();
|
||||
});
|
||||
|
||||
it('calls plugins for configuration', function () {
|
||||
expect(mockPlugin).toHaveBeenCalledWith(openmct);
|
||||
expect(mockPlugin2).toHaveBeenCalledWith(openmct);
|
||||
});
|
||||
|
||||
it('emits a start event', function () {
|
||||
expect(mockListener).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('Does not render Open MCT', function () {
|
||||
let openMctShellElements = document.body.querySelectorAll('div.l-shell');
|
||||
expect(openMctShellElements.length).toBe(0);
|
||||
});
|
||||
it('emits a start event', function () {
|
||||
expect(mockListener).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('setAssetPath', function () {
|
||||
let testAssetPath;
|
||||
it('Does not render Open MCT', function () {
|
||||
let openMctShellElements = document.body.querySelectorAll('div.l-shell');
|
||||
expect(openMctShellElements.length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
it('configures the path for assets', function () {
|
||||
testAssetPath = 'some/path/';
|
||||
openmct.setAssetPath(testAssetPath);
|
||||
expect(openmct.getAssetPath()).toBe(testAssetPath);
|
||||
});
|
||||
describe('setAssetPath', function () {
|
||||
let testAssetPath;
|
||||
|
||||
it('adds a trailing /', function () {
|
||||
testAssetPath = 'some/path';
|
||||
openmct.setAssetPath(testAssetPath);
|
||||
expect(openmct.getAssetPath()).toBe(testAssetPath + '/');
|
||||
});
|
||||
it('configures the path for assets', function () {
|
||||
testAssetPath = 'some/path/';
|
||||
openmct.setAssetPath(testAssetPath);
|
||||
expect(openmct.getAssetPath()).toBe(testAssetPath);
|
||||
});
|
||||
|
||||
it('adds a trailing /', function () {
|
||||
testAssetPath = 'some/path';
|
||||
openmct.setAssetPath(testAssetPath);
|
||||
expect(openmct.getAssetPath()).toBe(testAssetPath + '/');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -20,24 +20,24 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define([
|
||||
'./actions/ActionsAPI',
|
||||
'./composition/CompositionAPI',
|
||||
'./Editor',
|
||||
'./faultmanagement/FaultManagementAPI',
|
||||
'./forms/FormsAPI',
|
||||
'./indicators/IndicatorAPI',
|
||||
'./menu/MenuAPI',
|
||||
'./notifications/NotificationAPI',
|
||||
'./objects/ObjectAPI',
|
||||
'./priority/PriorityAPI',
|
||||
'./status/StatusAPI',
|
||||
'./telemetry/TelemetryAPI',
|
||||
'./time/TimeAPI',
|
||||
'./types/TypeRegistry',
|
||||
'./user/UserAPI',
|
||||
'./annotation/AnnotationAPI'
|
||||
], function (
|
||||
import ActionsAPI from './actions/ActionsAPI';
|
||||
import AnnotationAPI from './annotation/AnnotationAPI';
|
||||
import CompositionAPI from './composition/CompositionAPI';
|
||||
import EditorAPI from './Editor';
|
||||
import FaultManagementAPI from './faultmanagement/FaultManagementAPI';
|
||||
import FormsAPI from './forms/FormsAPI';
|
||||
import IndicatorAPI from './indicators/IndicatorAPI';
|
||||
import MenuAPI from './menu/MenuAPI';
|
||||
import NotificationAPI from './notifications/NotificationAPI';
|
||||
import ObjectAPI from './objects/ObjectAPI';
|
||||
import PriorityAPI from './priority/PriorityAPI';
|
||||
import StatusAPI from './status/StatusAPI';
|
||||
import TelemetryAPI from './telemetry/TelemetryAPI';
|
||||
import TimeAPI from './time/TimeAPI';
|
||||
import TypeRegistry from './types/TypeRegistry';
|
||||
import UserAPI from './user/UserAPI';
|
||||
|
||||
export default {
|
||||
ActionsAPI,
|
||||
CompositionAPI,
|
||||
EditorAPI,
|
||||
@ -54,23 +54,4 @@ define([
|
||||
TypeRegistry,
|
||||
UserAPI,
|
||||
AnnotationAPI
|
||||
) {
|
||||
return {
|
||||
ActionsAPI: ActionsAPI.default,
|
||||
CompositionAPI: CompositionAPI,
|
||||
EditorAPI: EditorAPI,
|
||||
FaultManagementAPI: FaultManagementAPI,
|
||||
FormsAPI: FormsAPI,
|
||||
IndicatorAPI: IndicatorAPI.default,
|
||||
MenuAPI: MenuAPI.default,
|
||||
NotificationAPI: NotificationAPI.default,
|
||||
ObjectAPI: ObjectAPI,
|
||||
PriorityAPI: PriorityAPI.default,
|
||||
StatusAPI: StatusAPI.default,
|
||||
TelemetryAPI: TelemetryAPI,
|
||||
TimeAPI: TimeAPI.default,
|
||||
TypeRegistry: TypeRegistry.default,
|
||||
UserAPI: UserAPI.default,
|
||||
AnnotationAPI: AnnotationAPI.default
|
||||
};
|
||||
});
|
||||
};
|
||||
|
@ -32,10 +32,11 @@
|
||||
<div
|
||||
v-for="section in formSections"
|
||||
:key="section.id"
|
||||
:aria-labelledby="'sectionTitle'"
|
||||
class="c-form__section"
|
||||
:class="section.cssClass"
|
||||
>
|
||||
<h2 v-if="section.name" class="c-form__section-header">
|
||||
<h2 v-if="section.name" :id="'sectionTitle' + section.id" class="c-form__section-header">
|
||||
{{ section.name }}
|
||||
</h2>
|
||||
<FormRow
|
||||
|
@ -20,163 +20,161 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define([], function () {
|
||||
/**
|
||||
* Utility for checking if a thing is an Open MCT Identifier.
|
||||
* @private
|
||||
*/
|
||||
function isIdentifier(thing) {
|
||||
return (
|
||||
typeof thing === 'object' &&
|
||||
Object.prototype.hasOwnProperty.call(thing, 'key') &&
|
||||
Object.prototype.hasOwnProperty.call(thing, 'namespace')
|
||||
);
|
||||
/**
|
||||
* Utility for checking if a thing is an Open MCT Identifier.
|
||||
* @private
|
||||
*/
|
||||
function isIdentifier(thing) {
|
||||
return (
|
||||
typeof thing === 'object' &&
|
||||
Object.prototype.hasOwnProperty.call(thing, 'key') &&
|
||||
Object.prototype.hasOwnProperty.call(thing, 'namespace')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility for checking if a thing is a key string. Not perfect.
|
||||
* @private
|
||||
*/
|
||||
function isKeyString(thing) {
|
||||
return typeof thing === 'string';
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a keyString into an Open MCT Identifier, ex:
|
||||
* 'scratch:root' ==> {namespace: 'scratch', key: 'root'}
|
||||
*
|
||||
* Idempotent.
|
||||
*
|
||||
* @param keyString
|
||||
* @returns identifier
|
||||
*/
|
||||
function parseKeyString(keyString) {
|
||||
if (isIdentifier(keyString)) {
|
||||
return keyString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility for checking if a thing is a key string. Not perfect.
|
||||
* @private
|
||||
*/
|
||||
function isKeyString(thing) {
|
||||
return typeof thing === 'string';
|
||||
let namespace = '';
|
||||
let key = keyString;
|
||||
for (let i = 0; i < key.length; i++) {
|
||||
if (key[i] === '\\' && key[i + 1] === ':') {
|
||||
i++; // skip escape character.
|
||||
} else if (key[i] === ':') {
|
||||
key = key.slice(i + 1);
|
||||
break;
|
||||
}
|
||||
|
||||
namespace += key[i];
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a keyString into an Open MCT Identifier, ex:
|
||||
* 'scratch:root' ==> {namespace: 'scratch', key: 'root'}
|
||||
*
|
||||
* Idempotent.
|
||||
*
|
||||
* @param keyString
|
||||
* @returns identifier
|
||||
*/
|
||||
function parseKeyString(keyString) {
|
||||
if (isIdentifier(keyString)) {
|
||||
return keyString;
|
||||
}
|
||||
|
||||
let namespace = '';
|
||||
let key = keyString;
|
||||
for (let i = 0; i < key.length; i++) {
|
||||
if (key[i] === '\\' && key[i + 1] === ':') {
|
||||
i++; // skip escape character.
|
||||
} else if (key[i] === ':') {
|
||||
key = key.slice(i + 1);
|
||||
break;
|
||||
}
|
||||
|
||||
namespace += key[i];
|
||||
}
|
||||
|
||||
if (keyString === namespace) {
|
||||
namespace = '';
|
||||
}
|
||||
|
||||
return {
|
||||
namespace: namespace,
|
||||
key: key
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an Open MCT Identifier into a keyString, ex:
|
||||
* {namespace: 'scratch', key: 'root'} ==> 'scratch:root'
|
||||
*
|
||||
* Idempotent
|
||||
*
|
||||
* @param identifier
|
||||
* @returns keyString
|
||||
*/
|
||||
function makeKeyString(identifier) {
|
||||
if (!identifier) {
|
||||
throw new Error('Cannot make key string from null identifier');
|
||||
}
|
||||
|
||||
if (isKeyString(identifier)) {
|
||||
return identifier;
|
||||
}
|
||||
|
||||
if (!identifier.namespace) {
|
||||
return identifier.key;
|
||||
}
|
||||
|
||||
return [identifier.namespace.replace(/:/g, '\\:'), identifier.key].join(':');
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a new domain object into an old format model, removing the
|
||||
* identifier and converting the composition array from Open MCT Identifiers
|
||||
* to old format keyStrings.
|
||||
*
|
||||
* @param domainObject
|
||||
* @returns oldFormatModel
|
||||
*/
|
||||
function toOldFormat(model) {
|
||||
model = JSON.parse(JSON.stringify(model));
|
||||
delete model.identifier;
|
||||
if (model.composition) {
|
||||
model.composition = model.composition.map(makeKeyString);
|
||||
}
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an old format domain object model into a new format domain
|
||||
* object. Adds an identifier using the provided keyString, and converts
|
||||
* the composition array to utilize Open MCT Identifiers.
|
||||
*
|
||||
* @param model
|
||||
* @param keyString
|
||||
* @returns domainObject
|
||||
*/
|
||||
function toNewFormat(model, keyString) {
|
||||
model = JSON.parse(JSON.stringify(model));
|
||||
model.identifier = parseKeyString(keyString);
|
||||
if (model.composition) {
|
||||
model.composition = model.composition.map(parseKeyString);
|
||||
}
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two Open MCT Identifiers, returning true if they are equal.
|
||||
*
|
||||
* @param identifier
|
||||
* @param otherIdentifier
|
||||
* @returns Boolean true if identifiers are equal.
|
||||
*/
|
||||
function identifierEquals(a, b) {
|
||||
return a.key === b.key && a.namespace === b.namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two domain objects, return true if they're the same object.
|
||||
* Equality is determined by identifier.
|
||||
*
|
||||
* @param domainObject
|
||||
* @param otherDomainOBject
|
||||
* @returns Boolean true if objects are equal.
|
||||
*/
|
||||
function objectEquals(a, b) {
|
||||
return identifierEquals(a.identifier, b.identifier);
|
||||
}
|
||||
|
||||
function refresh(oldObject, newObject) {
|
||||
let deleted = _.difference(Object.keys(oldObject), Object.keys(newObject));
|
||||
deleted.forEach((propertyName) => delete oldObject[propertyName]);
|
||||
Object.assign(oldObject, newObject);
|
||||
if (keyString === namespace) {
|
||||
namespace = '';
|
||||
}
|
||||
|
||||
return {
|
||||
isIdentifier: isIdentifier,
|
||||
toOldFormat: toOldFormat,
|
||||
toNewFormat: toNewFormat,
|
||||
makeKeyString: makeKeyString,
|
||||
parseKeyString: parseKeyString,
|
||||
equals: objectEquals,
|
||||
identifierEquals: identifierEquals,
|
||||
refresh: refresh
|
||||
namespace: namespace,
|
||||
key: key
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an Open MCT Identifier into a keyString, ex:
|
||||
* {namespace: 'scratch', key: 'root'} ==> 'scratch:root'
|
||||
*
|
||||
* Idempotent
|
||||
*
|
||||
* @param identifier
|
||||
* @returns keyString
|
||||
*/
|
||||
function makeKeyString(identifier) {
|
||||
if (!identifier) {
|
||||
throw new Error('Cannot make key string from null identifier');
|
||||
}
|
||||
|
||||
if (isKeyString(identifier)) {
|
||||
return identifier;
|
||||
}
|
||||
|
||||
if (!identifier.namespace) {
|
||||
return identifier.key;
|
||||
}
|
||||
|
||||
return [identifier.namespace.replace(/:/g, '\\:'), identifier.key].join(':');
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a new domain object into an old format model, removing the
|
||||
* identifier and converting the composition array from Open MCT Identifiers
|
||||
* to old format keyStrings.
|
||||
*
|
||||
* @param domainObject
|
||||
* @returns oldFormatModel
|
||||
*/
|
||||
function toOldFormat(model) {
|
||||
model = JSON.parse(JSON.stringify(model));
|
||||
delete model.identifier;
|
||||
if (model.composition) {
|
||||
model.composition = model.composition.map(makeKeyString);
|
||||
}
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an old format domain object model into a new format domain
|
||||
* object. Adds an identifier using the provided keyString, and converts
|
||||
* the composition array to utilize Open MCT Identifiers.
|
||||
*
|
||||
* @param model
|
||||
* @param keyString
|
||||
* @returns domainObject
|
||||
*/
|
||||
function toNewFormat(model, keyString) {
|
||||
model = JSON.parse(JSON.stringify(model));
|
||||
model.identifier = parseKeyString(keyString);
|
||||
if (model.composition) {
|
||||
model.composition = model.composition.map(parseKeyString);
|
||||
}
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two Open MCT Identifiers, returning true if they are equal.
|
||||
*
|
||||
* @param identifier
|
||||
* @param otherIdentifier
|
||||
* @returns Boolean true if identifiers are equal.
|
||||
*/
|
||||
function identifierEquals(a, b) {
|
||||
return a.key === b.key && a.namespace === b.namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two domain objects, return true if they're the same object.
|
||||
* Equality is determined by identifier.
|
||||
*
|
||||
* @param domainObject
|
||||
* @param otherDomainOBject
|
||||
* @returns Boolean true if objects are equal.
|
||||
*/
|
||||
function objectEquals(a, b) {
|
||||
return identifierEquals(a.identifier, b.identifier);
|
||||
}
|
||||
|
||||
function refresh(oldObject, newObject) {
|
||||
let deleted = _.difference(Object.keys(oldObject), Object.keys(newObject));
|
||||
deleted.forEach((propertyName) => delete oldObject[propertyName]);
|
||||
Object.assign(oldObject, newObject);
|
||||
}
|
||||
|
||||
export default {
|
||||
isIdentifier: isIdentifier,
|
||||
toOldFormat: toOldFormat,
|
||||
toNewFormat: toNewFormat,
|
||||
makeKeyString: makeKeyString,
|
||||
parseKeyString: parseKeyString,
|
||||
equals: objectEquals,
|
||||
identifierEquals: identifierEquals,
|
||||
refresh: refresh
|
||||
};
|
||||
|
@ -1,89 +1,127 @@
|
||||
define(['objectUtils'], function (objectUtils) {
|
||||
describe('objectUtils', function () {
|
||||
describe('keyString util', function () {
|
||||
const EXPECTATIONS = {
|
||||
ROOT: {
|
||||
import objectUtils from 'objectUtils';
|
||||
|
||||
describe('objectUtils', function () {
|
||||
describe('keyString util', function () {
|
||||
const EXPECTATIONS = {
|
||||
ROOT: {
|
||||
namespace: '',
|
||||
key: 'ROOT'
|
||||
},
|
||||
mine: {
|
||||
namespace: '',
|
||||
key: 'mine'
|
||||
},
|
||||
'extended:something:with:colons': {
|
||||
key: 'something:with:colons',
|
||||
namespace: 'extended'
|
||||
},
|
||||
'https\\://some/url:resourceId': {
|
||||
key: 'resourceId',
|
||||
namespace: 'https://some/url'
|
||||
},
|
||||
'scratch:root': {
|
||||
namespace: 'scratch',
|
||||
key: 'root'
|
||||
},
|
||||
'thingy\\:thing:abc123': {
|
||||
namespace: 'thingy:thing',
|
||||
key: 'abc123'
|
||||
}
|
||||
};
|
||||
|
||||
Object.keys(EXPECTATIONS).forEach(function (keyString) {
|
||||
it('parses "' + keyString + '".', function () {
|
||||
expect(objectUtils.parseKeyString(keyString)).toEqual(EXPECTATIONS[keyString]);
|
||||
});
|
||||
|
||||
it('parses and re-encodes "' + keyString + '"', function () {
|
||||
const identifier = objectUtils.parseKeyString(keyString);
|
||||
expect(objectUtils.makeKeyString(identifier)).toEqual(keyString);
|
||||
});
|
||||
|
||||
it('is idempotent for "' + keyString + '".', function () {
|
||||
const identifier = objectUtils.parseKeyString(keyString);
|
||||
let again = objectUtils.parseKeyString(identifier);
|
||||
expect(identifier).toEqual(again);
|
||||
again = objectUtils.parseKeyString(again);
|
||||
again = objectUtils.parseKeyString(again);
|
||||
expect(identifier).toEqual(again);
|
||||
|
||||
let againKeyString = objectUtils.makeKeyString(again);
|
||||
expect(againKeyString).toEqual(keyString);
|
||||
againKeyString = objectUtils.makeKeyString(againKeyString);
|
||||
againKeyString = objectUtils.makeKeyString(againKeyString);
|
||||
againKeyString = objectUtils.makeKeyString(againKeyString);
|
||||
expect(againKeyString).toEqual(keyString);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('old object conversions', function () {
|
||||
it('translate ids', function () {
|
||||
expect(
|
||||
objectUtils.toNewFormat(
|
||||
{
|
||||
prop: 'someValue'
|
||||
},
|
||||
'objId'
|
||||
)
|
||||
).toEqual({
|
||||
prop: 'someValue',
|
||||
identifier: {
|
||||
namespace: '',
|
||||
key: 'ROOT'
|
||||
},
|
||||
mine: {
|
||||
namespace: '',
|
||||
key: 'mine'
|
||||
},
|
||||
'extended:something:with:colons': {
|
||||
key: 'something:with:colons',
|
||||
namespace: 'extended'
|
||||
},
|
||||
'https\\://some/url:resourceId': {
|
||||
key: 'resourceId',
|
||||
namespace: 'https://some/url'
|
||||
},
|
||||
'scratch:root': {
|
||||
namespace: 'scratch',
|
||||
key: 'root'
|
||||
},
|
||||
'thingy\\:thing:abc123': {
|
||||
namespace: 'thingy:thing',
|
||||
key: 'abc123'
|
||||
key: 'objId'
|
||||
}
|
||||
};
|
||||
|
||||
Object.keys(EXPECTATIONS).forEach(function (keyString) {
|
||||
it('parses "' + keyString + '".', function () {
|
||||
expect(objectUtils.parseKeyString(keyString)).toEqual(EXPECTATIONS[keyString]);
|
||||
});
|
||||
|
||||
it('parses and re-encodes "' + keyString + '"', function () {
|
||||
const identifier = objectUtils.parseKeyString(keyString);
|
||||
expect(objectUtils.makeKeyString(identifier)).toEqual(keyString);
|
||||
});
|
||||
|
||||
it('is idempotent for "' + keyString + '".', function () {
|
||||
const identifier = objectUtils.parseKeyString(keyString);
|
||||
let again = objectUtils.parseKeyString(identifier);
|
||||
expect(identifier).toEqual(again);
|
||||
again = objectUtils.parseKeyString(again);
|
||||
again = objectUtils.parseKeyString(again);
|
||||
expect(identifier).toEqual(again);
|
||||
|
||||
let againKeyString = objectUtils.makeKeyString(again);
|
||||
expect(againKeyString).toEqual(keyString);
|
||||
againKeyString = objectUtils.makeKeyString(againKeyString);
|
||||
againKeyString = objectUtils.makeKeyString(againKeyString);
|
||||
againKeyString = objectUtils.makeKeyString(againKeyString);
|
||||
expect(againKeyString).toEqual(keyString);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('old object conversions', function () {
|
||||
it('translate ids', function () {
|
||||
expect(
|
||||
objectUtils.toNewFormat(
|
||||
{
|
||||
prop: 'someValue'
|
||||
},
|
||||
'objId'
|
||||
)
|
||||
).toEqual({
|
||||
it('translates composition', function () {
|
||||
expect(
|
||||
objectUtils.toNewFormat(
|
||||
{
|
||||
prop: 'someValue',
|
||||
composition: ['anotherObjectId', 'scratch:anotherObjectId']
|
||||
},
|
||||
'objId'
|
||||
)
|
||||
).toEqual({
|
||||
prop: 'someValue',
|
||||
composition: [
|
||||
{
|
||||
namespace: '',
|
||||
key: 'anotherObjectId'
|
||||
},
|
||||
{
|
||||
namespace: 'scratch',
|
||||
key: 'anotherObjectId'
|
||||
}
|
||||
],
|
||||
identifier: {
|
||||
namespace: '',
|
||||
key: 'objId'
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('new object conversions', function () {
|
||||
it('removes ids', function () {
|
||||
expect(
|
||||
objectUtils.toOldFormat({
|
||||
prop: 'someValue',
|
||||
identifier: {
|
||||
namespace: '',
|
||||
key: 'objId'
|
||||
}
|
||||
});
|
||||
})
|
||||
).toEqual({
|
||||
prop: 'someValue'
|
||||
});
|
||||
});
|
||||
|
||||
it('translates composition', function () {
|
||||
expect(
|
||||
objectUtils.toNewFormat(
|
||||
{
|
||||
prop: 'someValue',
|
||||
composition: ['anotherObjectId', 'scratch:anotherObjectId']
|
||||
},
|
||||
'objId'
|
||||
)
|
||||
).toEqual({
|
||||
it('translates composition', function () {
|
||||
expect(
|
||||
objectUtils.toOldFormat({
|
||||
prop: 'someValue',
|
||||
composition: [
|
||||
{
|
||||
@ -99,48 +137,10 @@ define(['objectUtils'], function (objectUtils) {
|
||||
namespace: '',
|
||||
key: 'objId'
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('new object conversions', function () {
|
||||
it('removes ids', function () {
|
||||
expect(
|
||||
objectUtils.toOldFormat({
|
||||
prop: 'someValue',
|
||||
identifier: {
|
||||
namespace: '',
|
||||
key: 'objId'
|
||||
}
|
||||
})
|
||||
).toEqual({
|
||||
prop: 'someValue'
|
||||
});
|
||||
});
|
||||
|
||||
it('translates composition', function () {
|
||||
expect(
|
||||
objectUtils.toOldFormat({
|
||||
prop: 'someValue',
|
||||
composition: [
|
||||
{
|
||||
namespace: '',
|
||||
key: 'anotherObjectId'
|
||||
},
|
||||
{
|
||||
namespace: 'scratch',
|
||||
key: 'anotherObjectId'
|
||||
}
|
||||
],
|
||||
identifier: {
|
||||
namespace: '',
|
||||
key: 'objId'
|
||||
}
|
||||
})
|
||||
).toEqual({
|
||||
prop: 'someValue',
|
||||
composition: ['anotherObjectId', 'scratch:anotherObjectId']
|
||||
});
|
||||
})
|
||||
).toEqual({
|
||||
prop: 'someValue',
|
||||
composition: ['anotherObjectId', 'scratch:anotherObjectId']
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -20,17 +20,19 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(['lodash'], function (_) {
|
||||
/**
|
||||
* This is the default metadata provider; for any object with a "telemetry"
|
||||
* property, this provider will return the value of that property as the
|
||||
* telemetry metadata.
|
||||
*
|
||||
* This provider also implements legacy support for telemetry metadata
|
||||
* defined on the type. Telemetry metadata definitions on type will be
|
||||
* depreciated in the future.
|
||||
*/
|
||||
function DefaultMetadataProvider(openmct) {
|
||||
import _ from 'lodash';
|
||||
|
||||
/**
|
||||
* This is the default metadata provider; for any object with a "telemetry"
|
||||
* property, this provider will return the value of that property as the
|
||||
* telemetry metadata.
|
||||
*
|
||||
* This provider also implements legacy support for telemetry metadata
|
||||
* defined on the type. Telemetry metadata definitions on type will be
|
||||
* depreciated in the future.
|
||||
*/
|
||||
export default class DefaultMetadataProvider {
|
||||
constructor(openmct) {
|
||||
this.openmct = openmct;
|
||||
}
|
||||
|
||||
@ -38,65 +40,14 @@ define(['lodash'], function (_) {
|
||||
* Applies to any domain object with a telemetry property, or whose type
|
||||
* definition has a telemetry property.
|
||||
*/
|
||||
DefaultMetadataProvider.prototype.supportsMetadata = function (domainObject) {
|
||||
supportsMetadata(domainObject) {
|
||||
return Boolean(domainObject.telemetry) || Boolean(this.typeHasTelemetry(domainObject));
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves valueMetadata from legacy metadata.
|
||||
* @private
|
||||
*/
|
||||
function valueMetadatasFromOldFormat(metadata) {
|
||||
const valueMetadatas = [];
|
||||
|
||||
valueMetadatas.push({
|
||||
key: 'name',
|
||||
name: 'Name'
|
||||
});
|
||||
|
||||
metadata.domains.forEach(function (domain, index) {
|
||||
const valueMetadata = _.clone(domain);
|
||||
valueMetadata.hints = {
|
||||
domain: index + 1
|
||||
};
|
||||
valueMetadatas.push(valueMetadata);
|
||||
});
|
||||
|
||||
metadata.ranges.forEach(function (range, index) {
|
||||
const valueMetadata = _.clone(range);
|
||||
valueMetadata.hints = {
|
||||
range: index,
|
||||
priority: index + metadata.domains.length + 1
|
||||
};
|
||||
|
||||
if (valueMetadata.type === 'enum') {
|
||||
valueMetadata.key = 'enum';
|
||||
valueMetadata.hints.y -= 10;
|
||||
valueMetadata.hints.range -= 10;
|
||||
valueMetadata.enumerations = _.sortBy(
|
||||
valueMetadata.enumerations.map(function (e) {
|
||||
return {
|
||||
string: e.string,
|
||||
value: Number(e.value)
|
||||
};
|
||||
}),
|
||||
'e.value'
|
||||
);
|
||||
valueMetadata.values = valueMetadata.enumerations.map((e) => e.value);
|
||||
valueMetadata.max = Math.max(valueMetadata.values);
|
||||
valueMetadata.min = Math.min(valueMetadata.values);
|
||||
}
|
||||
|
||||
valueMetadatas.push(valueMetadata);
|
||||
});
|
||||
|
||||
return valueMetadatas;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns telemetry metadata for a given domain object.
|
||||
*/
|
||||
DefaultMetadataProvider.prototype.getMetadata = function (domainObject) {
|
||||
getMetadata(domainObject) {
|
||||
const metadata = domainObject.telemetry || {};
|
||||
if (this.typeHasTelemetry(domainObject)) {
|
||||
const typeMetadata = this.openmct.types.get(domainObject.type).definition.telemetry;
|
||||
@ -109,16 +60,65 @@ define(['lodash'], function (_) {
|
||||
}
|
||||
|
||||
return metadata;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
DefaultMetadataProvider.prototype.typeHasTelemetry = function (domainObject) {
|
||||
typeHasTelemetry(domainObject) {
|
||||
const type = this.openmct.types.get(domainObject.type);
|
||||
|
||||
return Boolean(type.definition.telemetry);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return DefaultMetadataProvider;
|
||||
});
|
||||
/**
|
||||
* Retrieves valueMetadata from legacy metadata.
|
||||
* @private
|
||||
*/
|
||||
function valueMetadatasFromOldFormat(metadata) {
|
||||
const valueMetadatas = [];
|
||||
|
||||
valueMetadatas.push({
|
||||
key: 'name',
|
||||
name: 'Name'
|
||||
});
|
||||
|
||||
metadata.domains.forEach(function (domain, index) {
|
||||
const valueMetadata = _.clone(domain);
|
||||
valueMetadata.hints = {
|
||||
domain: index + 1
|
||||
};
|
||||
valueMetadatas.push(valueMetadata);
|
||||
});
|
||||
|
||||
metadata.ranges.forEach(function (range, index) {
|
||||
const valueMetadata = _.clone(range);
|
||||
valueMetadata.hints = {
|
||||
range: index,
|
||||
priority: index + metadata.domains.length + 1
|
||||
};
|
||||
|
||||
if (valueMetadata.type === 'enum') {
|
||||
valueMetadata.key = 'enum';
|
||||
valueMetadata.hints.y -= 10;
|
||||
valueMetadata.hints.range -= 10;
|
||||
valueMetadata.enumerations = _.sortBy(
|
||||
valueMetadata.enumerations.map(function (e) {
|
||||
return {
|
||||
string: e.string,
|
||||
value: Number(e.value)
|
||||
};
|
||||
}),
|
||||
'e.value'
|
||||
);
|
||||
valueMetadata.values = valueMetadata.enumerations.map((e) => e.value);
|
||||
valueMetadata.max = Math.max(valueMetadata.values);
|
||||
valueMetadata.min = Math.min(valueMetadata.values);
|
||||
}
|
||||
|
||||
valueMetadatas.push(valueMetadata);
|
||||
});
|
||||
|
||||
return valueMetadatas;
|
||||
}
|
||||
|
@ -20,143 +20,141 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(['lodash'], function (_) {
|
||||
function applyReasonableDefaults(valueMetadata, index) {
|
||||
valueMetadata.source = valueMetadata.source || valueMetadata.key;
|
||||
valueMetadata.hints = valueMetadata.hints || {};
|
||||
import _ from 'lodash';
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(valueMetadata.hints, 'x')) {
|
||||
if (!Object.prototype.hasOwnProperty.call(valueMetadata.hints, 'domain')) {
|
||||
valueMetadata.hints.domain = valueMetadata.hints.x;
|
||||
}
|
||||
function applyReasonableDefaults(valueMetadata, index) {
|
||||
valueMetadata.source = valueMetadata.source || valueMetadata.key;
|
||||
valueMetadata.hints = valueMetadata.hints || {};
|
||||
|
||||
delete valueMetadata.hints.x;
|
||||
if (Object.prototype.hasOwnProperty.call(valueMetadata.hints, 'x')) {
|
||||
if (!Object.prototype.hasOwnProperty.call(valueMetadata.hints, 'domain')) {
|
||||
valueMetadata.hints.domain = valueMetadata.hints.x;
|
||||
}
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(valueMetadata.hints, 'y')) {
|
||||
if (!Object.prototype.hasOwnProperty.call(valueMetadata.hints, 'range')) {
|
||||
valueMetadata.hints.range = valueMetadata.hints.y;
|
||||
}
|
||||
|
||||
delete valueMetadata.hints.y;
|
||||
}
|
||||
|
||||
if (valueMetadata.format === 'enum') {
|
||||
if (!valueMetadata.values) {
|
||||
valueMetadata.values = valueMetadata.enumerations.map((e) => e.value);
|
||||
}
|
||||
|
||||
if (!Object.prototype.hasOwnProperty.call(valueMetadata, 'max')) {
|
||||
valueMetadata.max = Math.max(valueMetadata.values) + 1;
|
||||
}
|
||||
|
||||
if (!Object.prototype.hasOwnProperty.call(valueMetadata, 'min')) {
|
||||
valueMetadata.min = Math.min(valueMetadata.values) - 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Object.prototype.hasOwnProperty.call(valueMetadata.hints, 'priority')) {
|
||||
valueMetadata.hints.priority = index;
|
||||
}
|
||||
|
||||
return valueMetadata;
|
||||
delete valueMetadata.hints.x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility class for handling and inspecting telemetry metadata. Applies
|
||||
* reasonable defaults to simplify the task of providing metadata, while
|
||||
* also providing methods for interrogating telemetry metadata.
|
||||
*/
|
||||
function TelemetryMetadataManager(metadata) {
|
||||
this.metadata = metadata;
|
||||
if (Object.prototype.hasOwnProperty.call(valueMetadata.hints, 'y')) {
|
||||
if (!Object.prototype.hasOwnProperty.call(valueMetadata.hints, 'range')) {
|
||||
valueMetadata.hints.range = valueMetadata.hints.y;
|
||||
}
|
||||
|
||||
this.valueMetadatas = this.metadata.values
|
||||
? this.metadata.values.map(applyReasonableDefaults)
|
||||
: [];
|
||||
delete valueMetadata.hints.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get value metadata for a single key.
|
||||
*/
|
||||
TelemetryMetadataManager.prototype.value = function (key) {
|
||||
return this.valueMetadatas.filter(function (metadata) {
|
||||
return metadata.key === key;
|
||||
if (valueMetadata.format === 'enum') {
|
||||
if (!valueMetadata.values) {
|
||||
valueMetadata.values = valueMetadata.enumerations.map((e) => e.value);
|
||||
}
|
||||
|
||||
if (!Object.prototype.hasOwnProperty.call(valueMetadata, 'max')) {
|
||||
valueMetadata.max = Math.max(valueMetadata.values) + 1;
|
||||
}
|
||||
|
||||
if (!Object.prototype.hasOwnProperty.call(valueMetadata, 'min')) {
|
||||
valueMetadata.min = Math.min(valueMetadata.values) - 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Object.prototype.hasOwnProperty.call(valueMetadata.hints, 'priority')) {
|
||||
valueMetadata.hints.priority = index;
|
||||
}
|
||||
|
||||
return valueMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility class for handling and inspecting telemetry metadata. Applies
|
||||
* reasonable defaults to simplify the task of providing metadata, while
|
||||
* also providing methods for interrogating telemetry metadata.
|
||||
*/
|
||||
export default function TelemetryMetadataManager(metadata) {
|
||||
this.metadata = metadata;
|
||||
|
||||
this.valueMetadatas = this.metadata.values
|
||||
? this.metadata.values.map(applyReasonableDefaults)
|
||||
: [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get value metadata for a single key.
|
||||
*/
|
||||
TelemetryMetadataManager.prototype.value = function (key) {
|
||||
return this.valueMetadatas.filter(function (metadata) {
|
||||
return metadata.key === key;
|
||||
})[0];
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns all value metadatas, sorted by priority.
|
||||
*/
|
||||
TelemetryMetadataManager.prototype.values = function () {
|
||||
return this.valuesForHints(['priority']);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get an array of valueMetadatas that possess all hints requested.
|
||||
* Array is sorted based on hint priority.
|
||||
*
|
||||
*/
|
||||
TelemetryMetadataManager.prototype.valuesForHints = function (hints) {
|
||||
function hasHint(hint) {
|
||||
// eslint-disable-next-line no-invalid-this
|
||||
return Object.prototype.hasOwnProperty.call(this.hints, hint);
|
||||
}
|
||||
|
||||
function hasHints(metadata) {
|
||||
return hints.every(hasHint, metadata);
|
||||
}
|
||||
|
||||
const matchingMetadata = this.valueMetadatas.filter(hasHints);
|
||||
let iteratees = hints.map((hint) => {
|
||||
return (metadata) => {
|
||||
return metadata.hints[hint];
|
||||
};
|
||||
});
|
||||
|
||||
return _.sortBy(matchingMetadata, ...iteratees);
|
||||
};
|
||||
|
||||
/**
|
||||
* check out of a given metadata has array values
|
||||
*/
|
||||
TelemetryMetadataManager.prototype.isArrayValue = function (metadata) {
|
||||
const regex = /\[\]$/g;
|
||||
if (!metadata.format && !metadata.formatString) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (metadata.format || metadata.formatString).match(regex) !== null;
|
||||
};
|
||||
|
||||
TelemetryMetadataManager.prototype.getFilterableValues = function () {
|
||||
return this.valueMetadatas.filter(
|
||||
(metadatum) => metadatum.filters && metadatum.filters.length > 0
|
||||
);
|
||||
};
|
||||
|
||||
TelemetryMetadataManager.prototype.getUseToUpdateInPlaceValue = function () {
|
||||
return this.valueMetadatas.find(this.isInPlaceUpdateValue);
|
||||
};
|
||||
|
||||
TelemetryMetadataManager.prototype.isInPlaceUpdateValue = function (metadatum) {
|
||||
return metadatum.useToUpdateInPlace === true;
|
||||
};
|
||||
|
||||
TelemetryMetadataManager.prototype.getDefaultDisplayValue = function () {
|
||||
let valueMetadata = this.valuesForHints(['range'])[0];
|
||||
|
||||
if (valueMetadata === undefined) {
|
||||
valueMetadata = this.values().filter((values) => {
|
||||
return !values.hints.domain;
|
||||
})[0];
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all value metadatas, sorted by priority.
|
||||
*/
|
||||
TelemetryMetadataManager.prototype.values = function () {
|
||||
return this.valuesForHints(['priority']);
|
||||
};
|
||||
if (valueMetadata === undefined) {
|
||||
valueMetadata = this.values()[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of valueMetadatas that possess all hints requested.
|
||||
* Array is sorted based on hint priority.
|
||||
*
|
||||
*/
|
||||
TelemetryMetadataManager.prototype.valuesForHints = function (hints) {
|
||||
function hasHint(hint) {
|
||||
// eslint-disable-next-line no-invalid-this
|
||||
return Object.prototype.hasOwnProperty.call(this.hints, hint);
|
||||
}
|
||||
|
||||
function hasHints(metadata) {
|
||||
return hints.every(hasHint, metadata);
|
||||
}
|
||||
|
||||
const matchingMetadata = this.valueMetadatas.filter(hasHints);
|
||||
let iteratees = hints.map((hint) => {
|
||||
return (metadata) => {
|
||||
return metadata.hints[hint];
|
||||
};
|
||||
});
|
||||
|
||||
return _.sortBy(matchingMetadata, ...iteratees);
|
||||
};
|
||||
|
||||
/**
|
||||
* check out of a given metadata has array values
|
||||
*/
|
||||
TelemetryMetadataManager.prototype.isArrayValue = function (metadata) {
|
||||
const regex = /\[\]$/g;
|
||||
if (!metadata.format && !metadata.formatString) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (metadata.format || metadata.formatString).match(regex) !== null;
|
||||
};
|
||||
|
||||
TelemetryMetadataManager.prototype.getFilterableValues = function () {
|
||||
return this.valueMetadatas.filter(
|
||||
(metadatum) => metadatum.filters && metadatum.filters.length > 0
|
||||
);
|
||||
};
|
||||
|
||||
TelemetryMetadataManager.prototype.getUseToUpdateInPlaceValue = function () {
|
||||
return this.valueMetadatas.find(this.isInPlaceUpdateValue);
|
||||
};
|
||||
|
||||
TelemetryMetadataManager.prototype.isInPlaceUpdateValue = function (metadatum) {
|
||||
return metadatum.useToUpdateInPlace === true;
|
||||
};
|
||||
|
||||
TelemetryMetadataManager.prototype.getDefaultDisplayValue = function () {
|
||||
let valueMetadata = this.valuesForHints(['range'])[0];
|
||||
|
||||
if (valueMetadata === undefined) {
|
||||
valueMetadata = this.values().filter((values) => {
|
||||
return !values.hints.domain;
|
||||
})[0];
|
||||
}
|
||||
|
||||
if (valueMetadata === undefined) {
|
||||
valueMetadata = this.values()[0];
|
||||
}
|
||||
|
||||
return valueMetadata;
|
||||
};
|
||||
|
||||
return TelemetryMetadataManager;
|
||||
});
|
||||
return valueMetadata;
|
||||
};
|
||||
|
@ -20,96 +20,92 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define([], function () {
|
||||
// Set of connection states; changing among these states will be
|
||||
// reflected in the indicator's appearance.
|
||||
// CONNECTED: Everything nominal, expect to be able to read/write.
|
||||
// DISCONNECTED: HTTP failed; maybe misconfigured, disconnected.
|
||||
// PENDING: Still trying to connect, and haven't failed yet.
|
||||
const CONNECTED = {
|
||||
statusClass: 's-status-on'
|
||||
};
|
||||
const PENDING = {
|
||||
statusClass: 's-status-warning-lo'
|
||||
};
|
||||
const DISCONNECTED = {
|
||||
statusClass: 's-status-warning-hi'
|
||||
};
|
||||
function URLIndicator(options, simpleIndicator) {
|
||||
this.bindMethods();
|
||||
this.count = 0;
|
||||
// Set of connection states; changing among these states will be
|
||||
// reflected in the indicator's appearance.
|
||||
// CONNECTED: Everything nominal, expect to be able to read/write.
|
||||
// DISCONNECTED: HTTP failed; maybe misconfigured, disconnected.
|
||||
// PENDING: Still trying to connect, and haven't failed yet.
|
||||
const CONNECTED = {
|
||||
statusClass: 's-status-on'
|
||||
};
|
||||
const PENDING = {
|
||||
statusClass: 's-status-warning-lo'
|
||||
};
|
||||
const DISCONNECTED = {
|
||||
statusClass: 's-status-warning-hi'
|
||||
};
|
||||
export default function URLIndicator(options, simpleIndicator) {
|
||||
this.bindMethods();
|
||||
this.count = 0;
|
||||
|
||||
this.indicator = simpleIndicator;
|
||||
this.setDefaultsFromOptions(options);
|
||||
this.setIndicatorToState(PENDING);
|
||||
this.indicator = simpleIndicator;
|
||||
this.setDefaultsFromOptions(options);
|
||||
this.setIndicatorToState(PENDING);
|
||||
|
||||
this.fetchUrl();
|
||||
setInterval(this.fetchUrl, this.interval);
|
||||
}
|
||||
this.fetchUrl();
|
||||
setInterval(this.fetchUrl, this.interval);
|
||||
}
|
||||
|
||||
URLIndicator.prototype.setIndicatorToState = function (state) {
|
||||
switch (state) {
|
||||
case CONNECTED: {
|
||||
this.indicator.text(this.label + ' is connected');
|
||||
this.indicator.description(
|
||||
this.label + ' is online, checking status every ' + this.interval + ' milliseconds.'
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
case PENDING: {
|
||||
this.indicator.text('Checking status of ' + this.label + ' please stand by...');
|
||||
this.indicator.description('Checking status of ' + this.label + ' please stand by...');
|
||||
break;
|
||||
}
|
||||
|
||||
case DISCONNECTED: {
|
||||
this.indicator.text(this.label + ' is offline');
|
||||
this.indicator.description(
|
||||
this.label + ' is offline, checking status every ' + this.interval + ' milliseconds'
|
||||
);
|
||||
break;
|
||||
}
|
||||
URLIndicator.prototype.setIndicatorToState = function (state) {
|
||||
switch (state) {
|
||||
case CONNECTED: {
|
||||
this.indicator.text(this.label + ' is connected');
|
||||
this.indicator.description(
|
||||
this.label + ' is online, checking status every ' + this.interval + ' milliseconds.'
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
this.indicator.statusClass(state.statusClass);
|
||||
};
|
||||
case PENDING: {
|
||||
this.indicator.text('Checking status of ' + this.label + ' please stand by...');
|
||||
this.indicator.description('Checking status of ' + this.label + ' please stand by...');
|
||||
break;
|
||||
}
|
||||
|
||||
URLIndicator.prototype.fetchUrl = function () {
|
||||
fetch(this.URLpath)
|
||||
.then((response) => {
|
||||
if (response.ok) {
|
||||
this.handleSuccess();
|
||||
} else {
|
||||
this.handleError();
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
case DISCONNECTED: {
|
||||
this.indicator.text(this.label + ' is offline');
|
||||
this.indicator.description(
|
||||
this.label + ' is offline, checking status every ' + this.interval + ' milliseconds'
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.indicator.statusClass(state.statusClass);
|
||||
};
|
||||
|
||||
URLIndicator.prototype.fetchUrl = function () {
|
||||
fetch(this.URLpath)
|
||||
.then((response) => {
|
||||
if (response.ok) {
|
||||
this.handleSuccess();
|
||||
} else {
|
||||
this.handleError();
|
||||
});
|
||||
};
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
this.handleError();
|
||||
});
|
||||
};
|
||||
|
||||
URLIndicator.prototype.handleError = function (e) {
|
||||
this.setIndicatorToState(DISCONNECTED);
|
||||
};
|
||||
URLIndicator.prototype.handleError = function (e) {
|
||||
this.setIndicatorToState(DISCONNECTED);
|
||||
};
|
||||
|
||||
URLIndicator.prototype.handleSuccess = function () {
|
||||
this.setIndicatorToState(CONNECTED);
|
||||
};
|
||||
URLIndicator.prototype.handleSuccess = function () {
|
||||
this.setIndicatorToState(CONNECTED);
|
||||
};
|
||||
|
||||
URLIndicator.prototype.setDefaultsFromOptions = function (options) {
|
||||
this.URLpath = options.url;
|
||||
this.label = options.label || options.url;
|
||||
this.interval = options.interval || 10000;
|
||||
this.indicator.iconClass(options.iconClass || 'icon-chain-links');
|
||||
};
|
||||
URLIndicator.prototype.setDefaultsFromOptions = function (options) {
|
||||
this.URLpath = options.url;
|
||||
this.label = options.label || options.url;
|
||||
this.interval = options.interval || 10000;
|
||||
this.indicator.iconClass(options.iconClass || 'icon-chain-links');
|
||||
};
|
||||
|
||||
URLIndicator.prototype.bindMethods = function () {
|
||||
this.fetchUrl = this.fetchUrl.bind(this);
|
||||
this.handleSuccess = this.handleSuccess.bind(this);
|
||||
this.handleError = this.handleError.bind(this);
|
||||
this.setIndicatorToState = this.setIndicatorToState.bind(this);
|
||||
};
|
||||
|
||||
return URLIndicator;
|
||||
});
|
||||
URLIndicator.prototype.bindMethods = function () {
|
||||
this.fetchUrl = this.fetchUrl.bind(this);
|
||||
this.handleSuccess = this.handleSuccess.bind(this);
|
||||
this.handleError = this.handleError.bind(this);
|
||||
this.setIndicatorToState = this.setIndicatorToState.bind(this);
|
||||
};
|
||||
|
@ -19,15 +19,15 @@
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
define(['./URLIndicator'], function URLIndicatorPlugin(URLIndicator) {
|
||||
return function (opts) {
|
||||
return function install(openmct) {
|
||||
const simpleIndicator = openmct.indicators.simpleIndicator();
|
||||
const urlIndicator = new URLIndicator(opts, simpleIndicator);
|
||||
import URLIndicator from './URLIndicator';
|
||||
|
||||
openmct.indicators.add(simpleIndicator);
|
||||
export default function URLIndicatorPlugin(opts) {
|
||||
return function install(openmct) {
|
||||
const simpleIndicator = openmct.indicators.simpleIndicator();
|
||||
const urlIndicator = new URLIndicator(opts, simpleIndicator);
|
||||
|
||||
return urlIndicator;
|
||||
};
|
||||
openmct.indicators.add(simpleIndicator);
|
||||
|
||||
return urlIndicator;
|
||||
};
|
||||
});
|
||||
}
|
||||
|
@ -20,118 +20,115 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(['utils/testing', './URLIndicator', './URLIndicatorPlugin', '../../MCT'], function (
|
||||
testingUtils,
|
||||
URLIndicator,
|
||||
URLIndicatorPlugin,
|
||||
MCT
|
||||
) {
|
||||
describe('The URLIndicator', function () {
|
||||
let openmct;
|
||||
let indicatorElement;
|
||||
let pluginOptions;
|
||||
let urlIndicator; // eslint-disable-line
|
||||
let fetchSpy;
|
||||
import * as testingUtils from 'utils/testing';
|
||||
|
||||
beforeEach(function () {
|
||||
jasmine.clock().install();
|
||||
openmct = new testingUtils.createOpenMct();
|
||||
spyOn(openmct.indicators, 'add');
|
||||
fetchSpy = spyOn(window, 'fetch').and.callFake(() =>
|
||||
Promise.resolve({
|
||||
ok: true
|
||||
})
|
||||
);
|
||||
});
|
||||
import URLIndicatorPlugin from './URLIndicatorPlugin';
|
||||
|
||||
afterEach(function () {
|
||||
if (window.fetch.restore) {
|
||||
window.fetch.restore();
|
||||
}
|
||||
describe('The URLIndicator', function () {
|
||||
let openmct;
|
||||
let indicatorElement;
|
||||
let pluginOptions;
|
||||
let urlIndicator; // eslint-disable-line
|
||||
let fetchSpy;
|
||||
|
||||
jasmine.clock().uninstall();
|
||||
beforeEach(function () {
|
||||
jasmine.clock().install();
|
||||
openmct = new testingUtils.createOpenMct();
|
||||
spyOn(openmct.indicators, 'add');
|
||||
fetchSpy = spyOn(window, 'fetch').and.callFake(() =>
|
||||
Promise.resolve({
|
||||
ok: true
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
return testingUtils.resetApplicationState(openmct);
|
||||
});
|
||||
afterEach(function () {
|
||||
if (window.fetch.restore) {
|
||||
window.fetch.restore();
|
||||
}
|
||||
|
||||
describe('on initialization', function () {
|
||||
describe('with default options', function () {
|
||||
beforeEach(function () {
|
||||
pluginOptions = {
|
||||
url: 'someURL'
|
||||
};
|
||||
urlIndicator = URLIndicatorPlugin(pluginOptions)(openmct);
|
||||
indicatorElement = openmct.indicators.add.calls.mostRecent().args[0].element;
|
||||
});
|
||||
jasmine.clock().uninstall();
|
||||
|
||||
it('has a default icon class if none supplied', function () {
|
||||
expect(indicatorElement.classList.contains('icon-chain-links')).toBe(true);
|
||||
});
|
||||
return testingUtils.resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
it('defaults to the URL if no label supplied', function () {
|
||||
expect(indicatorElement.textContent.indexOf(pluginOptions.url) >= 0).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with custom options', function () {
|
||||
beforeEach(function () {
|
||||
pluginOptions = {
|
||||
url: 'customURL',
|
||||
interval: 1814,
|
||||
iconClass: 'iconClass-checked',
|
||||
label: 'custom label'
|
||||
};
|
||||
urlIndicator = URLIndicatorPlugin(pluginOptions)(openmct);
|
||||
indicatorElement = openmct.indicators.add.calls.mostRecent().args[0].element;
|
||||
});
|
||||
|
||||
it('uses the custom iconClass', function () {
|
||||
expect(indicatorElement.classList.contains('iconClass-checked')).toBe(true);
|
||||
});
|
||||
it('uses custom interval', function () {
|
||||
expect(window.fetch).toHaveBeenCalledTimes(1);
|
||||
jasmine.clock().tick(1);
|
||||
expect(window.fetch).toHaveBeenCalledTimes(1);
|
||||
jasmine.clock().tick(pluginOptions.interval + 1);
|
||||
expect(window.fetch).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
it('uses custom label if supplied in initialization', function () {
|
||||
expect(indicatorElement.textContent.indexOf(pluginOptions.label) >= 0).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when running', function () {
|
||||
describe('on initialization', function () {
|
||||
describe('with default options', function () {
|
||||
beforeEach(function () {
|
||||
pluginOptions = {
|
||||
url: 'someURL',
|
||||
interval: 100
|
||||
url: 'someURL'
|
||||
};
|
||||
urlIndicator = URLIndicatorPlugin(pluginOptions)(openmct);
|
||||
indicatorElement = openmct.indicators.add.calls.mostRecent().args[0].element;
|
||||
});
|
||||
|
||||
it('requests the provided URL', function () {
|
||||
jasmine.clock().tick(pluginOptions.interval + 1);
|
||||
expect(window.fetch).toHaveBeenCalledWith(pluginOptions.url);
|
||||
it('has a default icon class if none supplied', function () {
|
||||
expect(indicatorElement.classList.contains('icon-chain-links')).toBe(true);
|
||||
});
|
||||
|
||||
it('indicates success if connection is nominal', async function () {
|
||||
jasmine.clock().tick(pluginOptions.interval + 1);
|
||||
await urlIndicator.fetchUrl();
|
||||
expect(indicatorElement.classList.contains('s-status-on')).toBe(true);
|
||||
it('defaults to the URL if no label supplied', function () {
|
||||
expect(indicatorElement.textContent.indexOf(pluginOptions.url) >= 0).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with custom options', function () {
|
||||
beforeEach(function () {
|
||||
pluginOptions = {
|
||||
url: 'customURL',
|
||||
interval: 1814,
|
||||
iconClass: 'iconClass-checked',
|
||||
label: 'custom label'
|
||||
};
|
||||
urlIndicator = URLIndicatorPlugin(pluginOptions)(openmct);
|
||||
indicatorElement = openmct.indicators.add.calls.mostRecent().args[0].element;
|
||||
});
|
||||
|
||||
it('indicates an error when the server cannot be reached', async function () {
|
||||
fetchSpy.and.callFake(() =>
|
||||
Promise.resolve({
|
||||
ok: false
|
||||
})
|
||||
);
|
||||
it('uses the custom iconClass', function () {
|
||||
expect(indicatorElement.classList.contains('iconClass-checked')).toBe(true);
|
||||
});
|
||||
it('uses custom interval', function () {
|
||||
expect(window.fetch).toHaveBeenCalledTimes(1);
|
||||
jasmine.clock().tick(1);
|
||||
expect(window.fetch).toHaveBeenCalledTimes(1);
|
||||
jasmine.clock().tick(pluginOptions.interval + 1);
|
||||
await urlIndicator.fetchUrl();
|
||||
expect(indicatorElement.classList.contains('s-status-warning-hi')).toBe(true);
|
||||
expect(window.fetch).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
it('uses custom label if supplied in initialization', function () {
|
||||
expect(indicatorElement.textContent.indexOf(pluginOptions.label) >= 0).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when running', function () {
|
||||
beforeEach(function () {
|
||||
pluginOptions = {
|
||||
url: 'someURL',
|
||||
interval: 100
|
||||
};
|
||||
urlIndicator = URLIndicatorPlugin(pluginOptions)(openmct);
|
||||
indicatorElement = openmct.indicators.add.calls.mostRecent().args[0].element;
|
||||
});
|
||||
|
||||
it('requests the provided URL', function () {
|
||||
jasmine.clock().tick(pluginOptions.interval + 1);
|
||||
expect(window.fetch).toHaveBeenCalledWith(pluginOptions.url);
|
||||
});
|
||||
|
||||
it('indicates success if connection is nominal', async function () {
|
||||
jasmine.clock().tick(pluginOptions.interval + 1);
|
||||
await urlIndicator.fetchUrl();
|
||||
expect(indicatorElement.classList.contains('s-status-on')).toBe(true);
|
||||
});
|
||||
|
||||
it('indicates an error when the server cannot be reached', async function () {
|
||||
fetchSpy.and.callFake(() =>
|
||||
Promise.resolve({
|
||||
ok: false
|
||||
})
|
||||
);
|
||||
jasmine.clock().tick(pluginOptions.interval + 1);
|
||||
await urlIndicator.fetchUrl();
|
||||
expect(indicatorElement.classList.contains('s-status-warning-hi')).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -20,15 +20,13 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define([], function () {
|
||||
/**
|
||||
* Constant values used by the Autoflow Tabular View.
|
||||
*/
|
||||
return {
|
||||
ROW_HEIGHT: 16,
|
||||
SLIDER_HEIGHT: 10,
|
||||
INITIAL_COLUMN_WIDTH: 225,
|
||||
MAX_COLUMN_WIDTH: 525,
|
||||
COLUMN_WIDTH_STEP: 25
|
||||
};
|
||||
});
|
||||
/**
|
||||
* Constant values used by the Autoflow Tabular View.
|
||||
*/
|
||||
export default {
|
||||
ROW_HEIGHT: 16,
|
||||
SLIDER_HEIGHT: 10,
|
||||
INITIAL_COLUMN_WIDTH: 225,
|
||||
MAX_COLUMN_WIDTH: 525,
|
||||
COLUMN_WIDTH_STEP: 25
|
||||
};
|
||||
|
@ -20,104 +20,102 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(['./AutoflowTabularRowController'], function (AutoflowTabularRowController) {
|
||||
/**
|
||||
* Controller for an Autoflow Tabular View. Subscribes to telemetry
|
||||
* associated with children of the domain object and passes that
|
||||
* information on to the view.
|
||||
*
|
||||
* @param {DomainObject} domainObject the object being viewed
|
||||
* @param {*} data the view data
|
||||
* @param openmct a reference to the openmct application
|
||||
*/
|
||||
function AutoflowTabularController(domainObject, data, openmct) {
|
||||
this.composition = openmct.composition.get(domainObject);
|
||||
this.data = data;
|
||||
this.openmct = openmct;
|
||||
import AutoflowTabularRowController from './AutoflowTabularRowController';
|
||||
|
||||
this.rows = {};
|
||||
this.controllers = {};
|
||||
/**
|
||||
* Controller for an Autoflow Tabular View. Subscribes to telemetry
|
||||
* associated with children of the domain object and passes that
|
||||
* information on to the view.
|
||||
*
|
||||
* @param {DomainObject} domainObject the object being viewed
|
||||
* @param {*} data the view data
|
||||
* @param openmct a reference to the openmct application
|
||||
*/
|
||||
export default function AutoflowTabularController(domainObject, data, openmct) {
|
||||
this.composition = openmct.composition.get(domainObject);
|
||||
this.data = data;
|
||||
this.openmct = openmct;
|
||||
|
||||
this.addRow = this.addRow.bind(this);
|
||||
this.removeRow = this.removeRow.bind(this);
|
||||
this.rows = {};
|
||||
this.controllers = {};
|
||||
|
||||
this.addRow = this.addRow.bind(this);
|
||||
this.removeRow = this.removeRow.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the "Last Updated" value to be displayed.
|
||||
* @param {String} value the value to display
|
||||
* @private
|
||||
*/
|
||||
AutoflowTabularController.prototype.trackLastUpdated = function (value) {
|
||||
this.data.updated = value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Respond to an `add` event from composition by adding a new row.
|
||||
* @private
|
||||
*/
|
||||
AutoflowTabularController.prototype.addRow = function (childObject) {
|
||||
const identifier = childObject.identifier;
|
||||
const id = [identifier.namespace, identifier.key].join(':');
|
||||
|
||||
if (!this.rows[id]) {
|
||||
this.rows[id] = {
|
||||
classes: '',
|
||||
name: childObject.name,
|
||||
value: undefined
|
||||
};
|
||||
this.controllers[id] = new AutoflowTabularRowController(
|
||||
childObject,
|
||||
this.rows[id],
|
||||
this.openmct,
|
||||
this.trackLastUpdated.bind(this)
|
||||
);
|
||||
this.controllers[id].activate();
|
||||
this.data.items.push(this.rows[id]);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the "Last Updated" value to be displayed.
|
||||
* @param {String} value the value to display
|
||||
* @private
|
||||
*/
|
||||
AutoflowTabularController.prototype.trackLastUpdated = function (value) {
|
||||
this.data.updated = value;
|
||||
};
|
||||
/**
|
||||
* Respond to an `remove` event from composition by removing any
|
||||
* related row.
|
||||
* @private
|
||||
*/
|
||||
AutoflowTabularController.prototype.removeRow = function (identifier) {
|
||||
const id = [identifier.namespace, identifier.key].join(':');
|
||||
|
||||
/**
|
||||
* Respond to an `add` event from composition by adding a new row.
|
||||
* @private
|
||||
*/
|
||||
AutoflowTabularController.prototype.addRow = function (childObject) {
|
||||
const identifier = childObject.identifier;
|
||||
const id = [identifier.namespace, identifier.key].join(':');
|
||||
|
||||
if (!this.rows[id]) {
|
||||
this.rows[id] = {
|
||||
classes: '',
|
||||
name: childObject.name,
|
||||
value: undefined
|
||||
};
|
||||
this.controllers[id] = new AutoflowTabularRowController(
|
||||
childObject,
|
||||
this.rows[id],
|
||||
this.openmct,
|
||||
this.trackLastUpdated.bind(this)
|
||||
);
|
||||
this.controllers[id].activate();
|
||||
this.data.items.push(this.rows[id]);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Respond to an `remove` event from composition by removing any
|
||||
* related row.
|
||||
* @private
|
||||
*/
|
||||
AutoflowTabularController.prototype.removeRow = function (identifier) {
|
||||
const id = [identifier.namespace, identifier.key].join(':');
|
||||
|
||||
if (this.rows[id]) {
|
||||
this.data.items = this.data.items.filter(
|
||||
function (item) {
|
||||
return item !== this.rows[id];
|
||||
}.bind(this)
|
||||
);
|
||||
this.controllers[id].destroy();
|
||||
delete this.controllers[id];
|
||||
delete this.rows[id];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Activate this controller; begin listening for changes.
|
||||
*/
|
||||
AutoflowTabularController.prototype.activate = function () {
|
||||
this.composition.on('add', this.addRow);
|
||||
this.composition.on('remove', this.removeRow);
|
||||
this.composition.load();
|
||||
};
|
||||
|
||||
/**
|
||||
* Destroy this controller; detach any associated resources.
|
||||
*/
|
||||
AutoflowTabularController.prototype.destroy = function () {
|
||||
Object.keys(this.controllers).forEach(
|
||||
function (id) {
|
||||
this.controllers[id].destroy();
|
||||
if (this.rows[id]) {
|
||||
this.data.items = this.data.items.filter(
|
||||
function (item) {
|
||||
return item !== this.rows[id];
|
||||
}.bind(this)
|
||||
);
|
||||
this.controllers = {};
|
||||
this.composition.off('add', this.addRow);
|
||||
this.composition.off('remove', this.removeRow);
|
||||
};
|
||||
this.controllers[id].destroy();
|
||||
delete this.controllers[id];
|
||||
delete this.rows[id];
|
||||
}
|
||||
};
|
||||
|
||||
return AutoflowTabularController;
|
||||
});
|
||||
/**
|
||||
* Activate this controller; begin listening for changes.
|
||||
*/
|
||||
AutoflowTabularController.prototype.activate = function () {
|
||||
this.composition.on('add', this.addRow);
|
||||
this.composition.on('remove', this.removeRow);
|
||||
this.composition.load();
|
||||
};
|
||||
|
||||
/**
|
||||
* Destroy this controller; detach any associated resources.
|
||||
*/
|
||||
AutoflowTabularController.prototype.destroy = function () {
|
||||
Object.keys(this.controllers).forEach(
|
||||
function (id) {
|
||||
this.controllers[id].destroy();
|
||||
}.bind(this)
|
||||
);
|
||||
this.controllers = {};
|
||||
this.composition.off('add', this.addRow);
|
||||
this.composition.off('remove', this.removeRow);
|
||||
};
|
||||
|
@ -20,23 +20,23 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(['./AutoflowTabularView'], function (AutoflowTabularView) {
|
||||
return function (options) {
|
||||
return function (openmct) {
|
||||
const views = openmct.mainViews || openmct.objectViews;
|
||||
import AutoflowTabularView from './AutoflowTabularView';
|
||||
|
||||
views.addProvider({
|
||||
name: 'Autoflow Tabular',
|
||||
key: 'autoflow',
|
||||
cssClass: 'icon-packet',
|
||||
description: 'A tabular view of packet contents.',
|
||||
canView: function (d) {
|
||||
return !options || options.type === d.type;
|
||||
},
|
||||
view: function (domainObject) {
|
||||
return new AutoflowTabularView(domainObject, openmct, document);
|
||||
}
|
||||
});
|
||||
};
|
||||
export default function (options) {
|
||||
return function (openmct) {
|
||||
const views = openmct.mainViews || openmct.objectViews;
|
||||
|
||||
views.addProvider({
|
||||
name: 'Autoflow Tabular',
|
||||
key: 'autoflow',
|
||||
cssClass: 'icon-packet',
|
||||
description: 'A tabular view of packet contents.',
|
||||
canView: function (d) {
|
||||
return !options || options.type === d.type;
|
||||
},
|
||||
view: function (domainObject) {
|
||||
return new AutoflowTabularView(domainObject, openmct, document);
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
}
|
||||
|
@ -20,76 +20,72 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define([], function () {
|
||||
/**
|
||||
* Controller for individual rows of an Autoflow Tabular View.
|
||||
* Subscribes to telemetry and updates row data.
|
||||
*
|
||||
* @param {DomainObject} domainObject the object being viewed
|
||||
* @param {*} data the view data
|
||||
* @param openmct a reference to the openmct application
|
||||
* @param {Function} callback a callback to invoke with "last updated" timestamps
|
||||
*/
|
||||
function AutoflowTabularRowController(domainObject, data, openmct, callback) {
|
||||
this.domainObject = domainObject;
|
||||
this.data = data;
|
||||
this.openmct = openmct;
|
||||
this.callback = callback;
|
||||
/**
|
||||
* Controller for individual rows of an Autoflow Tabular View.
|
||||
* Subscribes to telemetry and updates row data.
|
||||
*
|
||||
* @param {DomainObject} domainObject the object being viewed
|
||||
* @param {*} data the view data
|
||||
* @param openmct a reference to the openmct application
|
||||
* @param {Function} callback a callback to invoke with "last updated" timestamps
|
||||
*/
|
||||
export default function AutoflowTabularRowController(domainObject, data, openmct, callback) {
|
||||
this.domainObject = domainObject;
|
||||
this.data = data;
|
||||
this.openmct = openmct;
|
||||
this.callback = callback;
|
||||
|
||||
this.metadata = this.openmct.telemetry.getMetadata(this.domainObject);
|
||||
this.ranges = this.metadata.valuesForHints(['range']);
|
||||
this.domains = this.metadata.valuesForHints(['domain']);
|
||||
this.rangeFormatter = this.openmct.telemetry.getValueFormatter(this.ranges[0]);
|
||||
this.domainFormatter = this.openmct.telemetry.getValueFormatter(this.domains[0]);
|
||||
this.evaluator = this.openmct.telemetry.limitEvaluator(this.domainObject);
|
||||
this.metadata = this.openmct.telemetry.getMetadata(this.domainObject);
|
||||
this.ranges = this.metadata.valuesForHints(['range']);
|
||||
this.domains = this.metadata.valuesForHints(['domain']);
|
||||
this.rangeFormatter = this.openmct.telemetry.getValueFormatter(this.ranges[0]);
|
||||
this.domainFormatter = this.openmct.telemetry.getValueFormatter(this.domains[0]);
|
||||
this.evaluator = this.openmct.telemetry.limitEvaluator(this.domainObject);
|
||||
|
||||
this.initialized = false;
|
||||
this.initialized = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update row to reflect incoming telemetry data.
|
||||
* @private
|
||||
*/
|
||||
AutoflowTabularRowController.prototype.updateRowData = function (datum) {
|
||||
const violations = this.evaluator.evaluate(datum, this.ranges[0]);
|
||||
|
||||
this.initialized = true;
|
||||
this.data.classes = violations ? violations.cssClass : '';
|
||||
this.data.value = this.rangeFormatter.format(datum);
|
||||
this.callback(this.domainFormatter.format(datum));
|
||||
};
|
||||
|
||||
/**
|
||||
* Activate this controller; begin listening for changes.
|
||||
*/
|
||||
AutoflowTabularRowController.prototype.activate = function () {
|
||||
this.unsubscribe = this.openmct.telemetry.subscribe(
|
||||
this.domainObject,
|
||||
this.updateRowData.bind(this)
|
||||
);
|
||||
|
||||
const options = {
|
||||
size: 1,
|
||||
strategy: 'latest',
|
||||
timeContext: this.openmct.time.getContextForView([])
|
||||
};
|
||||
this.openmct.telemetry.request(this.domainObject, options).then(
|
||||
function (history) {
|
||||
if (!this.initialized && history.length > 0) {
|
||||
this.updateRowData(history[history.length - 1]);
|
||||
}
|
||||
}.bind(this)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Destroy this controller; detach any associated resources.
|
||||
*/
|
||||
AutoflowTabularRowController.prototype.destroy = function () {
|
||||
if (this.unsubscribe) {
|
||||
this.unsubscribe();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update row to reflect incoming telemetry data.
|
||||
* @private
|
||||
*/
|
||||
AutoflowTabularRowController.prototype.updateRowData = function (datum) {
|
||||
const violations = this.evaluator.evaluate(datum, this.ranges[0]);
|
||||
|
||||
this.initialized = true;
|
||||
this.data.classes = violations ? violations.cssClass : '';
|
||||
this.data.value = this.rangeFormatter.format(datum);
|
||||
this.callback(this.domainFormatter.format(datum));
|
||||
};
|
||||
|
||||
/**
|
||||
* Activate this controller; begin listening for changes.
|
||||
*/
|
||||
AutoflowTabularRowController.prototype.activate = function () {
|
||||
this.unsubscribe = this.openmct.telemetry.subscribe(
|
||||
this.domainObject,
|
||||
this.updateRowData.bind(this)
|
||||
);
|
||||
|
||||
const options = {
|
||||
size: 1,
|
||||
strategy: 'latest',
|
||||
timeContext: this.openmct.time.getContextForView([])
|
||||
};
|
||||
this.openmct.telemetry.request(this.domainObject, options).then(
|
||||
function (history) {
|
||||
if (!this.initialized && history.length > 0) {
|
||||
this.updateRowData(history[history.length - 1]);
|
||||
}
|
||||
}.bind(this)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Destroy this controller; detach any associated resources.
|
||||
*/
|
||||
AutoflowTabularRowController.prototype.destroy = function () {
|
||||
if (this.unsubscribe) {
|
||||
this.unsubscribe();
|
||||
}
|
||||
};
|
||||
|
||||
return AutoflowTabularRowController;
|
||||
});
|
||||
};
|
||||
|
@ -20,96 +20,92 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define([
|
||||
'./AutoflowTabularController',
|
||||
'./AutoflowTabularConstants',
|
||||
'./VueView',
|
||||
'./autoflow-tabular.html'
|
||||
], function (AutoflowTabularController, AutoflowTabularConstants, VueView, autoflowTemplate) {
|
||||
const ROW_HEIGHT = AutoflowTabularConstants.ROW_HEIGHT;
|
||||
const SLIDER_HEIGHT = AutoflowTabularConstants.SLIDER_HEIGHT;
|
||||
const INITIAL_COLUMN_WIDTH = AutoflowTabularConstants.INITIAL_COLUMN_WIDTH;
|
||||
const MAX_COLUMN_WIDTH = AutoflowTabularConstants.MAX_COLUMN_WIDTH;
|
||||
const COLUMN_WIDTH_STEP = AutoflowTabularConstants.COLUMN_WIDTH_STEP;
|
||||
import autoflowTemplate from './autoflow-tabular.html';
|
||||
import AutoflowTabularConstants from './AutoflowTabularConstants';
|
||||
import AutoflowTabularController from './AutoflowTabularController';
|
||||
import VueView from './VueView';
|
||||
|
||||
/**
|
||||
* Implements the Autoflow Tabular view of a domain object.
|
||||
*/
|
||||
function AutoflowTabularView(domainObject, openmct) {
|
||||
const data = {
|
||||
items: [],
|
||||
columns: [],
|
||||
width: INITIAL_COLUMN_WIDTH,
|
||||
filter: '',
|
||||
updated: 'No updates',
|
||||
rowCount: 1
|
||||
};
|
||||
const controller = new AutoflowTabularController(domainObject, data, openmct);
|
||||
let interval;
|
||||
const ROW_HEIGHT = AutoflowTabularConstants.ROW_HEIGHT;
|
||||
const SLIDER_HEIGHT = AutoflowTabularConstants.SLIDER_HEIGHT;
|
||||
const INITIAL_COLUMN_WIDTH = AutoflowTabularConstants.INITIAL_COLUMN_WIDTH;
|
||||
const MAX_COLUMN_WIDTH = AutoflowTabularConstants.MAX_COLUMN_WIDTH;
|
||||
const COLUMN_WIDTH_STEP = AutoflowTabularConstants.COLUMN_WIDTH_STEP;
|
||||
|
||||
VueView.call(this, {
|
||||
data: data,
|
||||
methods: {
|
||||
increaseColumnWidth: function () {
|
||||
data.width += COLUMN_WIDTH_STEP;
|
||||
data.width = data.width > MAX_COLUMN_WIDTH ? INITIAL_COLUMN_WIDTH : data.width;
|
||||
},
|
||||
reflow: function () {
|
||||
let column = [];
|
||||
let index = 0;
|
||||
const filteredItems = data.items.filter(function (item) {
|
||||
return item.name.toLowerCase().indexOf(data.filter.toLowerCase()) !== -1;
|
||||
});
|
||||
/**
|
||||
* Implements the Autoflow Tabular view of a domain object.
|
||||
*/
|
||||
export default function AutoflowTabularView(domainObject, openmct) {
|
||||
const data = {
|
||||
items: [],
|
||||
columns: [],
|
||||
width: INITIAL_COLUMN_WIDTH,
|
||||
filter: '',
|
||||
updated: 'No updates',
|
||||
rowCount: 1
|
||||
};
|
||||
const controller = new AutoflowTabularController(domainObject, data, openmct);
|
||||
let interval;
|
||||
|
||||
data.columns = [];
|
||||
VueView.call(this, {
|
||||
data: data,
|
||||
methods: {
|
||||
increaseColumnWidth: function () {
|
||||
data.width += COLUMN_WIDTH_STEP;
|
||||
data.width = data.width > MAX_COLUMN_WIDTH ? INITIAL_COLUMN_WIDTH : data.width;
|
||||
},
|
||||
reflow: function () {
|
||||
let column = [];
|
||||
let index = 0;
|
||||
const filteredItems = data.items.filter(function (item) {
|
||||
return item.name.toLowerCase().indexOf(data.filter.toLowerCase()) !== -1;
|
||||
});
|
||||
|
||||
while (index < filteredItems.length) {
|
||||
if (column.length >= data.rowCount) {
|
||||
data.columns.push(column);
|
||||
column = [];
|
||||
}
|
||||
data.columns = [];
|
||||
|
||||
column.push(filteredItems[index]);
|
||||
index += 1;
|
||||
}
|
||||
|
||||
if (column.length > 0) {
|
||||
while (index < filteredItems.length) {
|
||||
if (column.length >= data.rowCount) {
|
||||
data.columns.push(column);
|
||||
column = [];
|
||||
}
|
||||
|
||||
column.push(filteredItems[index]);
|
||||
index += 1;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
filter: 'reflow',
|
||||
items: 'reflow',
|
||||
rowCount: 'reflow'
|
||||
},
|
||||
template: autoflowTemplate,
|
||||
unmounted: function () {
|
||||
controller.destroy();
|
||||
|
||||
if (interval) {
|
||||
clearInterval(interval);
|
||||
interval = undefined;
|
||||
if (column.length > 0) {
|
||||
data.columns.push(column);
|
||||
}
|
||||
},
|
||||
mounted: function () {
|
||||
controller.activate();
|
||||
|
||||
const updateRowHeight = function () {
|
||||
const tabularArea = this.$refs.autoflowItems;
|
||||
const height = tabularArea ? tabularArea.clientHeight : 0;
|
||||
const available = height - SLIDER_HEIGHT;
|
||||
const rows = Math.max(1, Math.floor(available / ROW_HEIGHT));
|
||||
data.rowCount = rows;
|
||||
}.bind(this);
|
||||
|
||||
interval = setInterval(updateRowHeight, 50);
|
||||
this.$nextTick(updateRowHeight);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
filter: 'reflow',
|
||||
items: 'reflow',
|
||||
rowCount: 'reflow'
|
||||
},
|
||||
template: autoflowTemplate,
|
||||
unmounted: function () {
|
||||
controller.destroy();
|
||||
|
||||
AutoflowTabularView.prototype = Object.create(VueView.default.prototype);
|
||||
if (interval) {
|
||||
clearInterval(interval);
|
||||
interval = undefined;
|
||||
}
|
||||
},
|
||||
mounted: function () {
|
||||
controller.activate();
|
||||
|
||||
return AutoflowTabularView;
|
||||
});
|
||||
const updateRowHeight = function () {
|
||||
const tabularArea = this.$refs.autoflowItems;
|
||||
const height = tabularArea ? tabularArea.clientHeight : 0;
|
||||
const available = height - SLIDER_HEIGHT;
|
||||
const rows = Math.max(1, Math.floor(available / ROW_HEIGHT));
|
||||
data.rowCount = rows;
|
||||
}.bind(this);
|
||||
|
||||
interval = setInterval(updateRowHeight, 50);
|
||||
this.$nextTick(updateRowHeight);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
AutoflowTabularView.prototype = Object.create(VueView.prototype);
|
||||
|
@ -22,13 +22,15 @@
|
||||
|
||||
import mount from 'utils/mount';
|
||||
export default function () {
|
||||
return function VueView(options) {
|
||||
const { vNode, destroy } = mount(options);
|
||||
class VueView {
|
||||
constructor(options) {
|
||||
const { vNode, destroy } = mount(options);
|
||||
this.show = function (container) {
|
||||
container.appendChild(vNode.el);
|
||||
};
|
||||
this.destroy = destroy;
|
||||
}
|
||||
}
|
||||
|
||||
this.show = function (container) {
|
||||
container.appendChild(vNode.el);
|
||||
};
|
||||
|
||||
this.destroy = destroy;
|
||||
};
|
||||
return VueView;
|
||||
}
|
||||
|
@ -20,44 +20,40 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define([], function () {
|
||||
function DOMObserver(element) {
|
||||
this.element = element;
|
||||
this.observers = [];
|
||||
}
|
||||
export default function DOMObserver(element) {
|
||||
this.element = element;
|
||||
this.observers = [];
|
||||
}
|
||||
|
||||
DOMObserver.prototype.when = function (latchFunction) {
|
||||
return new Promise(
|
||||
function (resolve, reject) {
|
||||
//Test latch function at least once
|
||||
if (latchFunction()) {
|
||||
resolve();
|
||||
} else {
|
||||
//Latch condition not true yet, create observer on DOM and test again on change.
|
||||
const config = {
|
||||
attributes: true,
|
||||
childList: true,
|
||||
subtree: true
|
||||
};
|
||||
const observer = new MutationObserver(function () {
|
||||
if (latchFunction()) {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
observer.observe(this.element, config);
|
||||
this.observers.push(observer);
|
||||
}
|
||||
}.bind(this)
|
||||
);
|
||||
};
|
||||
DOMObserver.prototype.when = function (latchFunction) {
|
||||
return new Promise(
|
||||
function (resolve, reject) {
|
||||
//Test latch function at least once
|
||||
if (latchFunction()) {
|
||||
resolve();
|
||||
} else {
|
||||
//Latch condition not true yet, create observer on DOM and test again on change.
|
||||
const config = {
|
||||
attributes: true,
|
||||
childList: true,
|
||||
subtree: true
|
||||
};
|
||||
const observer = new MutationObserver(function () {
|
||||
if (latchFunction()) {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
observer.observe(this.element, config);
|
||||
this.observers.push(observer);
|
||||
}
|
||||
}.bind(this)
|
||||
);
|
||||
};
|
||||
|
||||
DOMObserver.prototype.destroy = function () {
|
||||
this.observers.forEach(
|
||||
function (observer) {
|
||||
observer.disconnect();
|
||||
}.bind(this)
|
||||
);
|
||||
};
|
||||
|
||||
return DOMObserver;
|
||||
});
|
||||
DOMObserver.prototype.destroy = function () {
|
||||
this.observers.forEach(
|
||||
function (observer) {
|
||||
observer.disconnect();
|
||||
}.bind(this)
|
||||
);
|
||||
};
|
||||
|
@ -20,53 +20,49 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(function () {
|
||||
function DisplayLayoutType() {
|
||||
return {
|
||||
name: 'Display Layout',
|
||||
creatable: true,
|
||||
description:
|
||||
'Assemble other objects and components together into a reusable screen layout. Simply drag in the objects you want, position and size them. Save your design and view or edit it at any time.',
|
||||
cssClass: 'icon-layout',
|
||||
initialize(domainObject) {
|
||||
domainObject.composition = [];
|
||||
domainObject.configuration = {
|
||||
items: [],
|
||||
layoutGrid: [10, 10]
|
||||
};
|
||||
export default function DisplayLayoutType() {
|
||||
return {
|
||||
name: 'Display Layout',
|
||||
creatable: true,
|
||||
description:
|
||||
'Assemble other objects and components together into a reusable screen layout. Simply drag in the objects you want, position and size them. Save your design and view or edit it at any time.',
|
||||
cssClass: 'icon-layout',
|
||||
initialize(domainObject) {
|
||||
domainObject.composition = [];
|
||||
domainObject.configuration = {
|
||||
items: [],
|
||||
layoutGrid: [10, 10]
|
||||
};
|
||||
},
|
||||
form: [
|
||||
{
|
||||
name: 'Horizontal grid (px)',
|
||||
control: 'numberfield',
|
||||
cssClass: 'l-input-sm l-numeric',
|
||||
property: ['configuration', 'layoutGrid', 0],
|
||||
required: true
|
||||
},
|
||||
form: [
|
||||
{
|
||||
name: 'Horizontal grid (px)',
|
||||
control: 'numberfield',
|
||||
cssClass: 'l-input-sm l-numeric',
|
||||
property: ['configuration', 'layoutGrid', 0],
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'Vertical grid (px)',
|
||||
control: 'numberfield',
|
||||
cssClass: 'l-input-sm l-numeric',
|
||||
property: ['configuration', 'layoutGrid', 1],
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'Horizontal size (px)',
|
||||
control: 'numberfield',
|
||||
cssClass: 'l-input-sm l-numeric',
|
||||
property: ['configuration', 'layoutDimensions', 0],
|
||||
required: false
|
||||
},
|
||||
{
|
||||
name: 'Vertical size (px)',
|
||||
control: 'numberfield',
|
||||
cssClass: 'l-input-sm l-numeric',
|
||||
property: ['configuration', 'layoutDimensions', 1],
|
||||
required: false
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
return DisplayLayoutType;
|
||||
});
|
||||
{
|
||||
name: 'Vertical grid (px)',
|
||||
control: 'numberfield',
|
||||
cssClass: 'l-input-sm l-numeric',
|
||||
property: ['configuration', 'layoutGrid', 1],
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'Horizontal size (px)',
|
||||
control: 'numberfield',
|
||||
cssClass: 'l-input-sm l-numeric',
|
||||
property: ['configuration', 'layoutDimensions', 0],
|
||||
required: false
|
||||
},
|
||||
{
|
||||
name: 'Vertical size (px)',
|
||||
control: 'numberfield',
|
||||
cssClass: 'l-input-sm l-numeric',
|
||||
property: ['configuration', 'layoutDimensions', 1],
|
||||
required: false
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
@ -20,93 +20,89 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define([], function () {
|
||||
/**
|
||||
* Handles drag interactions on frames in layouts. This will
|
||||
* provides new positions/dimensions for frames based on
|
||||
* relative pixel positions provided; these will take into account
|
||||
* the grid size (in a snap-to sense) and will enforce some minimums
|
||||
* on both position and dimensions.
|
||||
*
|
||||
* The provided position and dimensions factors will determine
|
||||
* whether this is a move or a resize, and what type of resize it
|
||||
* will be. For instance, a position factor of [1, 1]
|
||||
* will move a frame along with the mouse as the drag
|
||||
* proceeds, while a dimension factor of [0, 0] will leave
|
||||
* dimensions unchanged. Combining these in different
|
||||
* ways results in different handles; a position factor of
|
||||
* [1, 0] and a dimensions factor of [-1, 0] will implement
|
||||
* a left-edge resize, as the horizontal position will move
|
||||
* with the mouse while the horizontal dimensions shrink in
|
||||
* kind (and vertical properties remain unmodified.)
|
||||
*
|
||||
* @param {object} rawPosition the initial position/dimensions
|
||||
* of the frame being interacted with
|
||||
* @param {number[]} posFactor the position factor
|
||||
* @param {number[]} dimFactor the dimensions factor
|
||||
* @param {number[]} the size of each grid element, in pixels
|
||||
* @constructor
|
||||
* @memberof platform/features/layout
|
||||
*/
|
||||
function LayoutDrag(rawPosition, posFactor, dimFactor, gridSize) {
|
||||
this.rawPosition = rawPosition;
|
||||
this.posFactor = posFactor;
|
||||
this.dimFactor = dimFactor;
|
||||
this.gridSize = gridSize;
|
||||
}
|
||||
/**
|
||||
* Handles drag interactions on frames in layouts. This will
|
||||
* provides new positions/dimensions for frames based on
|
||||
* relative pixel positions provided; these will take into account
|
||||
* the grid size (in a snap-to sense) and will enforce some minimums
|
||||
* on both position and dimensions.
|
||||
*
|
||||
* The provided position and dimensions factors will determine
|
||||
* whether this is a move or a resize, and what type of resize it
|
||||
* will be. For instance, a position factor of [1, 1]
|
||||
* will move a frame along with the mouse as the drag
|
||||
* proceeds, while a dimension factor of [0, 0] will leave
|
||||
* dimensions unchanged. Combining these in different
|
||||
* ways results in different handles; a position factor of
|
||||
* [1, 0] and a dimensions factor of [-1, 0] will implement
|
||||
* a left-edge resize, as the horizontal position will move
|
||||
* with the mouse while the horizontal dimensions shrink in
|
||||
* kind (and vertical properties remain unmodified.)
|
||||
*
|
||||
* @param {object} rawPosition the initial position/dimensions
|
||||
* of the frame being interacted with
|
||||
* @param {number[]} posFactor the position factor
|
||||
* @param {number[]} dimFactor the dimensions factor
|
||||
* @param {number[]} the size of each grid element, in pixels
|
||||
* @constructor
|
||||
* @memberof platform/features/layout
|
||||
*/
|
||||
export default function LayoutDrag(rawPosition, posFactor, dimFactor, gridSize) {
|
||||
this.rawPosition = rawPosition;
|
||||
this.posFactor = posFactor;
|
||||
this.dimFactor = dimFactor;
|
||||
this.gridSize = gridSize;
|
||||
}
|
||||
|
||||
// Convert a delta from pixel coordinates to grid coordinates,
|
||||
// rounding to whole-number grid coordinates.
|
||||
function toGridDelta(gridSize, pixelDelta) {
|
||||
return pixelDelta.map(function (v, i) {
|
||||
return Math.round(v / gridSize[i]);
|
||||
});
|
||||
}
|
||||
// Convert a delta from pixel coordinates to grid coordinates,
|
||||
// rounding to whole-number grid coordinates.
|
||||
function toGridDelta(gridSize, pixelDelta) {
|
||||
return pixelDelta.map(function (v, i) {
|
||||
return Math.round(v / gridSize[i]);
|
||||
});
|
||||
}
|
||||
|
||||
// Utility function to perform element-by-element multiplication
|
||||
function multiply(array, factors) {
|
||||
return array.map(function (v, i) {
|
||||
return v * factors[i];
|
||||
});
|
||||
}
|
||||
// Utility function to perform element-by-element multiplication
|
||||
function multiply(array, factors) {
|
||||
return array.map(function (v, i) {
|
||||
return v * factors[i];
|
||||
});
|
||||
}
|
||||
|
||||
// Utility function to perform element-by-element addition
|
||||
function add(array, other) {
|
||||
return array.map(function (v, i) {
|
||||
return v + other[i];
|
||||
});
|
||||
}
|
||||
// Utility function to perform element-by-element addition
|
||||
function add(array, other) {
|
||||
return array.map(function (v, i) {
|
||||
return v + other[i];
|
||||
});
|
||||
}
|
||||
|
||||
// Utility function to perform element-by-element max-choosing
|
||||
function max(array, other) {
|
||||
return array.map(function (v, i) {
|
||||
return Math.max(v, other[i]);
|
||||
});
|
||||
}
|
||||
// Utility function to perform element-by-element max-choosing
|
||||
function max(array, other) {
|
||||
return array.map(function (v, i) {
|
||||
return Math.max(v, other[i]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a new position object in grid coordinates, with
|
||||
* position and dimensions both offset appropriately
|
||||
* according to the factors supplied in the constructor.
|
||||
* @param {number[]} pixelDelta the offset from the
|
||||
* original position, in pixels
|
||||
*/
|
||||
LayoutDrag.prototype.getAdjustedPositionAndDimensions = function (pixelDelta) {
|
||||
const gridDelta = toGridDelta(this.gridSize, pixelDelta);
|
||||
/**
|
||||
* Get a new position object in grid coordinates, with
|
||||
* position and dimensions both offset appropriately
|
||||
* according to the factors supplied in the constructor.
|
||||
* @param {number[]} pixelDelta the offset from the
|
||||
* original position, in pixels
|
||||
*/
|
||||
LayoutDrag.prototype.getAdjustedPositionAndDimensions = function (pixelDelta) {
|
||||
const gridDelta = toGridDelta(this.gridSize, pixelDelta);
|
||||
|
||||
return {
|
||||
position: max(add(this.rawPosition.position, multiply(gridDelta, this.posFactor)), [0, 0]),
|
||||
dimensions: max(add(this.rawPosition.dimensions, multiply(gridDelta, this.dimFactor)), [1, 1])
|
||||
};
|
||||
return {
|
||||
position: max(add(this.rawPosition.position, multiply(gridDelta, this.posFactor)), [0, 0]),
|
||||
dimensions: max(add(this.rawPosition.dimensions, multiply(gridDelta, this.dimFactor)), [1, 1])
|
||||
};
|
||||
};
|
||||
|
||||
LayoutDrag.prototype.getAdjustedPosition = function (pixelDelta) {
|
||||
const gridDelta = toGridDelta(this.gridSize, pixelDelta);
|
||||
LayoutDrag.prototype.getAdjustedPosition = function (pixelDelta) {
|
||||
const gridDelta = toGridDelta(this.gridSize, pixelDelta);
|
||||
|
||||
return {
|
||||
position: max(add(this.rawPosition.position, multiply(gridDelta, this.posFactor)), [0, 0])
|
||||
};
|
||||
return {
|
||||
position: max(add(this.rawPosition.position, multiply(gridDelta, this.posFactor)), [0, 0])
|
||||
};
|
||||
|
||||
return LayoutDrag;
|
||||
});
|
||||
};
|
||||
|
@ -20,12 +20,12 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(['./FiltersInspectorViewProvider'], function (FiltersInspectorViewProvider) {
|
||||
return function plugin(supportedObjectTypesArray) {
|
||||
return function install(openmct) {
|
||||
openmct.inspectorViews.addProvider(
|
||||
new FiltersInspectorViewProvider.default(openmct, supportedObjectTypesArray)
|
||||
);
|
||||
};
|
||||
import FiltersInspectorViewProvider from './FiltersInspectorViewProvider';
|
||||
|
||||
export default function plugin(supportedObjectTypesArray) {
|
||||
return function install(openmct) {
|
||||
openmct.inspectorViews.addProvider(
|
||||
new FiltersInspectorViewProvider(openmct, supportedObjectTypesArray)
|
||||
);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
@ -20,33 +20,31 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(['./flexibleLayoutViewProvider', './utils/container', './toolbarProvider'], function (
|
||||
FlexibleLayoutViewProvider,
|
||||
Container,
|
||||
ToolBarProvider
|
||||
) {
|
||||
return function plugin() {
|
||||
return function install(openmct) {
|
||||
openmct.objectViews.addProvider(new FlexibleLayoutViewProvider.default(openmct));
|
||||
import FlexibleLayoutViewProvider from './flexibleLayoutViewProvider';
|
||||
import ToolBarProvider from './toolbarProvider';
|
||||
import Container from './utils/container';
|
||||
|
||||
openmct.types.addType('flexible-layout', {
|
||||
name: 'Flexible Layout',
|
||||
creatable: true,
|
||||
description:
|
||||
'A fluid, flexible layout canvas that can display multiple objects in rows or columns.',
|
||||
cssClass: 'icon-flexible-layout',
|
||||
initialize: function (domainObject) {
|
||||
domainObject.configuration = {
|
||||
containers: [new Container.default(50), new Container.default(50)],
|
||||
rowsLayout: false
|
||||
};
|
||||
domainObject.composition = [];
|
||||
}
|
||||
});
|
||||
export default function plugin() {
|
||||
return function install(openmct) {
|
||||
openmct.objectViews.addProvider(new FlexibleLayoutViewProvider(openmct));
|
||||
|
||||
let toolbar = ToolBarProvider.default(openmct);
|
||||
openmct.types.addType('flexible-layout', {
|
||||
name: 'Flexible Layout',
|
||||
creatable: true,
|
||||
description:
|
||||
'A fluid, flexible layout canvas that can display multiple objects in rows or columns.',
|
||||
cssClass: 'icon-flexible-layout',
|
||||
initialize: function (domainObject) {
|
||||
domainObject.configuration = {
|
||||
containers: [new Container(50), new Container(50)],
|
||||
rowsLayout: false
|
||||
};
|
||||
domainObject.composition = [];
|
||||
}
|
||||
});
|
||||
|
||||
openmct.toolbars.addProvider(toolbar);
|
||||
};
|
||||
let toolbar = ToolBarProvider(openmct);
|
||||
|
||||
openmct.toolbars.addProvider(toolbar);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
@ -20,80 +20,78 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define([], function () {
|
||||
const helperFunctions = {
|
||||
listenTo: function (object, event, callback, context) {
|
||||
if (!this._listeningTo) {
|
||||
this._listeningTo = [];
|
||||
}
|
||||
|
||||
const listener = {
|
||||
object: object,
|
||||
event: event,
|
||||
callback: callback,
|
||||
context: context,
|
||||
_cb: context ? callback.bind(context) : callback
|
||||
};
|
||||
if (object.$watch && event.indexOf('change:') === 0) {
|
||||
const scopePath = event.replace('change:', '');
|
||||
listener.unlisten = object.$watch(scopePath, listener._cb, true);
|
||||
} else if (object.$on) {
|
||||
listener.unlisten = object.$on(event, listener._cb);
|
||||
} else if (object.addEventListener) {
|
||||
object.addEventListener(event, listener._cb);
|
||||
} else {
|
||||
object.on(event, listener._cb);
|
||||
}
|
||||
|
||||
this._listeningTo.push(listener);
|
||||
},
|
||||
|
||||
stopListening: function (object, event, callback, context) {
|
||||
if (!this._listeningTo) {
|
||||
this._listeningTo = [];
|
||||
}
|
||||
|
||||
this._listeningTo
|
||||
.filter(function (listener) {
|
||||
if (object && object !== listener.object) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (event && event !== listener.event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (callback && callback !== listener.callback) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (context && context !== listener.context) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
.map(function (listener) {
|
||||
if (listener.unlisten) {
|
||||
listener.unlisten();
|
||||
} else if (listener.object.removeEventListener) {
|
||||
listener.object.removeEventListener(listener.event, listener._cb);
|
||||
} else {
|
||||
listener.object.off(listener.event, listener._cb);
|
||||
}
|
||||
|
||||
return listener;
|
||||
})
|
||||
.forEach(function (listener) {
|
||||
this._listeningTo.splice(this._listeningTo.indexOf(listener), 1);
|
||||
}, this);
|
||||
},
|
||||
|
||||
extend: function (object) {
|
||||
object.listenTo = helperFunctions.listenTo;
|
||||
object.stopListening = helperFunctions.stopListening;
|
||||
const helperFunctions = {
|
||||
listenTo: function (object, event, callback, context) {
|
||||
if (!this._listeningTo) {
|
||||
this._listeningTo = [];
|
||||
}
|
||||
};
|
||||
|
||||
return helperFunctions;
|
||||
});
|
||||
const listener = {
|
||||
object: object,
|
||||
event: event,
|
||||
callback: callback,
|
||||
context: context,
|
||||
_cb: context ? callback.bind(context) : callback
|
||||
};
|
||||
if (object.$watch && event.indexOf('change:') === 0) {
|
||||
const scopePath = event.replace('change:', '');
|
||||
listener.unlisten = object.$watch(scopePath, listener._cb, true);
|
||||
} else if (object.$on) {
|
||||
listener.unlisten = object.$on(event, listener._cb);
|
||||
} else if (object.addEventListener) {
|
||||
object.addEventListener(event, listener._cb);
|
||||
} else {
|
||||
object.on(event, listener._cb);
|
||||
}
|
||||
|
||||
this._listeningTo.push(listener);
|
||||
},
|
||||
|
||||
stopListening: function (object, event, callback, context) {
|
||||
if (!this._listeningTo) {
|
||||
this._listeningTo = [];
|
||||
}
|
||||
|
||||
this._listeningTo
|
||||
.filter(function (listener) {
|
||||
if (object && object !== listener.object) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (event && event !== listener.event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (callback && callback !== listener.callback) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (context && context !== listener.context) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
.map(function (listener) {
|
||||
if (listener.unlisten) {
|
||||
listener.unlisten();
|
||||
} else if (listener.object.removeEventListener) {
|
||||
listener.object.removeEventListener(listener.event, listener._cb);
|
||||
} else {
|
||||
listener.object.off(listener.event, listener._cb);
|
||||
}
|
||||
|
||||
return listener;
|
||||
})
|
||||
.forEach(function (listener) {
|
||||
this._listeningTo.splice(this._listeningTo.indexOf(listener), 1);
|
||||
}, this);
|
||||
},
|
||||
|
||||
extend: function (object) {
|
||||
object.listenTo = helperFunctions.listenTo;
|
||||
object.stopListening = helperFunctions.stopListening;
|
||||
}
|
||||
};
|
||||
|
||||
export default helperFunctions;
|
||||
|
@ -20,24 +20,24 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(['../../../src/plugins/utcTimeSystem/LocalClock'], function (LocalClock) {
|
||||
import LocalClock from '../../../src/plugins/utcTimeSystem/LocalClock';
|
||||
|
||||
class LADClock extends LocalClock {
|
||||
/**
|
||||
* A {@link Clock} that mocks a "latest available data" type tick source.
|
||||
* This is for testing purposes only, and behaves identically to a local clock.
|
||||
* It DOES NOT tick on receipt of data.
|
||||
* @constructor
|
||||
*/
|
||||
function LADClock(period) {
|
||||
LocalClock.call(this, period);
|
||||
constructor(period) {
|
||||
super(period);
|
||||
|
||||
this.key = 'test-lad';
|
||||
this.mode = 'lad';
|
||||
this.cssClass = 'icon-suitcase';
|
||||
this.name = 'Latest available data';
|
||||
this.description = 'Updates when when new data is available';
|
||||
this.description = 'Updates when new data is available';
|
||||
}
|
||||
}
|
||||
|
||||
LADClock.prototype = Object.create(LocalClock.prototype);
|
||||
|
||||
return LADClock;
|
||||
});
|
||||
export default LADClock;
|
||||
|