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'),
|
MCT: path.join(projectRootDir, 'src/MCT'),
|
||||||
testUtils: path.join(projectRootDir, 'src/utils/testUtils.js'),
|
testUtils: path.join(projectRootDir, 'src/utils/testUtils.js'),
|
||||||
objectUtils: path.join(projectRootDir, 'src/api/objects/object-utils.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: [
|
plugins: [
|
||||||
|
@ -2,13 +2,16 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
extends: ['plugin:playwright/playwright-test'],
|
extends: ['plugin:playwright/playwright-test'],
|
||||||
rules: {
|
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: [
|
overrides: [
|
||||||
{
|
{
|
||||||
files: ['tests/visual/*.spec.js'],
|
files: ['tests/visual-a11y/*.spec.js'],
|
||||||
rules: {
|
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?
|
- How is Open MCT extending default Playwright functionality?
|
||||||
- What about Component Testing?
|
- 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
|
### e2e Troubleshooting
|
||||||
|
|
||||||
Please follow the general guide troubleshooting in [the general troubleshooting doc](../TESTING.md#troubleshooting-ci)
|
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();
|
await page.getByRole('button', { name: 'Create' }).click();
|
||||||
|
|
||||||
// Click the object specified by 'type'
|
// 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'
|
// Modify the name input field of the domain object to accept 'name'
|
||||||
const nameInput = page.locator('form[name="mctForm"] .first input[type="text"]');
|
await page.getByLabel('Title', { exact: true }).fill(name);
|
||||||
await nameInput.fill('');
|
|
||||||
await nameInput.fill(name);
|
|
||||||
|
|
||||||
if (page.testNotes) {
|
if (page.testNotes) {
|
||||||
// Fill the "Notes" section with information about the
|
// Fill the "Notes" section with information about the
|
||||||
|
@ -35,7 +35,7 @@
|
|||||||
|
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const { test, expect } = require('./pluginFixtures');
|
const { test, expect } = require('../pluginFixtures');
|
||||||
const AxeBuilder = require('@axe-core/playwright').default;
|
const AxeBuilder = require('@axe-core/playwright').default;
|
||||||
|
|
||||||
// Constants for repeated values
|
// 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
|
outputFolder: '../html-test-results' //Must be in different location due to https://github.com/microsoft/playwright/issues/12840
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
['junit', { outputFile: '../test-results/results.xml' }],
|
['junit', { outputFile: '../test-results/results.xml' }]
|
||||||
['@deploysentinel/playwright']
|
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
const { test, expect } = require('../../pluginFixtures.js');
|
const { test, expect } = require('../../fixtures/pluginFixtures');
|
||||||
const {
|
const {
|
||||||
createDomainObjectWithDefaults,
|
createDomainObjectWithDefaults,
|
||||||
createNotification,
|
createNotification,
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
This test suite is dedicated to tests which verify branding related components.
|
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.describe('Branding tests', () => {
|
||||||
test('About Modal launches with basic branding properties', async ({ page }) => {
|
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.
|
Make no assumptions about the order that elements appear in the DOM.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const { test, expect } = require('../../pluginFixtures');
|
const { test, expect } = require('../../../pluginFixtures');
|
||||||
const { createDomainObjectWithDefaults, expandEntireTree } = require('../../appActions');
|
const { createDomainObjectWithDefaults, expandEntireTree } = require('../../../appActions');
|
||||||
|
|
||||||
test.describe('Verify tooltips', () => {
|
test.describe('Verify tooltips', () => {
|
||||||
let folder1;
|
let folder1;
|
@ -20,8 +20,8 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
const { createDomainObjectWithDefaults, waitForPlotsToRender } = require('../../appActions');
|
const { createDomainObjectWithDefaults, waitForPlotsToRender } = require('../../../appActions');
|
||||||
const { test, expect } = require('../../pluginFixtures');
|
const { test, expect } = require('../../../pluginFixtures');
|
||||||
|
|
||||||
test.describe('Tabs View', () => {
|
test.describe('Tabs View', () => {
|
||||||
test('Renders tabbed elements nicely', async ({ page }) => {
|
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.
|
clockOptions plugin fixture.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const { VISUAL_URL, MISSION_TIME } = require('../../constants');
|
const { VISUAL_URL, MISSION_TIME } = require('../../../constants');
|
||||||
const { test, expect } = require('../../pluginFixtures');
|
const { test, expect } = require('../../../pluginFixtures');
|
||||||
const percySnapshot = require('@percy/playwright');
|
const percySnapshot = require('@percy/playwright');
|
||||||
|
|
||||||
test.describe('Visual - Controlled Clock', () => {
|
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' });
|
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,
|
page,
|
||||||
theme
|
theme
|
||||||
}) => {
|
}) => {
|
||||||
// Create a clock domain object
|
// Create a clock domain object
|
||||||
await createDomainObjectWithDefaults(page, {
|
await createDomainObjectWithDefaults(page, {
|
||||||
type: 'Clock',
|
type: 'Condition Widget',
|
||||||
name: 'Default Clock'
|
name: 'Visual Condition Widget'
|
||||||
});
|
});
|
||||||
// Click on the div with role="alert" that has "Save successful" text
|
// Click on the div with role="alert" that has "Save successful" text
|
||||||
await page.locator('div[role="alert"]:has-text("Save successful")').click();
|
await page.locator('div[role="alert"]:has-text("Save successful")').click();
|
@ -1,138 +1,134 @@
|
|||||||
define(['lodash'], function (_) {
|
const METADATA_BY_TYPE = {
|
||||||
var METADATA_BY_TYPE = {
|
generator: {
|
||||||
generator: {
|
values: [
|
||||||
values: [
|
{
|
||||||
{
|
key: 'name',
|
||||||
key: 'name',
|
name: 'Name',
|
||||||
name: 'Name',
|
format: 'string'
|
||||||
format: 'string'
|
},
|
||||||
},
|
{
|
||||||
{
|
key: 'utc',
|
||||||
key: 'utc',
|
name: 'Time',
|
||||||
name: 'Time',
|
format: 'utc',
|
||||||
format: 'utc',
|
hints: {
|
||||||
hints: {
|
domain: 1
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
},
|
||||||
},
|
{
|
||||||
'example.state-generator': {
|
key: 'yesterday',
|
||||||
values: [
|
name: 'Yesterday',
|
||||||
{
|
format: 'utc',
|
||||||
key: 'name',
|
hints: {
|
||||||
name: 'Name',
|
domain: 2
|
||||||
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: '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) {
|
GeneratorMetadataProvider.prototype.supportsMetadata = function (domainObject) {
|
||||||
return Object.prototype.hasOwnProperty.call(METADATA_BY_TYPE, domainObject.type);
|
return Object.prototype.hasOwnProperty.call(METADATA_BY_TYPE, domainObject.type);
|
||||||
};
|
};
|
||||||
|
|
||||||
GeneratorMetadataProvider.prototype.getMetadata = function (domainObject) {
|
GeneratorMetadataProvider.prototype.getMetadata = function (domainObject) {
|
||||||
return Object.assign({}, domainObject.telemetry, METADATA_BY_TYPE[domainObject.type]);
|
return Object.assign({}, domainObject.telemetry, METADATA_BY_TYPE[domainObject.type]);
|
||||||
};
|
};
|
||||||
|
|
||||||
return GeneratorMetadataProvider;
|
|
||||||
});
|
|
||||||
|
@ -20,86 +20,84 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define(['./WorkerInterface'], function (WorkerInterface) {
|
import WorkerInterface from './WorkerInterface';
|
||||||
var REQUEST_DEFAULTS = {
|
|
||||||
amplitude: 1,
|
|
||||||
period: 10,
|
|
||||||
offset: 0,
|
|
||||||
dataRateInHz: 1,
|
|
||||||
randomness: 0,
|
|
||||||
phase: 0,
|
|
||||||
loadDelay: 0,
|
|
||||||
infinityValues: false,
|
|
||||||
exceedFloat32: false
|
|
||||||
};
|
|
||||||
|
|
||||||
function GeneratorProvider(openmct, StalenessProvider) {
|
const REQUEST_DEFAULTS = {
|
||||||
this.openmct = openmct;
|
amplitude: 1,
|
||||||
this.workerInterface = new WorkerInterface(openmct, StalenessProvider);
|
period: 10,
|
||||||
}
|
offset: 0,
|
||||||
|
dataRateInHz: 1,
|
||||||
|
randomness: 0,
|
||||||
|
phase: 0,
|
||||||
|
loadDelay: 0,
|
||||||
|
infinityValues: false,
|
||||||
|
exceedFloat32: false
|
||||||
|
};
|
||||||
|
|
||||||
GeneratorProvider.prototype.canProvideTelemetry = function (domainObject) {
|
export default function GeneratorProvider(openmct, StalenessProvider) {
|
||||||
return domainObject.type === 'generator';
|
this.openmct = openmct;
|
||||||
};
|
this.workerInterface = new WorkerInterface(openmct, StalenessProvider);
|
||||||
|
}
|
||||||
|
|
||||||
GeneratorProvider.prototype.supportsRequest = GeneratorProvider.prototype.supportsSubscribe =
|
GeneratorProvider.prototype.canProvideTelemetry = function (domainObject) {
|
||||||
GeneratorProvider.prototype.canProvideTelemetry;
|
return domainObject.type === 'generator';
|
||||||
|
};
|
||||||
|
|
||||||
GeneratorProvider.prototype.makeWorkerRequest = function (domainObject, request) {
|
GeneratorProvider.prototype.supportsRequest = GeneratorProvider.prototype.supportsSubscribe =
|
||||||
var props = [
|
GeneratorProvider.prototype.canProvideTelemetry;
|
||||||
'amplitude',
|
|
||||||
'period',
|
|
||||||
'offset',
|
|
||||||
'dataRateInHz',
|
|
||||||
'randomness',
|
|
||||||
'phase',
|
|
||||||
'loadDelay',
|
|
||||||
'infinityValues',
|
|
||||||
'exceedFloat32'
|
|
||||||
];
|
|
||||||
|
|
||||||
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) {
|
var workerRequest = {};
|
||||||
if (
|
|
||||||
domainObject.telemetry &&
|
|
||||||
Object.prototype.hasOwnProperty.call(domainObject.telemetry, prop)
|
|
||||||
) {
|
|
||||||
workerRequest[prop] = domainObject.telemetry[prop];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request && Object.prototype.hasOwnProperty.call(request, prop)) {
|
props.forEach(function (prop) {
|
||||||
workerRequest[prop] = request[prop];
|
if (
|
||||||
}
|
domainObject.telemetry &&
|
||||||
|
Object.prototype.hasOwnProperty.call(domainObject.telemetry, prop)
|
||||||
|
) {
|
||||||
|
workerRequest[prop] = domainObject.telemetry[prop];
|
||||||
|
}
|
||||||
|
|
||||||
if (!Object.prototype.hasOwnProperty.call(workerRequest, prop)) {
|
if (request && Object.prototype.hasOwnProperty.call(request, prop)) {
|
||||||
workerRequest[prop] = REQUEST_DEFAULTS[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[prop] = Number(workerRequest[prop]);
|
||||||
workerRequest.name = domainObject.name;
|
});
|
||||||
|
|
||||||
return workerRequest;
|
workerRequest.id = this.openmct.objects.makeKeyString(domainObject.identifier);
|
||||||
};
|
workerRequest.name = domainObject.name;
|
||||||
|
|
||||||
GeneratorProvider.prototype.request = function (domainObject, request) {
|
return workerRequest;
|
||||||
var workerRequest = this.makeWorkerRequest(domainObject, request);
|
};
|
||||||
workerRequest.start = request.start;
|
|
||||||
workerRequest.end = request.end;
|
|
||||||
|
|
||||||
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) {
|
return this.workerInterface.request(workerRequest);
|
||||||
var workerRequest = this.makeWorkerRequest(domainObject, {});
|
};
|
||||||
|
|
||||||
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.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define([], function () {
|
var PURPLE = {
|
||||||
var PURPLE = {
|
sin: 2.2,
|
||||||
sin: 2.2,
|
cos: 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 = {
|
rl: {
|
||||||
sin: 0.9,
|
cssClass: 'is-limit--lwr is-limit--red',
|
||||||
cos: 0.9
|
high: -RED,
|
||||||
|
low: Number.NEGATIVE_INFINITY,
|
||||||
|
name: 'Red Low'
|
||||||
},
|
},
|
||||||
ORANGE = {
|
yh: {
|
||||||
sin: 0.7,
|
cssClass: 'is-limit--upr is-limit--yellow',
|
||||||
cos: 0.7
|
low: YELLOW,
|
||||||
|
high: RED,
|
||||||
|
name: 'Yellow High'
|
||||||
},
|
},
|
||||||
YELLOW = {
|
yl: {
|
||||||
sin: 0.5,
|
cssClass: 'is-limit--lwr is-limit--yellow',
|
||||||
cos: 0.5
|
low: -RED,
|
||||||
},
|
high: -YELLOW,
|
||||||
CYAN = {
|
name: 'Yellow Low'
|
||||||
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';
|
|
||||||
};
|
};
|
||||||
|
|
||||||
SinewaveLimitProvider.prototype.getLimitEvaluator = function (domainObject) {
|
export default function SinewaveLimitProvider() {}
|
||||||
return {
|
|
||||||
evaluate: function (datum, valueMetadata) {
|
|
||||||
var range = valueMetadata && valueMetadata.key;
|
|
||||||
|
|
||||||
if (datum[range] > RED[range]) {
|
SinewaveLimitProvider.prototype.supportsLimits = function (domainObject) {
|
||||||
return LIMITS.rh;
|
return domainObject.type === 'generator';
|
||||||
}
|
};
|
||||||
|
|
||||||
if (datum[range] < -RED[range]) {
|
SinewaveLimitProvider.prototype.getLimitEvaluator = function (domainObject) {
|
||||||
return LIMITS.rl;
|
return {
|
||||||
}
|
evaluate: function (datum, valueMetadata) {
|
||||||
|
var range = valueMetadata && valueMetadata.key;
|
||||||
|
|
||||||
if (datum[range] > YELLOW[range]) {
|
if (datum[range] > RED[range]) {
|
||||||
return LIMITS.yh;
|
return LIMITS.rh;
|
||||||
}
|
|
||||||
|
|
||||||
if (datum[range] < -YELLOW[range]) {
|
|
||||||
return LIMITS.yl;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
SinewaveLimitProvider.prototype.getLimits = function (domainObject) {
|
if (datum[range] < -RED[range]) {
|
||||||
return {
|
return LIMITS.rl;
|
||||||
limits: function () {
|
}
|
||||||
return Promise.resolve({
|
|
||||||
WATCH: {
|
if (datum[range] > YELLOW[range]) {
|
||||||
low: {
|
return LIMITS.yh;
|
||||||
color: 'cyan',
|
}
|
||||||
sin: -CYAN.sin,
|
|
||||||
cos: -CYAN.cos
|
if (datum[range] < -YELLOW[range]) {
|
||||||
},
|
return LIMITS.yl;
|
||||||
high: {
|
}
|
||||||
color: 'cyan',
|
}
|
||||||
...CYAN
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
|
SinewaveLimitProvider.prototype.getLimits = function (domainObject) {
|
||||||
|
return {
|
||||||
|
limits: function () {
|
||||||
|
return Promise.resolve({
|
||||||
|
WATCH: {
|
||||||
|
low: {
|
||||||
|
color: 'cyan',
|
||||||
|
sin: -CYAN.sin,
|
||||||
|
cos: -CYAN.cos
|
||||||
},
|
},
|
||||||
WARNING: {
|
high: {
|
||||||
low: {
|
color: 'cyan',
|
||||||
color: 'yellow',
|
...CYAN
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
}
|
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.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define([], function () {
|
export default function StateGeneratorProvider() {}
|
||||||
function StateGeneratorProvider() {}
|
|
||||||
|
|
||||||
function pointForTimestamp(timestamp, duration, name) {
|
function pointForTimestamp(timestamp, duration, name) {
|
||||||
return {
|
return {
|
||||||
name: name,
|
name: name,
|
||||||
utc: Math.floor(timestamp / duration) * duration,
|
utc: Math.floor(timestamp / duration) * duration,
|
||||||
value: Math.floor(timestamp / duration) % 2
|
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) {
|
var data = [];
|
||||||
return domainObject.type === 'example.state-generator';
|
while (start <= end && data.length < 5000) {
|
||||||
};
|
data.push(pointForTimestamp(start, duration, domainObject.name));
|
||||||
|
start += duration;
|
||||||
|
}
|
||||||
|
|
||||||
StateGeneratorProvider.prototype.subscribe = function (domainObject, callback) {
|
return Promise.resolve(data);
|
||||||
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;
|
|
||||||
});
|
|
||||||
|
@ -20,88 +20,86 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define(['uuid'], function ({ v4: uuid }) {
|
import { v4 as uuid } from '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 = {};
|
|
||||||
|
|
||||||
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.worker.postMessage(message);
|
||||||
this.StalenessProvider.on('stalenessEvent', ({ id, isStale }) => {
|
|
||||||
this.staleTelemetryIds[id] = isStale;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
WorkerInterface.prototype.onMessage = function (message) {
|
return message.id;
|
||||||
message = message.data;
|
};
|
||||||
var callback = this.callbacks[message.id];
|
|
||||||
if (callback) {
|
|
||||||
callback(message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
WorkerInterface.prototype.dispatch = function (request, data, callback) {
|
WorkerInterface.prototype.request = function (request) {
|
||||||
var message = {
|
var deferred = {};
|
||||||
request: request,
|
var promise = new Promise(function (resolve, reject) {
|
||||||
data: data,
|
deferred.resolve = resolve;
|
||||||
id: uuid()
|
deferred.reject = reject;
|
||||||
};
|
});
|
||||||
|
var messageId;
|
||||||
|
|
||||||
if (callback) {
|
let self = this;
|
||||||
this.callbacks[message.id] = callback;
|
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) {
|
return promise;
|
||||||
var deferred = {};
|
};
|
||||||
var promise = new Promise(function (resolve, reject) {
|
|
||||||
deferred.resolve = resolve;
|
|
||||||
deferred.reject = reject;
|
|
||||||
});
|
|
||||||
var messageId;
|
|
||||||
|
|
||||||
let self = this;
|
WorkerInterface.prototype.subscribe = function (request, cb) {
|
||||||
function callback(message) {
|
const { id, loadDelay } = request;
|
||||||
if (message.error) {
|
const messageId = this.dispatch('subscribe', request, (message) => {
|
||||||
deferred.reject(message.error);
|
if (!this.staleTelemetryIds[id]) {
|
||||||
} else {
|
setTimeout(() => cb(message.data), Math.max(loadDelay, 0));
|
||||||
deferred.resolve(message.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete self.callbacks[messageId];
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
messageId = this.dispatch('request', request, callback.bind(this));
|
return function () {
|
||||||
|
this.dispatch('unsubscribe', {
|
||||||
return promise;
|
id: 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));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
delete this.callbacks[messageId];
|
||||||
return function () {
|
}.bind(this);
|
||||||
this.dispatch('unsubscribe', {
|
};
|
||||||
id: messageId
|
|
||||||
});
|
|
||||||
delete this.callbacks[messageId];
|
|
||||||
}.bind(this);
|
|
||||||
};
|
|
||||||
|
|
||||||
return WorkerInterface;
|
|
||||||
});
|
|
||||||
|
@ -75,7 +75,7 @@ if (document.currentScript) {
|
|||||||
* @property {OpenMCTComponent[]} components
|
* @property {OpenMCTComponent[]} components
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const MCT = require('./src/MCT');
|
const { MCT } = require('./src/MCT');
|
||||||
|
|
||||||
/** @type {OpenMCT} */
|
/** @type {OpenMCT} */
|
||||||
const openmct = new MCT();
|
const openmct = new MCT();
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
"eslint-config-prettier": "9.0.0",
|
"eslint-config-prettier": "9.0.0",
|
||||||
"eslint-plugin-compat": "4.2.0",
|
"eslint-plugin-compat": "4.2.0",
|
||||||
"eslint-plugin-no-unsanitized": "4.0.2",
|
"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-prettier": "4.2.1",
|
||||||
"eslint-plugin-simple-import-sort": "10.0.0",
|
"eslint-plugin-simple-import-sort": "10.0.0",
|
||||||
"eslint-plugin-unicorn": "49.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: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: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: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: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: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",
|
"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.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
/* eslint-disable no-undef */
|
/* eslint-disable no-undef */
|
||||||
define([
|
import EventEmitter from 'EventEmitter';
|
||||||
'EventEmitter',
|
import { createApp, markRaw } from 'vue';
|
||||||
'./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 ActionsAPI from './api/actions/ActionsAPI';
|
||||||
* The Open MCT application. This may be configured by installing plugins
|
import AnnotationAPI from './api/annotation/AnnotationAPI';
|
||||||
* or registering extensions before the application is started.
|
import BrandingAPI from './api/Branding';
|
||||||
* @constructor
|
import CompositionAPI from './api/composition/CompositionAPI';
|
||||||
* @memberof module:openmct
|
import EditorAPI from './api/Editor';
|
||||||
*/
|
import FaultManagementAPI from './api/faultmanagement/FaultManagementAPI';
|
||||||
function MCT() {
|
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);
|
EventEmitter.call(this);
|
||||||
|
|
||||||
this.buildInfo = {
|
this.buildInfo = {
|
||||||
version: __OPENMCT_VERSION__,
|
version: __OPENMCT_VERSION__,
|
||||||
buildDate: __OPENMCT_BUILD_DATE__,
|
buildDate: __OPENMCT_BUILD_DATE__,
|
||||||
@ -95,169 +89,140 @@ define([
|
|||||||
|
|
||||||
this.destroy = this.destroy.bind(this);
|
this.destroy = this.destroy.bind(this);
|
||||||
this.defaultClock = 'local';
|
this.defaultClock = 'local';
|
||||||
[
|
|
||||||
/**
|
|
||||||
* Tracks current selection state of the application.
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
['selection', () => new Selection.default(this)],
|
|
||||||
|
|
||||||
/**
|
this.plugins = plugins;
|
||||||
* 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)],
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An interface for interacting with the composition of domain objects.
|
* Tracks current selection state of the application.
|
||||||
* The composition of a domain object is the list of other domain
|
* @private
|
||||||
* objects it "contains" (for instance, that should be displayed
|
*/
|
||||||
* beneath it in the tree.)
|
this.selection = new Selection(this);
|
||||||
*
|
|
||||||
* `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)],
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registry for views of domain objects which should appear in the
|
* MCT's time conductor, which may be used to synchronize view contents
|
||||||
* main viewing area.
|
* for telemetry- or time-based views.
|
||||||
*
|
* @type {module:openmct.TimeConductor}
|
||||||
* @type {module:openmct.ViewRegistry}
|
* @memberof module:openmct.MCT#
|
||||||
* @memberof module:openmct.MCT#
|
* @name conductor
|
||||||
* @name objectViews
|
*/
|
||||||
*/
|
this.time = new TimeAPI(this);
|
||||||
['objectViews', () => new ViewRegistry()],
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registry for views which should appear in the Inspector area.
|
* An interface for interacting with the composition of domain objects.
|
||||||
* These views will be chosen based on the selection state.
|
* The composition of a domain object is the list of other domain
|
||||||
*
|
* objects it "contains" (for instance, that should be displayed
|
||||||
* @type {module:openmct.InspectorViewRegistry}
|
* beneath it in the tree.)
|
||||||
* @memberof module:openmct.MCT#
|
*
|
||||||
* @name inspectorViews
|
* `composition` may be called as a function, in which case it acts
|
||||||
*/
|
* as [`composition.get`]{@link module:openmct.CompositionAPI#get}.
|
||||||
['inspectorViews', () => new InspectorViewRegistry.default()],
|
*
|
||||||
|
* @type {module:openmct.CompositionAPI}
|
||||||
|
* @memberof module:openmct.MCT#
|
||||||
|
* @name composition
|
||||||
|
*/
|
||||||
|
this.composition = new CompositionAPI(this);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registry for views which should appear in Edit Properties
|
* Registry for views of domain objects which should appear in the
|
||||||
* dialogs, and similar user interface elements used for
|
* main viewing area.
|
||||||
* modifying domain objects external to its regular views.
|
*
|
||||||
*
|
* @type {module:openmct.ViewRegistry}
|
||||||
* @type {module:openmct.ViewRegistry}
|
* @memberof module:openmct.MCT#
|
||||||
* @memberof module:openmct.MCT#
|
* @name objectViews
|
||||||
* @name propertyEditors
|
*/
|
||||||
*/
|
this.objectViews = new ViewRegistry();
|
||||||
['propertyEditors', () => new ViewRegistry()],
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registry for views which should appear in the toolbar area while
|
* Registry for views which should appear in the Inspector area.
|
||||||
* editing. These views will be chosen based on the selection state.
|
* These views will be chosen based on the selection state.
|
||||||
*
|
*
|
||||||
* @type {module:openmct.ToolbarRegistry}
|
* @type {module:openmct.InspectorViewRegistry}
|
||||||
* @memberof module:openmct.MCT#
|
* @memberof module:openmct.MCT#
|
||||||
* @name toolbars
|
* @name inspectorViews
|
||||||
*/
|
*/
|
||||||
['toolbars', () => new ToolbarRegistry()],
|
this.inspectorViews = new InspectorViewRegistry();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registry for domain object types which may exist within this
|
* Registry for views which should appear in Edit Properties
|
||||||
* instance of Open MCT.
|
* dialogs, and similar user interface elements used for
|
||||||
*
|
* modifying domain objects external to its regular views.
|
||||||
* @type {module:openmct.TypeRegistry}
|
*
|
||||||
* @memberof module:openmct.MCT#
|
* @type {module:openmct.ViewRegistry}
|
||||||
* @name types
|
* @memberof module:openmct.MCT#
|
||||||
*/
|
* @name propertyEditors
|
||||||
['types', () => new api.TypeRegistry()],
|
*/
|
||||||
|
this.propertyEditors = new ViewRegistry();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An interface for interacting with domain objects and the domain
|
* Registry for views which should appear in the toolbar area while
|
||||||
* object hierarchy.
|
* editing. These views will be chosen based on the selection state.
|
||||||
*
|
*
|
||||||
* @type {module:openmct.ObjectAPI}
|
* @type {module:openmct.ToolbarRegistry}
|
||||||
* @memberof module:openmct.MCT#
|
* @memberof module:openmct.MCT#
|
||||||
* @name objects
|
* @name toolbars
|
||||||
*/
|
*/
|
||||||
['objects', () => new api.ObjectAPI.default(this.types, this)],
|
this.toolbars = new ToolbarRegistry();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An interface for retrieving and interpreting telemetry data associated
|
* Registry for domain object types which may exist within this
|
||||||
* with a domain object.
|
* instance of Open MCT.
|
||||||
*
|
*
|
||||||
* @type {module:openmct.TelemetryAPI}
|
* @type {module:openmct.TypeRegistry}
|
||||||
* @memberof module:openmct.MCT#
|
* @memberof module:openmct.MCT#
|
||||||
* @name telemetry
|
* @name types
|
||||||
*/
|
*/
|
||||||
['telemetry', () => new api.TelemetryAPI.default(this)],
|
this.types = new TypeRegistry();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An interface for creating new indicators and changing them dynamically.
|
* An interface for interacting with domain objects and the domain
|
||||||
*
|
* object hierarchy.
|
||||||
* @type {module:openmct.IndicatorAPI}
|
*
|
||||||
* @memberof module:openmct.MCT#
|
* @type {module:openmct.ObjectAPI}
|
||||||
* @name indicators
|
* @memberof module:openmct.MCT#
|
||||||
*/
|
* @name objects
|
||||||
['indicators', () => new api.IndicatorAPI(this)],
|
*/
|
||||||
|
this.objects = new ObjectAPI(this.types, this);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MCT's user awareness management, to enable user and
|
* An interface for retrieving and interpreting telemetry data associated
|
||||||
* role specific functionality.
|
* with a domain object.
|
||||||
* @type {module:openmct.UserAPI}
|
*
|
||||||
* @memberof module:openmct.MCT#
|
* @type {module:openmct.TelemetryAPI}
|
||||||
* @name user
|
* @memberof module:openmct.MCT#
|
||||||
*/
|
* @name telemetry
|
||||||
['user', () => new api.UserAPI(this)],
|
*/
|
||||||
|
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()],
|
this.notifications = new NotificationAPI();
|
||||||
|
this.editor = new EditorAPI(this);
|
||||||
['tooltips', () => new ToolTipAPI.default()],
|
this.overlays = new OverlayAPI();
|
||||||
|
this.tooltips = new ToolTipAPI();
|
||||||
['menus', () => new api.MenuAPI(this)],
|
this.menus = new MenuAPI(this);
|
||||||
|
this.actions = new ActionsAPI(this);
|
||||||
['actions', () => new api.ActionsAPI(this)],
|
this.status = new StatusAPI(this);
|
||||||
|
this.priority = PriorityAPI;
|
||||||
['status', () => new api.StatusAPI(this)],
|
this.router = new ApplicationRouter(this);
|
||||||
|
this.faults = new FaultManagementAPI(this);
|
||||||
['priority', () => api.PriorityAPI],
|
this.forms = new FormsAPI(this);
|
||||||
|
this.branding = BrandingAPI;
|
||||||
['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
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MCT's annotation API that enables
|
* MCT's annotation API that enables
|
||||||
@ -266,23 +231,23 @@ define([
|
|||||||
* @memberof module:openmct.MCT#
|
* @memberof module:openmct.MCT#
|
||||||
* @name annotation
|
* @name annotation
|
||||||
*/
|
*/
|
||||||
this.annotation = new api.AnnotationAPI(this);
|
this.annotation = new AnnotationAPI(this);
|
||||||
|
|
||||||
// Plugins that are installed by default
|
// Plugins that are installed by default
|
||||||
this.install(this.plugins.Plot());
|
this.install(this.plugins.Plot());
|
||||||
this.install(this.plugins.TelemetryTable.default());
|
this.install(this.plugins.TelemetryTable());
|
||||||
this.install(PreviewPlugin.default());
|
this.install(PreviewPlugin());
|
||||||
this.install(LicensesPlugin.default());
|
this.install(LicensesPlugin());
|
||||||
this.install(RemoveActionPlugin.default());
|
this.install(RemoveActionPlugin());
|
||||||
this.install(MoveActionPlugin.default());
|
this.install(MoveActionPlugin());
|
||||||
this.install(LinkActionPlugin.default());
|
this.install(LinkActionPlugin());
|
||||||
this.install(DuplicateActionPlugin.default());
|
this.install(DuplicateActionPlugin());
|
||||||
this.install(ExportAsJSONAction.default());
|
this.install(ExportAsJSONAction());
|
||||||
this.install(ImportFromJSONAction.default());
|
this.install(ImportFromJSONAction());
|
||||||
this.install(this.plugins.FormActions.default());
|
this.install(this.plugins.FormActions());
|
||||||
this.install(this.plugins.FolderView());
|
this.install(this.plugins.FolderView());
|
||||||
this.install(this.plugins.Tabs());
|
this.install(this.plugins.Tabs());
|
||||||
this.install(ImageryPlugin.default());
|
this.install(ImageryPlugin());
|
||||||
this.install(this.plugins.FlexibleLayout());
|
this.install(this.plugins.FlexibleLayout());
|
||||||
this.install(this.plugins.GoToOriginalAction());
|
this.install(this.plugins.GoToOriginalAction());
|
||||||
this.install(this.plugins.OpenInNewTabAction());
|
this.install(this.plugins.OpenInNewTabAction());
|
||||||
@ -300,26 +265,20 @@ define([
|
|||||||
this.install(this.plugins.Gauge());
|
this.install(this.plugins.Gauge());
|
||||||
this.install(this.plugins.InspectorViews());
|
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.
|
* Set path to where assets are hosted. This should be the path to main.js.
|
||||||
* @memberof module:openmct.MCT#
|
* @memberof module:openmct.MCT#
|
||||||
* @method setAssetPath
|
* @method setAssetPath
|
||||||
*/
|
*/
|
||||||
MCT.prototype.setAssetPath = function (assetPath) {
|
setAssetPath(assetPath) {
|
||||||
this._assetPath = assetPath;
|
this._assetPath = assetPath;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get path to where assets are hosted.
|
* Get path to where assets are hosted.
|
||||||
* @memberof module:openmct.MCT#
|
* @memberof module:openmct.MCT#
|
||||||
* @method getAssetPath
|
* @method getAssetPath
|
||||||
*/
|
*/
|
||||||
MCT.prototype.getAssetPath = function () {
|
getAssetPath() {
|
||||||
const assetPathLength = this._assetPath && this._assetPath.length;
|
const assetPathLength = this._assetPath && this._assetPath.length;
|
||||||
if (!assetPathLength) {
|
if (!assetPathLength) {
|
||||||
return '/';
|
return '/';
|
||||||
@ -330,8 +289,7 @@ define([
|
|||||||
}
|
}
|
||||||
|
|
||||||
return this._assetPath;
|
return this._assetPath;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start running Open MCT. This should be called only after any plugins
|
* Start running Open MCT. This should be called only after any plugins
|
||||||
* have been installed.
|
* have been installed.
|
||||||
@ -341,10 +299,7 @@ define([
|
|||||||
* @param {HTMLElement} [domElement] the DOM element in which to run
|
* @param {HTMLElement} [domElement] the DOM element in which to run
|
||||||
* MCT; if undefined, MCT will be run in the body of the document
|
* MCT; if undefined, MCT will be run in the body of the document
|
||||||
*/
|
*/
|
||||||
MCT.prototype.start = function (
|
start(domElement = document.body.firstElementChild, isHeadlessMode = false) {
|
||||||
domElement = document.body.firstElementChild,
|
|
||||||
isHeadlessMode = false
|
|
||||||
) {
|
|
||||||
// Create element to mount Layout if it doesn't exist
|
// Create element to mount Layout if it doesn't exist
|
||||||
if (domElement === null) {
|
if (domElement === null) {
|
||||||
domElement = document.createElement('div');
|
domElement = document.createElement('div');
|
||||||
@ -376,20 +331,12 @@ define([
|
|||||||
* @event start
|
* @event start
|
||||||
* @memberof module:openmct.MCT~
|
* @memberof module:openmct.MCT~
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (!isHeadlessMode) {
|
if (!isHeadlessMode) {
|
||||||
const appLayout = Vue.createApp({
|
const appLayout = createApp(Layout);
|
||||||
components: {
|
appLayout.provide('openmct', markRaw(this));
|
||||||
Layout: Layout.default
|
|
||||||
},
|
|
||||||
provide: {
|
|
||||||
openmct: Vue.markRaw(this)
|
|
||||||
},
|
|
||||||
template: '<Layout ref="layout"></Layout>'
|
|
||||||
});
|
|
||||||
const component = appLayout.mount(domElement);
|
const component = appLayout.mount(domElement);
|
||||||
component.$nextTick(() => {
|
component.$nextTick(() => {
|
||||||
this.layout = component.$refs.layout;
|
this.layout = component;
|
||||||
this.app = appLayout;
|
this.app = appLayout;
|
||||||
Browse(this);
|
Browse(this);
|
||||||
window.addEventListener('beforeunload', this.destroy);
|
window.addEventListener('beforeunload', this.destroy);
|
||||||
@ -402,14 +349,12 @@ define([
|
|||||||
this.router.start();
|
this.router.start();
|
||||||
this.emit('start');
|
this.emit('start');
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
startHeadless() {
|
||||||
MCT.prototype.startHeadless = function () {
|
|
||||||
let unreachableNode = document.createElement('div');
|
let unreachableNode = document.createElement('div');
|
||||||
|
|
||||||
return this.start(unreachableNode, true);
|
return this.start(unreachableNode, true);
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Install a plugin in MCT.
|
* Install a plugin in MCT.
|
||||||
*
|
*
|
||||||
@ -417,17 +362,13 @@ define([
|
|||||||
* invoked with the mct instance.
|
* invoked with the mct instance.
|
||||||
* @memberof module:openmct.MCT#
|
* @memberof module:openmct.MCT#
|
||||||
*/
|
*/
|
||||||
MCT.prototype.install = function (plugin) {
|
install(plugin) {
|
||||||
plugin(this);
|
plugin(this);
|
||||||
};
|
}
|
||||||
|
|
||||||
MCT.prototype.destroy = function () {
|
destroy() {
|
||||||
window.removeEventListener('beforeunload', this.destroy);
|
window.removeEventListener('beforeunload', this.destroy);
|
||||||
this.emit('destroy');
|
this.emit('destroy');
|
||||||
this.router.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.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define(['./plugins/plugins', 'utils/testing'], function (plugins, testUtils) {
|
import * as testUtils from 'utils/testing';
|
||||||
describe('MCT', function () {
|
|
||||||
let openmct;
|
|
||||||
let mockPlugin;
|
|
||||||
let mockPlugin2;
|
|
||||||
let mockListener;
|
|
||||||
|
|
||||||
beforeEach(function () {
|
import plugins from './plugins/plugins';
|
||||||
mockPlugin = jasmine.createSpy('plugin');
|
|
||||||
mockPlugin2 = jasmine.createSpy('plugin2');
|
|
||||||
mockListener = jasmine.createSpy('listener');
|
|
||||||
|
|
||||||
openmct = testUtils.createOpenMct();
|
describe('MCT', function () {
|
||||||
|
let openmct;
|
||||||
|
let mockPlugin;
|
||||||
|
let mockPlugin2;
|
||||||
|
let mockListener;
|
||||||
|
|
||||||
openmct.install(mockPlugin);
|
beforeEach(function () {
|
||||||
openmct.install(mockPlugin2);
|
mockPlugin = jasmine.createSpy('plugin');
|
||||||
openmct.on('start', mockListener);
|
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.
|
it('calls plugins for configuration', function () {
|
||||||
afterEach(function () {
|
expect(mockPlugin).toHaveBeenCalledWith(openmct);
|
||||||
return testUtils.resetApplicationState(openmct);
|
expect(mockPlugin2).toHaveBeenCalledWith(openmct);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('exposes plugins', function () {
|
it('emits a start event', function () {
|
||||||
expect(openmct.plugins).toEqual(plugins);
|
expect(mockListener).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not issue a start event before started', function () {
|
it('Renders the application into the provided container element', function () {
|
||||||
expect(mockListener).not.toHaveBeenCalled();
|
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 () {
|
it('calls plugins for configuration', function () {
|
||||||
let appHolder;
|
expect(mockPlugin).toHaveBeenCalledWith(openmct);
|
||||||
beforeEach(function (done) {
|
expect(mockPlugin2).toHaveBeenCalledWith(openmct);
|
||||||
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);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('startHeadless', function () {
|
it('emits a start event', function () {
|
||||||
beforeEach(function (done) {
|
expect(mockListener).toHaveBeenCalled();
|
||||||
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);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('setAssetPath', function () {
|
it('Does not render Open MCT', function () {
|
||||||
let testAssetPath;
|
let openMctShellElements = document.body.querySelectorAll('div.l-shell');
|
||||||
|
expect(openMctShellElements.length).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('configures the path for assets', function () {
|
describe('setAssetPath', function () {
|
||||||
testAssetPath = 'some/path/';
|
let testAssetPath;
|
||||||
openmct.setAssetPath(testAssetPath);
|
|
||||||
expect(openmct.getAssetPath()).toBe(testAssetPath);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('adds a trailing /', function () {
|
it('configures the path for assets', function () {
|
||||||
testAssetPath = 'some/path';
|
testAssetPath = 'some/path/';
|
||||||
openmct.setAssetPath(testAssetPath);
|
openmct.setAssetPath(testAssetPath);
|
||||||
expect(openmct.getAssetPath()).toBe(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.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define([
|
import ActionsAPI from './actions/ActionsAPI';
|
||||||
'./actions/ActionsAPI',
|
import AnnotationAPI from './annotation/AnnotationAPI';
|
||||||
'./composition/CompositionAPI',
|
import CompositionAPI from './composition/CompositionAPI';
|
||||||
'./Editor',
|
import EditorAPI from './Editor';
|
||||||
'./faultmanagement/FaultManagementAPI',
|
import FaultManagementAPI from './faultmanagement/FaultManagementAPI';
|
||||||
'./forms/FormsAPI',
|
import FormsAPI from './forms/FormsAPI';
|
||||||
'./indicators/IndicatorAPI',
|
import IndicatorAPI from './indicators/IndicatorAPI';
|
||||||
'./menu/MenuAPI',
|
import MenuAPI from './menu/MenuAPI';
|
||||||
'./notifications/NotificationAPI',
|
import NotificationAPI from './notifications/NotificationAPI';
|
||||||
'./objects/ObjectAPI',
|
import ObjectAPI from './objects/ObjectAPI';
|
||||||
'./priority/PriorityAPI',
|
import PriorityAPI from './priority/PriorityAPI';
|
||||||
'./status/StatusAPI',
|
import StatusAPI from './status/StatusAPI';
|
||||||
'./telemetry/TelemetryAPI',
|
import TelemetryAPI from './telemetry/TelemetryAPI';
|
||||||
'./time/TimeAPI',
|
import TimeAPI from './time/TimeAPI';
|
||||||
'./types/TypeRegistry',
|
import TypeRegistry from './types/TypeRegistry';
|
||||||
'./user/UserAPI',
|
import UserAPI from './user/UserAPI';
|
||||||
'./annotation/AnnotationAPI'
|
|
||||||
], function (
|
export default {
|
||||||
ActionsAPI,
|
ActionsAPI,
|
||||||
CompositionAPI,
|
CompositionAPI,
|
||||||
EditorAPI,
|
EditorAPI,
|
||||||
@ -54,23 +54,4 @@ define([
|
|||||||
TypeRegistry,
|
TypeRegistry,
|
||||||
UserAPI,
|
UserAPI,
|
||||||
AnnotationAPI
|
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
|
<div
|
||||||
v-for="section in formSections"
|
v-for="section in formSections"
|
||||||
:key="section.id"
|
:key="section.id"
|
||||||
|
:aria-labelledby="'sectionTitle'"
|
||||||
class="c-form__section"
|
class="c-form__section"
|
||||||
:class="section.cssClass"
|
: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 }}
|
{{ section.name }}
|
||||||
</h2>
|
</h2>
|
||||||
<FormRow
|
<FormRow
|
||||||
|
@ -20,163 +20,161 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define([], function () {
|
/**
|
||||||
/**
|
* Utility for checking if a thing is an Open MCT Identifier.
|
||||||
* Utility for checking if a thing is an Open MCT Identifier.
|
* @private
|
||||||
* @private
|
*/
|
||||||
*/
|
function isIdentifier(thing) {
|
||||||
function isIdentifier(thing) {
|
return (
|
||||||
return (
|
typeof thing === 'object' &&
|
||||||
typeof thing === 'object' &&
|
Object.prototype.hasOwnProperty.call(thing, 'key') &&
|
||||||
Object.prototype.hasOwnProperty.call(thing, 'key') &&
|
Object.prototype.hasOwnProperty.call(thing, 'namespace')
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
let namespace = '';
|
||||||
* Utility for checking if a thing is a key string. Not perfect.
|
let key = keyString;
|
||||||
* @private
|
for (let i = 0; i < key.length; i++) {
|
||||||
*/
|
if (key[i] === '\\' && key[i + 1] === ':') {
|
||||||
function isKeyString(thing) {
|
i++; // skip escape character.
|
||||||
return typeof thing === 'string';
|
} else if (key[i] === ':') {
|
||||||
|
key = key.slice(i + 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace += key[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
if (keyString === namespace) {
|
||||||
* Convert a keyString into an Open MCT Identifier, ex:
|
namespace = '';
|
||||||
* '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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isIdentifier: isIdentifier,
|
namespace: namespace,
|
||||||
toOldFormat: toOldFormat,
|
key: key
|
||||||
toNewFormat: toNewFormat,
|
|
||||||
makeKeyString: makeKeyString,
|
|
||||||
parseKeyString: parseKeyString,
|
|
||||||
equals: objectEquals,
|
|
||||||
identifierEquals: identifierEquals,
|
|
||||||
refresh: refresh
|
|
||||||
};
|
};
|
||||||
});
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
import objectUtils from 'objectUtils';
|
||||||
describe('objectUtils', function () {
|
|
||||||
describe('keyString util', function () {
|
describe('objectUtils', function () {
|
||||||
const EXPECTATIONS = {
|
describe('keyString util', function () {
|
||||||
ROOT: {
|
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: '',
|
namespace: '',
|
||||||
key: 'ROOT'
|
key: 'objId'
|
||||||
},
|
|
||||||
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('translates composition', function () {
|
||||||
it('translate ids', function () {
|
expect(
|
||||||
expect(
|
objectUtils.toNewFormat(
|
||||||
objectUtils.toNewFormat(
|
{
|
||||||
{
|
prop: 'someValue',
|
||||||
prop: 'someValue'
|
composition: ['anotherObjectId', 'scratch:anotherObjectId']
|
||||||
},
|
},
|
||||||
'objId'
|
'objId'
|
||||||
)
|
)
|
||||||
).toEqual({
|
).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',
|
prop: 'someValue',
|
||||||
identifier: {
|
identifier: {
|
||||||
namespace: '',
|
namespace: '',
|
||||||
key: 'objId'
|
key: 'objId'
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
).toEqual({
|
||||||
|
prop: 'someValue'
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('translates composition', function () {
|
it('translates composition', function () {
|
||||||
expect(
|
expect(
|
||||||
objectUtils.toNewFormat(
|
objectUtils.toOldFormat({
|
||||||
{
|
|
||||||
prop: 'someValue',
|
|
||||||
composition: ['anotherObjectId', 'scratch:anotherObjectId']
|
|
||||||
},
|
|
||||||
'objId'
|
|
||||||
)
|
|
||||||
).toEqual({
|
|
||||||
prop: 'someValue',
|
prop: 'someValue',
|
||||||
composition: [
|
composition: [
|
||||||
{
|
{
|
||||||
@ -99,48 +137,10 @@ define(['objectUtils'], function (objectUtils) {
|
|||||||
namespace: '',
|
namespace: '',
|
||||||
key: 'objId'
|
key: 'objId'
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
});
|
).toEqual({
|
||||||
});
|
prop: 'someValue',
|
||||||
|
composition: ['anotherObjectId', 'scratch:anotherObjectId']
|
||||||
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']
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -20,17 +20,19 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define(['lodash'], function (_) {
|
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
|
* This is the default metadata provider; for any object with a "telemetry"
|
||||||
* telemetry metadata.
|
* 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
|
* This provider also implements legacy support for telemetry metadata
|
||||||
* depreciated in the future.
|
* defined on the type. Telemetry metadata definitions on type will be
|
||||||
*/
|
* depreciated in the future.
|
||||||
function DefaultMetadataProvider(openmct) {
|
*/
|
||||||
|
export default class DefaultMetadataProvider {
|
||||||
|
constructor(openmct) {
|
||||||
this.openmct = openmct;
|
this.openmct = openmct;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,65 +40,14 @@ define(['lodash'], function (_) {
|
|||||||
* Applies to any domain object with a telemetry property, or whose type
|
* Applies to any domain object with a telemetry property, or whose type
|
||||||
* definition has a telemetry property.
|
* definition has a telemetry property.
|
||||||
*/
|
*/
|
||||||
DefaultMetadataProvider.prototype.supportsMetadata = function (domainObject) {
|
supportsMetadata(domainObject) {
|
||||||
return Boolean(domainObject.telemetry) || Boolean(this.typeHasTelemetry(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.
|
* Returns telemetry metadata for a given domain object.
|
||||||
*/
|
*/
|
||||||
DefaultMetadataProvider.prototype.getMetadata = function (domainObject) {
|
getMetadata(domainObject) {
|
||||||
const metadata = domainObject.telemetry || {};
|
const metadata = domainObject.telemetry || {};
|
||||||
if (this.typeHasTelemetry(domainObject)) {
|
if (this.typeHasTelemetry(domainObject)) {
|
||||||
const typeMetadata = this.openmct.types.get(domainObject.type).definition.telemetry;
|
const typeMetadata = this.openmct.types.get(domainObject.type).definition.telemetry;
|
||||||
@ -109,16 +60,65 @@ define(['lodash'], function (_) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return metadata;
|
return metadata;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
DefaultMetadataProvider.prototype.typeHasTelemetry = function (domainObject) {
|
typeHasTelemetry(domainObject) {
|
||||||
const type = this.openmct.types.get(domainObject.type);
|
const type = this.openmct.types.get(domainObject.type);
|
||||||
|
|
||||||
return Boolean(type.definition.telemetry);
|
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.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define(['lodash'], function (_) {
|
import _ from 'lodash';
|
||||||
function applyReasonableDefaults(valueMetadata, index) {
|
|
||||||
valueMetadata.source = valueMetadata.source || valueMetadata.key;
|
|
||||||
valueMetadata.hints = valueMetadata.hints || {};
|
|
||||||
|
|
||||||
if (Object.prototype.hasOwnProperty.call(valueMetadata.hints, 'x')) {
|
function applyReasonableDefaults(valueMetadata, index) {
|
||||||
if (!Object.prototype.hasOwnProperty.call(valueMetadata.hints, 'domain')) {
|
valueMetadata.source = valueMetadata.source || valueMetadata.key;
|
||||||
valueMetadata.hints.domain = valueMetadata.hints.x;
|
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')) {
|
delete valueMetadata.hints.x;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
if (Object.prototype.hasOwnProperty.call(valueMetadata.hints, 'y')) {
|
||||||
* Utility class for handling and inspecting telemetry metadata. Applies
|
if (!Object.prototype.hasOwnProperty.call(valueMetadata.hints, 'range')) {
|
||||||
* reasonable defaults to simplify the task of providing metadata, while
|
valueMetadata.hints.range = valueMetadata.hints.y;
|
||||||
* also providing methods for interrogating telemetry metadata.
|
}
|
||||||
*/
|
|
||||||
function TelemetryMetadataManager(metadata) {
|
|
||||||
this.metadata = metadata;
|
|
||||||
|
|
||||||
this.valueMetadatas = this.metadata.values
|
delete valueMetadata.hints.y;
|
||||||
? this.metadata.values.map(applyReasonableDefaults)
|
|
||||||
: [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
if (valueMetadata.format === 'enum') {
|
||||||
* Get value metadata for a single key.
|
if (!valueMetadata.values) {
|
||||||
*/
|
valueMetadata.values = valueMetadata.enumerations.map((e) => e.value);
|
||||||
TelemetryMetadataManager.prototype.value = function (key) {
|
}
|
||||||
return this.valueMetadatas.filter(function (metadata) {
|
|
||||||
return metadata.key === key;
|
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];
|
})[0];
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
if (valueMetadata === undefined) {
|
||||||
* Returns all value metadatas, sorted by priority.
|
valueMetadata = this.values()[0];
|
||||||
*/
|
}
|
||||||
TelemetryMetadataManager.prototype.values = function () {
|
|
||||||
return this.valuesForHints(['priority']);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
return valueMetadata;
|
||||||
* 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;
|
|
||||||
});
|
|
||||||
|
@ -20,96 +20,92 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define([], function () {
|
// Set of connection states; changing among these states will be
|
||||||
// Set of connection states; changing among these states will be
|
// reflected in the indicator's appearance.
|
||||||
// reflected in the indicator's appearance.
|
// CONNECTED: Everything nominal, expect to be able to read/write.
|
||||||
// CONNECTED: Everything nominal, expect to be able to read/write.
|
// DISCONNECTED: HTTP failed; maybe misconfigured, disconnected.
|
||||||
// DISCONNECTED: HTTP failed; maybe misconfigured, disconnected.
|
// PENDING: Still trying to connect, and haven't failed yet.
|
||||||
// PENDING: Still trying to connect, and haven't failed yet.
|
const CONNECTED = {
|
||||||
const CONNECTED = {
|
statusClass: 's-status-on'
|
||||||
statusClass: 's-status-on'
|
};
|
||||||
};
|
const PENDING = {
|
||||||
const PENDING = {
|
statusClass: 's-status-warning-lo'
|
||||||
statusClass: 's-status-warning-lo'
|
};
|
||||||
};
|
const DISCONNECTED = {
|
||||||
const DISCONNECTED = {
|
statusClass: 's-status-warning-hi'
|
||||||
statusClass: 's-status-warning-hi'
|
};
|
||||||
};
|
export default function URLIndicator(options, simpleIndicator) {
|
||||||
function URLIndicator(options, simpleIndicator) {
|
this.bindMethods();
|
||||||
this.bindMethods();
|
this.count = 0;
|
||||||
this.count = 0;
|
|
||||||
|
|
||||||
this.indicator = simpleIndicator;
|
this.indicator = simpleIndicator;
|
||||||
this.setDefaultsFromOptions(options);
|
this.setDefaultsFromOptions(options);
|
||||||
this.setIndicatorToState(PENDING);
|
this.setIndicatorToState(PENDING);
|
||||||
|
|
||||||
this.fetchUrl();
|
this.fetchUrl();
|
||||||
setInterval(this.fetchUrl, this.interval);
|
setInterval(this.fetchUrl, this.interval);
|
||||||
}
|
}
|
||||||
|
|
||||||
URLIndicator.prototype.setIndicatorToState = function (state) {
|
URLIndicator.prototype.setIndicatorToState = function (state) {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case CONNECTED: {
|
case CONNECTED: {
|
||||||
this.indicator.text(this.label + ' is connected');
|
this.indicator.text(this.label + ' is connected');
|
||||||
this.indicator.description(
|
this.indicator.description(
|
||||||
this.label + ' is online, checking status every ' + this.interval + ' milliseconds.'
|
this.label + ' is online, checking status every ' + this.interval + ' milliseconds.'
|
||||||
);
|
);
|
||||||
break;
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 () {
|
case DISCONNECTED: {
|
||||||
fetch(this.URLpath)
|
this.indicator.text(this.label + ' is offline');
|
||||||
.then((response) => {
|
this.indicator.description(
|
||||||
if (response.ok) {
|
this.label + ' is offline, checking status every ' + this.interval + ' milliseconds'
|
||||||
this.handleSuccess();
|
);
|
||||||
} else {
|
break;
|
||||||
this.handleError();
|
}
|
||||||
}
|
}
|
||||||
})
|
|
||||||
.catch((error) => {
|
this.indicator.statusClass(state.statusClass);
|
||||||
|
};
|
||||||
|
|
||||||
|
URLIndicator.prototype.fetchUrl = function () {
|
||||||
|
fetch(this.URLpath)
|
||||||
|
.then((response) => {
|
||||||
|
if (response.ok) {
|
||||||
|
this.handleSuccess();
|
||||||
|
} else {
|
||||||
this.handleError();
|
this.handleError();
|
||||||
});
|
}
|
||||||
};
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
this.handleError();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
URLIndicator.prototype.handleError = function (e) {
|
URLIndicator.prototype.handleError = function (e) {
|
||||||
this.setIndicatorToState(DISCONNECTED);
|
this.setIndicatorToState(DISCONNECTED);
|
||||||
};
|
};
|
||||||
|
|
||||||
URLIndicator.prototype.handleSuccess = function () {
|
URLIndicator.prototype.handleSuccess = function () {
|
||||||
this.setIndicatorToState(CONNECTED);
|
this.setIndicatorToState(CONNECTED);
|
||||||
};
|
};
|
||||||
|
|
||||||
URLIndicator.prototype.setDefaultsFromOptions = function (options) {
|
URLIndicator.prototype.setDefaultsFromOptions = function (options) {
|
||||||
this.URLpath = options.url;
|
this.URLpath = options.url;
|
||||||
this.label = options.label || options.url;
|
this.label = options.label || options.url;
|
||||||
this.interval = options.interval || 10000;
|
this.interval = options.interval || 10000;
|
||||||
this.indicator.iconClass(options.iconClass || 'icon-chain-links');
|
this.indicator.iconClass(options.iconClass || 'icon-chain-links');
|
||||||
};
|
};
|
||||||
|
|
||||||
URLIndicator.prototype.bindMethods = function () {
|
URLIndicator.prototype.bindMethods = function () {
|
||||||
this.fetchUrl = this.fetchUrl.bind(this);
|
this.fetchUrl = this.fetchUrl.bind(this);
|
||||||
this.handleSuccess = this.handleSuccess.bind(this);
|
this.handleSuccess = this.handleSuccess.bind(this);
|
||||||
this.handleError = this.handleError.bind(this);
|
this.handleError = this.handleError.bind(this);
|
||||||
this.setIndicatorToState = this.setIndicatorToState.bind(this);
|
this.setIndicatorToState = this.setIndicatorToState.bind(this);
|
||||||
};
|
};
|
||||||
|
|
||||||
return URLIndicator;
|
|
||||||
});
|
|
||||||
|
@ -19,15 +19,15 @@
|
|||||||
* this source code distribution or the Licensing information page available
|
* this source code distribution or the Licensing information page available
|
||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
define(['./URLIndicator'], function URLIndicatorPlugin(URLIndicator) {
|
import URLIndicator from './URLIndicator';
|
||||||
return function (opts) {
|
|
||||||
return function install(openmct) {
|
|
||||||
const simpleIndicator = openmct.indicators.simpleIndicator();
|
|
||||||
const urlIndicator = new URLIndicator(opts, simpleIndicator);
|
|
||||||
|
|
||||||
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.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define(['utils/testing', './URLIndicator', './URLIndicatorPlugin', '../../MCT'], function (
|
import * as testingUtils from 'utils/testing';
|
||||||
testingUtils,
|
|
||||||
URLIndicator,
|
|
||||||
URLIndicatorPlugin,
|
|
||||||
MCT
|
|
||||||
) {
|
|
||||||
describe('The URLIndicator', function () {
|
|
||||||
let openmct;
|
|
||||||
let indicatorElement;
|
|
||||||
let pluginOptions;
|
|
||||||
let urlIndicator; // eslint-disable-line
|
|
||||||
let fetchSpy;
|
|
||||||
|
|
||||||
beforeEach(function () {
|
import URLIndicatorPlugin from './URLIndicatorPlugin';
|
||||||
jasmine.clock().install();
|
|
||||||
openmct = new testingUtils.createOpenMct();
|
|
||||||
spyOn(openmct.indicators, 'add');
|
|
||||||
fetchSpy = spyOn(window, 'fetch').and.callFake(() =>
|
|
||||||
Promise.resolve({
|
|
||||||
ok: true
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(function () {
|
describe('The URLIndicator', function () {
|
||||||
if (window.fetch.restore) {
|
let openmct;
|
||||||
window.fetch.restore();
|
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 () {
|
jasmine.clock().uninstall();
|
||||||
describe('with default options', function () {
|
|
||||||
beforeEach(function () {
|
|
||||||
pluginOptions = {
|
|
||||||
url: 'someURL'
|
|
||||||
};
|
|
||||||
urlIndicator = URLIndicatorPlugin(pluginOptions)(openmct);
|
|
||||||
indicatorElement = openmct.indicators.add.calls.mostRecent().args[0].element;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('has a default icon class if none supplied', function () {
|
return testingUtils.resetApplicationState(openmct);
|
||||||
expect(indicatorElement.classList.contains('icon-chain-links')).toBe(true);
|
});
|
||||||
});
|
|
||||||
|
|
||||||
it('defaults to the URL if no label supplied', function () {
|
describe('on initialization', function () {
|
||||||
expect(indicatorElement.textContent.indexOf(pluginOptions.url) >= 0).toBe(true);
|
describe('with default options', function () {
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
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 () {
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
pluginOptions = {
|
pluginOptions = {
|
||||||
url: 'someURL',
|
url: 'someURL'
|
||||||
interval: 100
|
|
||||||
};
|
};
|
||||||
urlIndicator = URLIndicatorPlugin(pluginOptions)(openmct);
|
urlIndicator = URLIndicatorPlugin(pluginOptions)(openmct);
|
||||||
indicatorElement = openmct.indicators.add.calls.mostRecent().args[0].element;
|
indicatorElement = openmct.indicators.add.calls.mostRecent().args[0].element;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('requests the provided URL', function () {
|
it('has a default icon class if none supplied', function () {
|
||||||
jasmine.clock().tick(pluginOptions.interval + 1);
|
expect(indicatorElement.classList.contains('icon-chain-links')).toBe(true);
|
||||||
expect(window.fetch).toHaveBeenCalledWith(pluginOptions.url);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('indicates success if connection is nominal', async function () {
|
it('defaults to the URL if no label supplied', function () {
|
||||||
jasmine.clock().tick(pluginOptions.interval + 1);
|
expect(indicatorElement.textContent.indexOf(pluginOptions.url) >= 0).toBe(true);
|
||||||
await urlIndicator.fetchUrl();
|
});
|
||||||
expect(indicatorElement.classList.contains('s-status-on')).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 () {
|
it('uses the custom iconClass', function () {
|
||||||
fetchSpy.and.callFake(() =>
|
expect(indicatorElement.classList.contains('iconClass-checked')).toBe(true);
|
||||||
Promise.resolve({
|
});
|
||||||
ok: false
|
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);
|
jasmine.clock().tick(pluginOptions.interval + 1);
|
||||||
await urlIndicator.fetchUrl();
|
expect(window.fetch).toHaveBeenCalledTimes(2);
|
||||||
expect(indicatorElement.classList.contains('s-status-warning-hi')).toBe(true);
|
});
|
||||||
|
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.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define([], function () {
|
/**
|
||||||
/**
|
* Constant values used by the Autoflow Tabular View.
|
||||||
* Constant values used by the Autoflow Tabular View.
|
*/
|
||||||
*/
|
export default {
|
||||||
return {
|
ROW_HEIGHT: 16,
|
||||||
ROW_HEIGHT: 16,
|
SLIDER_HEIGHT: 10,
|
||||||
SLIDER_HEIGHT: 10,
|
INITIAL_COLUMN_WIDTH: 225,
|
||||||
INITIAL_COLUMN_WIDTH: 225,
|
MAX_COLUMN_WIDTH: 525,
|
||||||
MAX_COLUMN_WIDTH: 525,
|
COLUMN_WIDTH_STEP: 25
|
||||||
COLUMN_WIDTH_STEP: 25
|
};
|
||||||
};
|
|
||||||
});
|
|
||||||
|
@ -20,104 +20,102 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define(['./AutoflowTabularRowController'], function (AutoflowTabularRowController) {
|
import AutoflowTabularRowController from './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;
|
|
||||||
|
|
||||||
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.rows = {};
|
||||||
this.removeRow = this.removeRow.bind(this);
|
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.
|
* Respond to an `remove` event from composition by removing any
|
||||||
* @param {String} value the value to display
|
* related row.
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
AutoflowTabularController.prototype.trackLastUpdated = function (value) {
|
AutoflowTabularController.prototype.removeRow = function (identifier) {
|
||||||
this.data.updated = value;
|
const id = [identifier.namespace, identifier.key].join(':');
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
if (this.rows[id]) {
|
||||||
* Respond to an `add` event from composition by adding a new row.
|
this.data.items = this.data.items.filter(
|
||||||
* @private
|
function (item) {
|
||||||
*/
|
return item !== this.rows[id];
|
||||||
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();
|
|
||||||
}.bind(this)
|
}.bind(this)
|
||||||
);
|
);
|
||||||
this.controllers = {};
|
this.controllers[id].destroy();
|
||||||
this.composition.off('add', this.addRow);
|
delete this.controllers[id];
|
||||||
this.composition.off('remove', this.removeRow);
|
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.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define(['./AutoflowTabularView'], function (AutoflowTabularView) {
|
import AutoflowTabularView from './AutoflowTabularView';
|
||||||
return function (options) {
|
|
||||||
return function (openmct) {
|
|
||||||
const views = openmct.mainViews || openmct.objectViews;
|
|
||||||
|
|
||||||
views.addProvider({
|
export default function (options) {
|
||||||
name: 'Autoflow Tabular',
|
return function (openmct) {
|
||||||
key: 'autoflow',
|
const views = openmct.mainViews || openmct.objectViews;
|
||||||
cssClass: 'icon-packet',
|
|
||||||
description: 'A tabular view of packet contents.',
|
views.addProvider({
|
||||||
canView: function (d) {
|
name: 'Autoflow Tabular',
|
||||||
return !options || options.type === d.type;
|
key: 'autoflow',
|
||||||
},
|
cssClass: 'icon-packet',
|
||||||
view: function (domainObject) {
|
description: 'A tabular view of packet contents.',
|
||||||
return new AutoflowTabularView(domainObject, openmct, document);
|
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.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define([], function () {
|
/**
|
||||||
/**
|
* Controller for individual rows of an Autoflow Tabular View.
|
||||||
* Controller for individual rows of an Autoflow Tabular View.
|
* Subscribes to telemetry and updates row data.
|
||||||
* Subscribes to telemetry and updates row data.
|
*
|
||||||
*
|
* @param {DomainObject} domainObject the object being viewed
|
||||||
* @param {DomainObject} domainObject the object being viewed
|
* @param {*} data the view data
|
||||||
* @param {*} data the view data
|
* @param openmct a reference to the openmct application
|
||||||
* @param openmct a reference to the openmct application
|
* @param {Function} callback a callback to invoke with "last updated" timestamps
|
||||||
* @param {Function} callback a callback to invoke with "last updated" timestamps
|
*/
|
||||||
*/
|
export default function AutoflowTabularRowController(domainObject, data, openmct, callback) {
|
||||||
function AutoflowTabularRowController(domainObject, data, openmct, callback) {
|
this.domainObject = domainObject;
|
||||||
this.domainObject = domainObject;
|
this.data = data;
|
||||||
this.data = data;
|
this.openmct = openmct;
|
||||||
this.openmct = openmct;
|
this.callback = callback;
|
||||||
this.callback = callback;
|
|
||||||
|
|
||||||
this.metadata = this.openmct.telemetry.getMetadata(this.domainObject);
|
this.metadata = this.openmct.telemetry.getMetadata(this.domainObject);
|
||||||
this.ranges = this.metadata.valuesForHints(['range']);
|
this.ranges = this.metadata.valuesForHints(['range']);
|
||||||
this.domains = this.metadata.valuesForHints(['domain']);
|
this.domains = this.metadata.valuesForHints(['domain']);
|
||||||
this.rangeFormatter = this.openmct.telemetry.getValueFormatter(this.ranges[0]);
|
this.rangeFormatter = this.openmct.telemetry.getValueFormatter(this.ranges[0]);
|
||||||
this.domainFormatter = this.openmct.telemetry.getValueFormatter(this.domains[0]);
|
this.domainFormatter = this.openmct.telemetry.getValueFormatter(this.domains[0]);
|
||||||
this.evaluator = this.openmct.telemetry.limitEvaluator(this.domainObject);
|
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.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define([
|
import autoflowTemplate from './autoflow-tabular.html';
|
||||||
'./AutoflowTabularController',
|
import AutoflowTabularConstants from './AutoflowTabularConstants';
|
||||||
'./AutoflowTabularConstants',
|
import AutoflowTabularController from './AutoflowTabularController';
|
||||||
'./VueView',
|
import VueView from './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;
|
|
||||||
|
|
||||||
/**
|
const ROW_HEIGHT = AutoflowTabularConstants.ROW_HEIGHT;
|
||||||
* Implements the Autoflow Tabular view of a domain object.
|
const SLIDER_HEIGHT = AutoflowTabularConstants.SLIDER_HEIGHT;
|
||||||
*/
|
const INITIAL_COLUMN_WIDTH = AutoflowTabularConstants.INITIAL_COLUMN_WIDTH;
|
||||||
function AutoflowTabularView(domainObject, openmct) {
|
const MAX_COLUMN_WIDTH = AutoflowTabularConstants.MAX_COLUMN_WIDTH;
|
||||||
const data = {
|
const COLUMN_WIDTH_STEP = AutoflowTabularConstants.COLUMN_WIDTH_STEP;
|
||||||
items: [],
|
|
||||||
columns: [],
|
|
||||||
width: INITIAL_COLUMN_WIDTH,
|
|
||||||
filter: '',
|
|
||||||
updated: 'No updates',
|
|
||||||
rowCount: 1
|
|
||||||
};
|
|
||||||
const controller = new AutoflowTabularController(domainObject, data, openmct);
|
|
||||||
let interval;
|
|
||||||
|
|
||||||
VueView.call(this, {
|
/**
|
||||||
data: data,
|
* Implements the Autoflow Tabular view of a domain object.
|
||||||
methods: {
|
*/
|
||||||
increaseColumnWidth: function () {
|
export default function AutoflowTabularView(domainObject, openmct) {
|
||||||
data.width += COLUMN_WIDTH_STEP;
|
const data = {
|
||||||
data.width = data.width > MAX_COLUMN_WIDTH ? INITIAL_COLUMN_WIDTH : data.width;
|
items: [],
|
||||||
},
|
columns: [],
|
||||||
reflow: function () {
|
width: INITIAL_COLUMN_WIDTH,
|
||||||
let column = [];
|
filter: '',
|
||||||
let index = 0;
|
updated: 'No updates',
|
||||||
const filteredItems = data.items.filter(function (item) {
|
rowCount: 1
|
||||||
return item.name.toLowerCase().indexOf(data.filter.toLowerCase()) !== -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) {
|
data.columns = [];
|
||||||
if (column.length >= data.rowCount) {
|
|
||||||
data.columns.push(column);
|
|
||||||
column = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
column.push(filteredItems[index]);
|
while (index < filteredItems.length) {
|
||||||
index += 1;
|
if (column.length >= data.rowCount) {
|
||||||
}
|
|
||||||
|
|
||||||
if (column.length > 0) {
|
|
||||||
data.columns.push(column);
|
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) {
|
if (column.length > 0) {
|
||||||
clearInterval(interval);
|
data.columns.push(column);
|
||||||
interval = undefined;
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
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';
|
import mount from 'utils/mount';
|
||||||
export default function () {
|
export default function () {
|
||||||
return function VueView(options) {
|
class VueView {
|
||||||
const { vNode, destroy } = mount(options);
|
constructor(options) {
|
||||||
|
const { vNode, destroy } = mount(options);
|
||||||
|
this.show = function (container) {
|
||||||
|
container.appendChild(vNode.el);
|
||||||
|
};
|
||||||
|
this.destroy = destroy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.show = function (container) {
|
return VueView;
|
||||||
container.appendChild(vNode.el);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.destroy = destroy;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
@ -20,44 +20,40 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define([], function () {
|
export default function DOMObserver(element) {
|
||||||
function DOMObserver(element) {
|
this.element = element;
|
||||||
this.element = element;
|
this.observers = [];
|
||||||
this.observers = [];
|
}
|
||||||
}
|
|
||||||
|
|
||||||
DOMObserver.prototype.when = function (latchFunction) {
|
DOMObserver.prototype.when = function (latchFunction) {
|
||||||
return new Promise(
|
return new Promise(
|
||||||
function (resolve, reject) {
|
function (resolve, reject) {
|
||||||
//Test latch function at least once
|
//Test latch function at least once
|
||||||
if (latchFunction()) {
|
if (latchFunction()) {
|
||||||
resolve();
|
resolve();
|
||||||
} else {
|
} else {
|
||||||
//Latch condition not true yet, create observer on DOM and test again on change.
|
//Latch condition not true yet, create observer on DOM and test again on change.
|
||||||
const config = {
|
const config = {
|
||||||
attributes: true,
|
attributes: true,
|
||||||
childList: true,
|
childList: true,
|
||||||
subtree: true
|
subtree: true
|
||||||
};
|
};
|
||||||
const observer = new MutationObserver(function () {
|
const observer = new MutationObserver(function () {
|
||||||
if (latchFunction()) {
|
if (latchFunction()) {
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
observer.observe(this.element, config);
|
observer.observe(this.element, config);
|
||||||
this.observers.push(observer);
|
this.observers.push(observer);
|
||||||
}
|
}
|
||||||
}.bind(this)
|
}.bind(this)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
DOMObserver.prototype.destroy = function () {
|
DOMObserver.prototype.destroy = function () {
|
||||||
this.observers.forEach(
|
this.observers.forEach(
|
||||||
function (observer) {
|
function (observer) {
|
||||||
observer.disconnect();
|
observer.disconnect();
|
||||||
}.bind(this)
|
}.bind(this)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
return DOMObserver;
|
|
||||||
});
|
|
||||||
|
@ -20,53 +20,49 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define(function () {
|
export default function DisplayLayoutType() {
|
||||||
function DisplayLayoutType() {
|
return {
|
||||||
return {
|
name: 'Display Layout',
|
||||||
name: 'Display Layout',
|
creatable: true,
|
||||||
creatable: true,
|
description:
|
||||||
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.',
|
||||||
'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',
|
||||||
cssClass: 'icon-layout',
|
initialize(domainObject) {
|
||||||
initialize(domainObject) {
|
domainObject.composition = [];
|
||||||
domainObject.composition = [];
|
domainObject.configuration = {
|
||||||
domainObject.configuration = {
|
items: [],
|
||||||
items: [],
|
layoutGrid: [10, 10]
|
||||||
layoutGrid: [10, 10]
|
};
|
||||||
};
|
},
|
||||||
|
form: [
|
||||||
|
{
|
||||||
|
name: 'Horizontal grid (px)',
|
||||||
|
control: 'numberfield',
|
||||||
|
cssClass: 'l-input-sm l-numeric',
|
||||||
|
property: ['configuration', 'layoutGrid', 0],
|
||||||
|
required: true
|
||||||
},
|
},
|
||||||
form: [
|
{
|
||||||
{
|
name: 'Vertical grid (px)',
|
||||||
name: 'Horizontal grid (px)',
|
control: 'numberfield',
|
||||||
control: 'numberfield',
|
cssClass: 'l-input-sm l-numeric',
|
||||||
cssClass: 'l-input-sm l-numeric',
|
property: ['configuration', 'layoutGrid', 1],
|
||||||
property: ['configuration', 'layoutGrid', 0],
|
required: true
|
||||||
required: true
|
},
|
||||||
},
|
{
|
||||||
{
|
name: 'Horizontal size (px)',
|
||||||
name: 'Vertical grid (px)',
|
control: 'numberfield',
|
||||||
control: 'numberfield',
|
cssClass: 'l-input-sm l-numeric',
|
||||||
cssClass: 'l-input-sm l-numeric',
|
property: ['configuration', 'layoutDimensions', 0],
|
||||||
property: ['configuration', 'layoutGrid', 1],
|
required: false
|
||||||
required: true
|
},
|
||||||
},
|
{
|
||||||
{
|
name: 'Vertical size (px)',
|
||||||
name: 'Horizontal size (px)',
|
control: 'numberfield',
|
||||||
control: 'numberfield',
|
cssClass: 'l-input-sm l-numeric',
|
||||||
cssClass: 'l-input-sm l-numeric',
|
property: ['configuration', 'layoutDimensions', 1],
|
||||||
property: ['configuration', 'layoutDimensions', 0],
|
required: false
|
||||||
required: false
|
}
|
||||||
},
|
]
|
||||||
{
|
};
|
||||||
name: 'Vertical size (px)',
|
}
|
||||||
control: 'numberfield',
|
|
||||||
cssClass: 'l-input-sm l-numeric',
|
|
||||||
property: ['configuration', 'layoutDimensions', 1],
|
|
||||||
required: false
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return DisplayLayoutType;
|
|
||||||
});
|
|
||||||
|
@ -20,93 +20,89 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define([], function () {
|
/**
|
||||||
/**
|
* Handles drag interactions on frames in layouts. This will
|
||||||
* Handles drag interactions on frames in layouts. This will
|
* provides new positions/dimensions for frames based on
|
||||||
* provides new positions/dimensions for frames based on
|
* relative pixel positions provided; these will take into account
|
||||||
* relative pixel positions provided; these will take into account
|
* the grid size (in a snap-to sense) and will enforce some minimums
|
||||||
* the grid size (in a snap-to sense) and will enforce some minimums
|
* on both position and dimensions.
|
||||||
* on both position and dimensions.
|
*
|
||||||
*
|
* The provided position and dimensions factors will determine
|
||||||
* The provided position and dimensions factors will determine
|
* whether this is a move or a resize, and what type of resize it
|
||||||
* 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 be. For instance, a position factor of [1, 1]
|
* will move a frame along with the mouse as the drag
|
||||||
* will move a frame along with the mouse as the drag
|
* proceeds, while a dimension factor of [0, 0] will leave
|
||||||
* proceeds, while a dimension factor of [0, 0] will leave
|
* dimensions unchanged. Combining these in different
|
||||||
* dimensions unchanged. Combining these in different
|
* ways results in different handles; a position factor of
|
||||||
* ways results in different handles; a position factor of
|
* [1, 0] and a dimensions factor of [-1, 0] will implement
|
||||||
* [1, 0] and a dimensions factor of [-1, 0] will implement
|
* a left-edge resize, as the horizontal position will move
|
||||||
* a left-edge resize, as the horizontal position will move
|
* with the mouse while the horizontal dimensions shrink in
|
||||||
* with the mouse while the horizontal dimensions shrink in
|
* kind (and vertical properties remain unmodified.)
|
||||||
* kind (and vertical properties remain unmodified.)
|
*
|
||||||
*
|
* @param {object} rawPosition the initial position/dimensions
|
||||||
* @param {object} rawPosition the initial position/dimensions
|
* of the frame being interacted with
|
||||||
* of the frame being interacted with
|
* @param {number[]} posFactor the position factor
|
||||||
* @param {number[]} posFactor the position factor
|
* @param {number[]} dimFactor the dimensions factor
|
||||||
* @param {number[]} dimFactor the dimensions factor
|
* @param {number[]} the size of each grid element, in pixels
|
||||||
* @param {number[]} the size of each grid element, in pixels
|
* @constructor
|
||||||
* @constructor
|
* @memberof platform/features/layout
|
||||||
* @memberof platform/features/layout
|
*/
|
||||||
*/
|
export default function LayoutDrag(rawPosition, posFactor, dimFactor, gridSize) {
|
||||||
function LayoutDrag(rawPosition, posFactor, dimFactor, gridSize) {
|
this.rawPosition = rawPosition;
|
||||||
this.rawPosition = rawPosition;
|
this.posFactor = posFactor;
|
||||||
this.posFactor = posFactor;
|
this.dimFactor = dimFactor;
|
||||||
this.dimFactor = dimFactor;
|
this.gridSize = gridSize;
|
||||||
this.gridSize = gridSize;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Convert a delta from pixel coordinates to grid coordinates,
|
// Convert a delta from pixel coordinates to grid coordinates,
|
||||||
// rounding to whole-number grid coordinates.
|
// rounding to whole-number grid coordinates.
|
||||||
function toGridDelta(gridSize, pixelDelta) {
|
function toGridDelta(gridSize, pixelDelta) {
|
||||||
return pixelDelta.map(function (v, i) {
|
return pixelDelta.map(function (v, i) {
|
||||||
return Math.round(v / gridSize[i]);
|
return Math.round(v / gridSize[i]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Utility function to perform element-by-element multiplication
|
// Utility function to perform element-by-element multiplication
|
||||||
function multiply(array, factors) {
|
function multiply(array, factors) {
|
||||||
return array.map(function (v, i) {
|
return array.map(function (v, i) {
|
||||||
return v * factors[i];
|
return v * factors[i];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Utility function to perform element-by-element addition
|
// Utility function to perform element-by-element addition
|
||||||
function add(array, other) {
|
function add(array, other) {
|
||||||
return array.map(function (v, i) {
|
return array.map(function (v, i) {
|
||||||
return v + other[i];
|
return v + other[i];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Utility function to perform element-by-element max-choosing
|
// Utility function to perform element-by-element max-choosing
|
||||||
function max(array, other) {
|
function max(array, other) {
|
||||||
return array.map(function (v, i) {
|
return array.map(function (v, i) {
|
||||||
return Math.max(v, other[i]);
|
return Math.max(v, other[i]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a new position object in grid coordinates, with
|
* Get a new position object in grid coordinates, with
|
||||||
* position and dimensions both offset appropriately
|
* position and dimensions both offset appropriately
|
||||||
* according to the factors supplied in the constructor.
|
* according to the factors supplied in the constructor.
|
||||||
* @param {number[]} pixelDelta the offset from the
|
* @param {number[]} pixelDelta the offset from the
|
||||||
* original position, in pixels
|
* original position, in pixels
|
||||||
*/
|
*/
|
||||||
LayoutDrag.prototype.getAdjustedPositionAndDimensions = function (pixelDelta) {
|
LayoutDrag.prototype.getAdjustedPositionAndDimensions = function (pixelDelta) {
|
||||||
const gridDelta = toGridDelta(this.gridSize, pixelDelta);
|
const gridDelta = toGridDelta(this.gridSize, pixelDelta);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
position: max(add(this.rawPosition.position, multiply(gridDelta, this.posFactor)), [0, 0]),
|
position: max(add(this.rawPosition.position, multiply(gridDelta, this.posFactor)), [0, 0]),
|
||||||
dimensions: max(add(this.rawPosition.dimensions, multiply(gridDelta, this.dimFactor)), [1, 1])
|
dimensions: max(add(this.rawPosition.dimensions, multiply(gridDelta, this.dimFactor)), [1, 1])
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
|
||||||
LayoutDrag.prototype.getAdjustedPosition = function (pixelDelta) {
|
LayoutDrag.prototype.getAdjustedPosition = function (pixelDelta) {
|
||||||
const gridDelta = toGridDelta(this.gridSize, pixelDelta);
|
const gridDelta = toGridDelta(this.gridSize, pixelDelta);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
position: max(add(this.rawPosition.position, multiply(gridDelta, this.posFactor)), [0, 0])
|
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.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define(['./FiltersInspectorViewProvider'], function (FiltersInspectorViewProvider) {
|
import FiltersInspectorViewProvider from './FiltersInspectorViewProvider';
|
||||||
return function plugin(supportedObjectTypesArray) {
|
|
||||||
return function install(openmct) {
|
export default function plugin(supportedObjectTypesArray) {
|
||||||
openmct.inspectorViews.addProvider(
|
return function install(openmct) {
|
||||||
new FiltersInspectorViewProvider.default(openmct, supportedObjectTypesArray)
|
openmct.inspectorViews.addProvider(
|
||||||
);
|
new FiltersInspectorViewProvider(openmct, supportedObjectTypesArray)
|
||||||
};
|
);
|
||||||
};
|
};
|
||||||
});
|
}
|
||||||
|
@ -20,33 +20,31 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define(['./flexibleLayoutViewProvider', './utils/container', './toolbarProvider'], function (
|
import FlexibleLayoutViewProvider from './flexibleLayoutViewProvider';
|
||||||
FlexibleLayoutViewProvider,
|
import ToolBarProvider from './toolbarProvider';
|
||||||
Container,
|
import Container from './utils/container';
|
||||||
ToolBarProvider
|
|
||||||
) {
|
|
||||||
return function plugin() {
|
|
||||||
return function install(openmct) {
|
|
||||||
openmct.objectViews.addProvider(new FlexibleLayoutViewProvider.default(openmct));
|
|
||||||
|
|
||||||
openmct.types.addType('flexible-layout', {
|
export default function plugin() {
|
||||||
name: 'Flexible Layout',
|
return function install(openmct) {
|
||||||
creatable: true,
|
openmct.objectViews.addProvider(new FlexibleLayoutViewProvider(openmct));
|
||||||
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 = [];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
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.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define([], function () {
|
const helperFunctions = {
|
||||||
const helperFunctions = {
|
listenTo: function (object, event, callback, context) {
|
||||||
listenTo: function (object, event, callback, context) {
|
if (!this._listeningTo) {
|
||||||
if (!this._listeningTo) {
|
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;
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
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.
|
* 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.
|
* 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.
|
* This is for testing purposes only, and behaves identically to a local clock.
|
||||||
* It DOES NOT tick on receipt of data.
|
* It DOES NOT tick on receipt of data.
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function LADClock(period) {
|
constructor(period) {
|
||||||
LocalClock.call(this, period);
|
super(period);
|
||||||
|
|
||||||
this.key = 'test-lad';
|
this.key = 'test-lad';
|
||||||
this.mode = 'lad';
|
this.mode = 'lad';
|
||||||
this.cssClass = 'icon-suitcase';
|
this.cssClass = 'icon-suitcase';
|
||||||
this.name = 'Latest available data';
|
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);
|
export default LADClock;
|
||||||
|
|
||||||
return LADClock;
|
|
||||||
});
|
|
||||||
|