From df7affca2d13fa71874a1c2bc94d2c62124eafed Mon Sep 17 00:00:00 2001 From: Tristan F <26509014+LeoDog896@users.noreply.github.com> Date: Fri, 1 Sep 2023 11:35:27 -0400 Subject: [PATCH 01/20] feat: full amd -> es6 conversion --- .../generator/GeneratorMetadataProvider.js | 260 ++- example/generator/GeneratorProvider.js | 160 +- example/generator/SinewaveLimitProvider.js | 246 ++- example/generator/StateGeneratorProvider.js | 80 +- example/generator/WorkerInterface.js | 142 +- openmct.js | 4 +- src/MCT.js | 746 +++++---- src/MCTSpec.js | 154 +- src/api/api.js | 89 +- src/api/objects/object-utils.js | 278 ++-- src/api/objects/test/object-utilsSpec.js | 228 +-- src/api/telemetry/DefaultMetadataProvider.js | 138 +- src/api/telemetry/TelemetryMetadataManager.js | 226 ++- src/api/telemetry/TelemetryValueFormatter.js | 217 ++- .../URLIndicatorPlugin/URLIndicator.js | 156 +- .../URLIndicatorPlugin/URLIndicatorPlugin.js | 18 +- .../URLIndicatorPlugin/URLIndicatorSpec.js | 179 +-- .../autoflow/AutoflowTabularConstants.js | 22 +- .../autoflow/AutoflowTabularController.js | 178 +-- src/plugins/autoflow/AutoflowTabularPlugin.js | 36 +- .../autoflow/AutoflowTabularRowController.js | 126 +- src/plugins/autoflow/AutoflowTabularView.js | 154 +- src/plugins/autoflow/dom-observer.js | 74 +- .../displayLayout/DisplayLayoutToolbar.js | 1417 ++++++++--------- .../displayLayout/DisplayLayoutType.js | 94 +- src/plugins/displayLayout/LayoutDrag.js | 156 +- src/plugins/filters/plugin.js | 16 +- src/plugins/flexibleLayout/plugin.js | 50 +- src/plugins/imagery/lib/eventHelpers.js | 130 +- src/plugins/latestDataClock/LADClock.js | 16 +- src/plugins/latestDataClock/plugin.js | 12 +- .../localTimeSystem/LocalTimeFormat.js | 74 +- .../localTimeSystem/LocalTimeSystem.js | 18 +- src/plugins/localTimeSystem/plugin.js | 15 +- src/plugins/objectMigration/Migrations.js | 452 +++--- src/plugins/plugins.js | 343 ++-- .../summaryWidget/SummaryWidgetViewPolicy.js | 26 +- .../SummaryWidgetsCompositionPolicy.js | 24 +- src/plugins/summaryWidget/plugin.js | 181 +-- src/plugins/summaryWidget/src/Condition.js | 429 +++-- .../summaryWidget/src/ConditionEvaluator.js | 866 +++++----- .../summaryWidget/src/ConditionManager.js | 733 +++++---- src/plugins/summaryWidget/src/Rule.js | 953 ++++++----- .../summaryWidget/src/SummaryWidget.js | 750 +++++---- src/plugins/summaryWidget/src/TestDataItem.js | 353 ++-- .../summaryWidget/src/TestDataManager.js | 369 +++-- src/plugins/summaryWidget/src/WidgetDnD.js | 299 ++-- src/plugins/summaryWidget/src/eventHelpers.js | 130 +- .../summaryWidget/src/input/ColorPalette.js | 236 ++- .../summaryWidget/src/input/IconPalette.js | 130 +- .../summaryWidget/src/input/KeySelect.js | 160 +- .../summaryWidget/src/input/ObjectSelect.js | 146 +- .../src/input/OperationSelect.js | 213 ++- .../summaryWidget/src/input/Palette.js | 331 ++-- src/plugins/summaryWidget/src/input/Select.js | 277 ++-- .../src/telemetry/EvaluatorPool.js | 64 +- .../src/telemetry/EvaluatorPoolSpec.js | 129 +- .../src/telemetry/SummaryWidgetCondition.js | 86 +- .../telemetry/SummaryWidgetConditionSpec.js | 180 +-- .../src/telemetry/SummaryWidgetEvaluator.js | 443 +++--- .../SummaryWidgetMetadataProvider.js | 160 +- .../src/telemetry/SummaryWidgetRule.js | 80 +- .../src/telemetry/SummaryWidgetRuleSpec.js | 228 +-- .../SummaryWidgetTelemetryProvider.js | 62 +- .../SummaryWidgetTelemetryProviderSpec.js | 790 ++++----- .../summaryWidget/src/telemetry/operations.js | 382 +++-- .../src/views/SummaryWidgetView.js | 179 +-- .../src/views/SummaryWidgetViewProvider.js | 88 +- .../test/ConditionEvaluatorSpec.js | 670 ++++---- .../test/ConditionManagerSpec.js | 725 +++++---- .../summaryWidget/test/ConditionSpec.js | 308 ++-- src/plugins/summaryWidget/test/RuleSpec.js | 546 ++++--- .../summaryWidget/test/SummaryWidgetSpec.js | 300 ++-- .../test/SummaryWidgetViewPolicySpec.js | 56 +- .../summaryWidget/test/TestDataItemSpec.js | 274 ++-- .../summaryWidget/test/TestDataManagerSpec.js | 444 +++--- .../test/input/ColorPaletteSpec.js | 38 +- .../test/input/IconPaletteSpec.js | 38 +- .../summaryWidget/test/input/KeySelectSpec.js | 212 +-- .../test/input/ObjectSelectSpec.js | 210 +-- .../test/input/OperationSelectSpec.js | 276 ++-- .../summaryWidget/test/input/PaletteSpec.js | 70 +- .../summaryWidget/test/input/SelectSpec.js | 104 +- src/plugins/tabs/plugin.js | 69 +- src/plugins/telemetryMean/plugin.js | 104 +- .../src/MeanTelemetryProvider.js | 210 +-- .../src/MeanTelemetryProviderSpec.js | 1033 ++++++------ .../telemetryMean/src/MockTelemetryApi.js | 150 +- .../telemetryMean/src/TelemetryAverager.js | 184 +-- src/plugins/telemetryTable/TelemetryTable.js | 685 ++++---- .../telemetryTable/TelemetryTableColumn.js | 68 +- .../TelemetryTableConfiguration.js | 239 ++- .../TelemetryTableNameColumn.js | 27 +- .../telemetryTable/TelemetryTableRow.js | 164 +- .../telemetryTable/TelemetryTableType.js | 30 +- .../TelemetryTableUnitColumn.js | 58 +- .../collections/TableRowCollection.js | 580 ++++--- src/plugins/utcTimeSystem/UTCTimeSystem.js | 22 +- src/ui/registries/ToolbarRegistry.js | 144 +- src/ui/registries/ViewRegistry.js | 333 ++-- src/ui/router/Browse.js | 260 ++- 101 files changed, 12182 insertions(+), 12630 deletions(-) diff --git a/example/generator/GeneratorMetadataProvider.js b/example/generator/GeneratorMetadataProvider.js index fd12a1a7d07..51ec3cc88c8 100644 --- a/example/generator/GeneratorMetadataProvider.js +++ b/example/generator/GeneratorMetadataProvider.js @@ -1,138 +1,134 @@ -define(['lodash'], function (_) { - var METADATA_BY_TYPE = { - generator: { - values: [ - { - key: 'name', - name: 'Name', - format: 'string' - }, - { - key: 'utc', - name: 'Time', - format: 'utc', - hints: { - domain: 1 - } - }, - { - key: 'yesterday', - name: 'Yesterday', - format: 'utc', - hints: { - domain: 2 - } - }, - { - key: 'wavelengths', - name: 'Wavelength', - unit: 'nm', - format: 'string[]', - hints: { - range: 4 - } - }, - // Need to enable "LocalTimeSystem" plugin to make use of this - // { - // key: "local", - // name: "Time", - // format: "local-format", - // source: "utc", - // hints: { - // domain: 3 - // } - // }, - { - key: 'sin', - name: 'Sine', - unit: 'Hz', - formatString: '%0.2f', - hints: { - range: 1 - } - }, - { - key: 'cos', - name: 'Cosine', - unit: 'deg', - formatString: '%0.2f', - hints: { - range: 2 - } - }, - { - key: 'intensities', - name: 'Intensities', - format: 'number[]', - hints: { - range: 3 - } +const METADATA_BY_TYPE = { + generator: { + values: [ + { + key: 'name', + name: 'Name', + format: 'string' + }, + { + key: 'utc', + name: 'Time', + format: 'utc', + hints: { + domain: 1 } - ] - }, - 'example.state-generator': { - values: [ - { - key: 'name', - name: 'Name', - format: 'string' - }, - { - key: 'utc', - name: 'Time', - format: 'utc', - hints: { - domain: 1 - } - }, - { - key: 'local', - name: 'Time', - format: 'utc', - source: 'utc', - hints: { - domain: 2 - } - }, - { - key: 'state', - source: 'value', - name: 'State', - format: 'enum', - enumerations: [ - { - value: 0, - string: 'OFF' - }, - { - value: 1, - string: 'ON' - } - ], - hints: { - range: 1 - } - }, - { - key: 'value', - name: 'Value', - hints: { - range: 2 + }, + { + key: 'yesterday', + name: 'Yesterday', + format: 'utc', + hints: { + domain: 2 + } + }, + { + key: 'wavelengths', + name: 'Wavelength', + unit: 'nm', + format: 'string[]', + hints: { + range: 4 + } + }, + // Need to enable "LocalTimeSystem" plugin to make use of this + // { + // key: "local", + // name: "Time", + // format: "local-format", + // source: "utc", + // hints: { + // domain: 3 + // } + // }, + { + key: 'sin', + name: 'Sine', + unit: 'Hz', + formatString: '%0.2f', + hints: { + range: 1 + } + }, + { + key: 'cos', + name: 'Cosine', + unit: 'deg', + formatString: '%0.2f', + hints: { + range: 2 + } + }, + { + key: 'intensities', + name: 'Intensities', + format: 'number[]', + hints: { + range: 3 + } + } + ] + }, + 'example.state-generator': { + values: [ + { + key: 'name', + name: 'Name', + format: 'string' + }, + { + key: 'utc', + name: 'Time', + format: 'utc', + hints: { + domain: 1 + } + }, + { + key: 'local', + name: 'Time', + format: 'utc', + source: 'utc', + hints: { + domain: 2 + } + }, + { + key: 'state', + source: 'value', + name: 'State', + format: 'enum', + enumerations: [ + { + value: 0, + string: 'OFF' + }, + { + value: 1, + string: 'ON' } + ], + hints: { + range: 1 } - ] - } - }; - - function GeneratorMetadataProvider() {} + }, + { + key: 'value', + name: 'Value', + hints: { + range: 2 + } + } + ] + } +}; - GeneratorMetadataProvider.prototype.supportsMetadata = function (domainObject) { - return Object.prototype.hasOwnProperty.call(METADATA_BY_TYPE, domainObject.type); - }; +export default function GeneratorMetadataProvider() {} - GeneratorMetadataProvider.prototype.getMetadata = function (domainObject) { - return Object.assign({}, domainObject.telemetry, METADATA_BY_TYPE[domainObject.type]); - }; +GeneratorMetadataProvider.prototype.supportsMetadata = function (domainObject) { + return Object.prototype.hasOwnProperty.call(METADATA_BY_TYPE, domainObject.type); +}; - return GeneratorMetadataProvider; -}); +GeneratorMetadataProvider.prototype.getMetadata = function (domainObject) { + return Object.assign({}, domainObject.telemetry, METADATA_BY_TYPE[domainObject.type]); +}; diff --git a/example/generator/GeneratorProvider.js b/example/generator/GeneratorProvider.js index b74915ef12c..2fc576d544b 100644 --- a/example/generator/GeneratorProvider.js +++ b/example/generator/GeneratorProvider.js @@ -20,84 +20,82 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define(['./WorkerInterface'], function (WorkerInterface) { - var REQUEST_DEFAULTS = { - amplitude: 1, - period: 10, - offset: 0, - dataRateInHz: 1, - randomness: 0, - phase: 0, - loadDelay: 0, - infinityValues: false - }; - - function GeneratorProvider(openmct, StalenessProvider) { - this.openmct = openmct; - this.workerInterface = new WorkerInterface(openmct, StalenessProvider); - } - - GeneratorProvider.prototype.canProvideTelemetry = function (domainObject) { - return domainObject.type === 'generator'; - }; - - GeneratorProvider.prototype.supportsRequest = GeneratorProvider.prototype.supportsSubscribe = - GeneratorProvider.prototype.canProvideTelemetry; - - GeneratorProvider.prototype.makeWorkerRequest = function (domainObject, request) { - var props = [ - 'amplitude', - 'period', - 'offset', - 'dataRateInHz', - 'randomness', - 'phase', - 'loadDelay', - 'infinityValues' - ]; - - request = request || {}; - - var workerRequest = {}; - - props.forEach(function (prop) { - if ( - domainObject.telemetry && - Object.prototype.hasOwnProperty.call(domainObject.telemetry, prop) - ) { - workerRequest[prop] = domainObject.telemetry[prop]; - } - - if (request && Object.prototype.hasOwnProperty.call(request, prop)) { - workerRequest[prop] = request[prop]; - } - - if (!Object.prototype.hasOwnProperty.call(workerRequest, prop)) { - workerRequest[prop] = REQUEST_DEFAULTS[prop]; - } - - workerRequest[prop] = Number(workerRequest[prop]); - }); - - workerRequest.id = this.openmct.objects.makeKeyString(domainObject.identifier); - workerRequest.name = domainObject.name; - - return workerRequest; - }; - - GeneratorProvider.prototype.request = function (domainObject, request) { - var workerRequest = this.makeWorkerRequest(domainObject, request); - workerRequest.start = request.start; - workerRequest.end = request.end; - - return this.workerInterface.request(workerRequest); - }; - - GeneratorProvider.prototype.subscribe = function (domainObject, callback) { - var workerRequest = this.makeWorkerRequest(domainObject, {}); - - return this.workerInterface.subscribe(workerRequest, callback); - }; - - return GeneratorProvider; -}); +import WorkerInterface from './WorkerInterface'; + +const REQUEST_DEFAULTS = { + amplitude: 1, + period: 10, + offset: 0, + dataRateInHz: 1, + randomness: 0, + phase: 0, + loadDelay: 0, + infinityValues: false +}; + +export default function GeneratorProvider(openmct, StalenessProvider) { + this.openmct = openmct; + this.workerInterface = new WorkerInterface(openmct, StalenessProvider); +} + +GeneratorProvider.prototype.canProvideTelemetry = function (domainObject) { + return domainObject.type === 'generator'; +}; + +GeneratorProvider.prototype.supportsRequest = GeneratorProvider.prototype.supportsSubscribe = + GeneratorProvider.prototype.canProvideTelemetry; + +GeneratorProvider.prototype.makeWorkerRequest = function (domainObject, request) { + var props = [ + 'amplitude', + 'period', + 'offset', + 'dataRateInHz', + 'randomness', + 'phase', + 'loadDelay', + 'infinityValues' + ]; + + request = request || {}; + + var workerRequest = {}; + + props.forEach(function (prop) { + if ( + domainObject.telemetry && + Object.prototype.hasOwnProperty.call(domainObject.telemetry, prop) + ) { + workerRequest[prop] = domainObject.telemetry[prop]; + } + + if (request && Object.prototype.hasOwnProperty.call(request, prop)) { + workerRequest[prop] = request[prop]; + } + + if (!Object.prototype.hasOwnProperty.call(workerRequest, prop)) { + workerRequest[prop] = REQUEST_DEFAULTS[prop]; + } + + workerRequest[prop] = Number(workerRequest[prop]); + }); + + workerRequest.id = this.openmct.objects.makeKeyString(domainObject.identifier); + workerRequest.name = domainObject.name; + + return workerRequest; +}; + +GeneratorProvider.prototype.request = function (domainObject, request) { + var workerRequest = this.makeWorkerRequest(domainObject, request); + workerRequest.start = request.start; + workerRequest.end = request.end; + + return this.workerInterface.request(workerRequest); +}; + +GeneratorProvider.prototype.subscribe = function (domainObject, callback) { + var workerRequest = this.makeWorkerRequest(domainObject, {}); + + return this.workerInterface.subscribe(workerRequest, callback); +}; diff --git a/example/generator/SinewaveLimitProvider.js b/example/generator/SinewaveLimitProvider.js index 3f71747f8f3..0184a6c3163 100644 --- a/example/generator/SinewaveLimitProvider.js +++ b/example/generator/SinewaveLimitProvider.js @@ -20,147 +20,143 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define([], function () { - var PURPLE = { - sin: 2.2, - cos: 2.2 +var PURPLE = { + sin: 2.2, + cos: 2.2 + }, + RED = { + sin: 0.9, + cos: 0.9 + }, + ORANGE = { + sin: 0.7, + cos: 0.7 + }, + YELLOW = { + sin: 0.5, + cos: 0.5 + }, + CYAN = { + sin: 0.45, + cos: 0.45 + }, + LIMITS = { + rh: { + cssClass: 'is-limit--upr is-limit--red', + low: RED, + high: Number.POSITIVE_INFINITY, + name: 'Red High' }, - RED = { - sin: 0.9, - cos: 0.9 + rl: { + cssClass: 'is-limit--lwr is-limit--red', + high: -RED, + low: Number.NEGATIVE_INFINITY, + name: 'Red Low' }, - ORANGE = { - sin: 0.7, - cos: 0.7 + yh: { + cssClass: 'is-limit--upr is-limit--yellow', + low: YELLOW, + high: RED, + name: 'Yellow High' }, - YELLOW = { - sin: 0.5, - cos: 0.5 - }, - CYAN = { - sin: 0.45, - cos: 0.45 - }, - LIMITS = { - rh: { - cssClass: 'is-limit--upr is-limit--red', - low: RED, - high: Number.POSITIVE_INFINITY, - name: 'Red High' - }, - rl: { - cssClass: 'is-limit--lwr is-limit--red', - high: -RED, - low: Number.NEGATIVE_INFINITY, - name: 'Red Low' - }, - yh: { - cssClass: 'is-limit--upr is-limit--yellow', - low: YELLOW, - high: RED, - name: 'Yellow High' - }, - yl: { - cssClass: 'is-limit--lwr is-limit--yellow', - low: -RED, - high: -YELLOW, - name: 'Yellow Low' - } - }; + yl: { + cssClass: 'is-limit--lwr is-limit--yellow', + low: -RED, + high: -YELLOW, + name: 'Yellow Low' + } + }; - function SinewaveLimitProvider() {} +export default function SinewaveLimitProvider() {} - SinewaveLimitProvider.prototype.supportsLimits = function (domainObject) { - return domainObject.type === 'generator'; - }; +SinewaveLimitProvider.prototype.supportsLimits = function (domainObject) { + return domainObject.type === 'generator'; +}; - SinewaveLimitProvider.prototype.getLimitEvaluator = function (domainObject) { - return { - evaluate: function (datum, valueMetadata) { - var range = valueMetadata && valueMetadata.key; +SinewaveLimitProvider.prototype.getLimitEvaluator = function (domainObject) { + return { + evaluate: function (datum, valueMetadata) { + var range = valueMetadata && valueMetadata.key; - if (datum[range] > RED[range]) { - return LIMITS.rh; - } + if (datum[range] > RED[range]) { + return LIMITS.rh; + } - if (datum[range] < -RED[range]) { - return LIMITS.rl; - } + if (datum[range] < -RED[range]) { + return LIMITS.rl; + } - if (datum[range] > YELLOW[range]) { - return LIMITS.yh; - } + if (datum[range] > YELLOW[range]) { + return LIMITS.yh; + } - if (datum[range] < -YELLOW[range]) { - return LIMITS.yl; - } + if (datum[range] < -YELLOW[range]) { + return LIMITS.yl; } - }; + } }; +}; - SinewaveLimitProvider.prototype.getLimits = function (domainObject) { - return { - limits: function () { - return Promise.resolve({ - WATCH: { - low: { - color: 'cyan', - sin: -CYAN.sin, - cos: -CYAN.cos - }, - 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: { - low: { - color: 'yellow', - sin: -YELLOW.sin, - cos: -YELLOW.cos - }, - high: { - color: 'yellow', - ...YELLOW - } + high: { + color: 'cyan', + ...CYAN + } + }, + WARNING: { + low: { + color: 'yellow', + sin: -YELLOW.sin, + cos: -YELLOW.cos + }, + high: { + color: 'yellow', + ...YELLOW + } + }, + DISTRESS: { + low: { + color: 'orange', + sin: -ORANGE.sin, + cos: -ORANGE.cos }, - DISTRESS: { - low: { - color: 'orange', - sin: -ORANGE.sin, - cos: -ORANGE.cos - }, - high: { - color: 'orange', - ...ORANGE - } + high: { + color: 'orange', + ...ORANGE + } + }, + CRITICAL: { + low: { + color: 'red', + sin: -RED.sin, + cos: -RED.cos }, - CRITICAL: { - low: { - color: 'red', - sin: -RED.sin, - cos: -RED.cos - }, - high: { - color: 'red', - ...RED - } + high: { + color: 'red', + ...RED + } + }, + SEVERE: { + low: { + color: 'purple', + sin: -PURPLE.sin, + cos: -PURPLE.cos }, - SEVERE: { - low: { - color: 'purple', - sin: -PURPLE.sin, - cos: -PURPLE.cos - }, - high: { - color: 'purple', - ...PURPLE - } + high: { + color: 'purple', + ...PURPLE } - }); - } - }; + } + }); + } }; - - return SinewaveLimitProvider; -}); +}; diff --git a/example/generator/StateGeneratorProvider.js b/example/generator/StateGeneratorProvider.js index c840e6bb99c..973e0c92a75 100644 --- a/example/generator/StateGeneratorProvider.js +++ b/example/generator/StateGeneratorProvider.js @@ -20,56 +20,52 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define([], function () { - function StateGeneratorProvider() {} +export default function StateGeneratorProvider() {} - function pointForTimestamp(timestamp, duration, name) { - return { - name: name, - utc: Math.floor(timestamp / duration) * duration, - value: Math.floor(timestamp / duration) % 2 - }; - } - - StateGeneratorProvider.prototype.supportsSubscribe = function (domainObject) { - return domainObject.type === 'example.state-generator'; +function pointForTimestamp(timestamp, duration, name) { + return { + name: name, + utc: Math.floor(timestamp / duration) * duration, + value: Math.floor(timestamp / duration) % 2 }; +} - StateGeneratorProvider.prototype.subscribe = function (domainObject, callback) { - var duration = domainObject.telemetry.duration * 1000; +StateGeneratorProvider.prototype.supportsSubscribe = function (domainObject) { + return domainObject.type === 'example.state-generator'; +}; - var interval = setInterval(function () { - var now = Date.now(); - var datum = pointForTimestamp(now, duration, domainObject.name); - datum.value = String(datum.value); - callback(datum); - }, duration); +StateGeneratorProvider.prototype.subscribe = function (domainObject, callback) { + var duration = domainObject.telemetry.duration * 1000; - return function () { - clearInterval(interval); - }; - }; + var interval = setInterval(function () { + var now = Date.now(); + var datum = pointForTimestamp(now, duration, domainObject.name); + datum.value = String(datum.value); + callback(datum); + }, duration); - StateGeneratorProvider.prototype.supportsRequest = function (domainObject, options) { - return domainObject.type === 'example.state-generator'; + return function () { + clearInterval(interval); }; +}; - 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.supportsRequest = function (domainObject, options) { + return domainObject.type === 'example.state-generator'; +}; - var data = []; - while (start <= end && data.length < 5000) { - data.push(pointForTimestamp(start, duration, domainObject.name)); - start += duration; - } +StateGeneratorProvider.prototype.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; + } - return Promise.resolve(data); - }; + var data = []; + while (start <= end && data.length < 5000) { + data.push(pointForTimestamp(start, duration, domainObject.name)); + start += duration; + } - return StateGeneratorProvider; -}); + return Promise.resolve(data); +}; diff --git a/example/generator/WorkerInterface.js b/example/generator/WorkerInterface.js index d02546d34ac..416a59f088b 100644 --- a/example/generator/WorkerInterface.js +++ b/example/generator/WorkerInterface.js @@ -20,88 +20,86 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define(['uuid'], function ({ v4: uuid }) { - function WorkerInterface(openmct, StalenessProvider) { - // eslint-disable-next-line no-undef - const workerUrl = `${openmct.getAssetPath()}${__OPENMCT_ROOT_RELATIVE__}generatorWorker.js`; - this.StalenessProvider = StalenessProvider; - this.worker = new Worker(workerUrl); - this.worker.onmessage = this.onMessage.bind(this); - this.callbacks = {}; - this.staleTelemetryIds = {}; - - this.watchStaleness(); +import * as uuid from 'uuid'; + +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.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() }; - WorkerInterface.prototype.dispatch = function (request, data, callback) { - var message = { - request: request, - data: data, - id: uuid() - }; + if (callback) { + this.callbacks[message.id] = callback; + } - if (callback) { - this.callbacks[message.id] = callback; + this.worker.postMessage(message); + + return message.id; +}; + +WorkerInterface.prototype.request = function (request) { + var deferred = {}; + var promise = new Promise(function (resolve, reject) { + deferred.resolve = resolve; + deferred.reject = reject; + }); + var messageId; + + let self = this; + function callback(message) { + if (message.error) { + deferred.reject(message.error); + } else { + deferred.resolve(message.data); } - this.worker.postMessage(message); + delete self.callbacks[messageId]; + } - return message.id; - }; + messageId = this.dispatch('request', request, callback.bind(this)); - WorkerInterface.prototype.request = function (request) { - var deferred = {}; - var promise = new Promise(function (resolve, reject) { - deferred.resolve = resolve; - deferred.reject = reject; - }); - var messageId; + return promise; +}; - let self = this; - function callback(message) { - if (message.error) { - deferred.reject(message.error); - } else { - deferred.resolve(message.data); - } - - delete self.callbacks[messageId]; +WorkerInterface.prototype.subscribe = function (request, cb) { + const { id, loadDelay } = request; + const messageId = this.dispatch('subscribe', request, (message) => { + if (!this.staleTelemetryIds[id]) { + setTimeout(() => cb(message.data), Math.max(loadDelay, 0)); } + }); - messageId = this.dispatch('request', request, callback.bind(this)); - - return promise; - }; - - WorkerInterface.prototype.subscribe = function (request, cb) { - const { id, loadDelay } = request; - const messageId = this.dispatch('subscribe', request, (message) => { - if (!this.staleTelemetryIds[id]) { - setTimeout(() => cb(message.data), Math.max(loadDelay, 0)); - } + return function () { + this.dispatch('unsubscribe', { + id: messageId }); - - return function () { - this.dispatch('unsubscribe', { - id: messageId - }); - delete this.callbacks[messageId]; - }.bind(this); - }; - - return WorkerInterface; -}); + delete this.callbacks[messageId]; + }.bind(this); +}; diff --git a/openmct.js b/openmct.js index 9c7a8e0503e..2e9c7e70706 100644 --- a/openmct.js +++ b/openmct.js @@ -75,9 +75,9 @@ if (document.currentScript) { * @property {OpenMCTComponent[]} components */ -const MCT = require('./src/MCT'); +import { MCT } from './src/MCT'; /** @type {OpenMCT} */ const openmct = new MCT(); -module.exports = openmct; +export default openmct; diff --git a/src/MCT.js b/src/MCT.js index eb5f969f7dd..f7bb48bd1c5 100644 --- a/src/MCT.js +++ b/src/MCT.js @@ -20,246 +20,201 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ /* eslint-disable no-undef */ -define([ - 'EventEmitter', - './api/api', - './api/overlays/OverlayAPI', - './api/tooltips/ToolTipAPI', - './selection/Selection', - './plugins/plugins', - './ui/registries/ViewRegistry', - './plugins/imagery/plugin', - './ui/registries/InspectorViewRegistry', - './ui/registries/ToolbarRegistry', - './ui/router/ApplicationRouter', - './ui/router/Browse', - './ui/layout/Layout.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', - './ui/components/components', - 'vue' -], function ( - EventEmitter, - api, - OverlayAPI, - ToolTipAPI, - Selection, - plugins, - ViewRegistry, - ImageryPlugin, - InspectorViewRegistry, - ToolbarRegistry, - ApplicationRouter, - Browse, - Layout, - PreviewPlugin, - BrandingAPI, - LicensesPlugin, - RemoveActionPlugin, - MoveActionPlugin, - LinkActionPlugin, - DuplicateActionPlugin, - ImportFromJSONAction, - ExportAsJSONAction, - components, - Vue -) { - /** - * Open MCT is an extensible web application for building mission - * control user interfaces. This module is itself an instance of - * [MCT]{@link module:openmct.MCT}, which provides an interface for - * configuring and executing the application. - * - * @exports openmct - */ +import EventEmitter from 'EventEmitter'; +import Vue from 'vue'; + +import api from './api/api'; +import BrandingAPI from './api/Branding'; +import OverlayAPI from './api/overlays/OverlayAPI'; +import ToolTipAPI from './api/tooltips/ToolTipAPI'; +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 components from './ui/components/components'; +import Layout from './ui/layout/Layout.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 function MCT() { + EventEmitter.call(this); + this.buildInfo = { + version: __OPENMCT_VERSION__, + buildDate: __OPENMCT_BUILD_DATE__, + revision: __OPENMCT_REVISION__, + branch: __OPENMCT_BUILD_BRANCH__ + }; - /** - * The Open MCT application. This may be configured by installing plugins - * or registering extensions before the application is started. - * @constructor - * @memberof module:openmct - */ - function MCT() { - EventEmitter.call(this); - this.buildInfo = { - version: __OPENMCT_VERSION__, - buildDate: __OPENMCT_BUILD_DATE__, - revision: __OPENMCT_REVISION__, - branch: __OPENMCT_BUILD_BRANCH__ - }; - - this.destroy = this.destroy.bind(this); - this.defaultClock = 'local'; - [ - /** - * Tracks current selection state of the application. - * @private - */ - ['selection', () => new Selection.default(this)], - - /** - * MCT's time conductor, which may be used to synchronize view contents - * for telemetry- or time-based views. - * @type {module:openmct.TimeConductor} - * @memberof module:openmct.MCT# - * @name conductor - */ - ['time', () => new api.TimeAPI(this)], - - /** - * An interface for interacting with the composition of domain objects. - * The composition of a domain object is the list of other domain - * objects it "contains" (for instance, that should be displayed - * beneath it in the tree.) - * - * `composition` may be called as a function, in which case it acts - * as [`composition.get`]{@link module:openmct.CompositionAPI#get}. - * - * @type {module:openmct.CompositionAPI} - * @memberof module:openmct.MCT# - * @name composition - */ - ['composition', () => new api.CompositionAPI.default(this)], - - /** - * Registry for views of domain objects which should appear in the - * main viewing area. - * - * @type {module:openmct.ViewRegistry} - * @memberof module:openmct.MCT# - * @name objectViews - */ - ['objectViews', () => new ViewRegistry()], - - /** - * Registry for views which should appear in the Inspector area. - * These views will be chosen based on the selection state. - * - * @type {module:openmct.InspectorViewRegistry} - * @memberof module:openmct.MCT# - * @name inspectorViews - */ - ['inspectorViews', () => new InspectorViewRegistry.default()], - - /** - * Registry for views which should appear in Edit Properties - * dialogs, and similar user interface elements used for - * modifying domain objects external to its regular views. - * - * @type {module:openmct.ViewRegistry} - * @memberof module:openmct.MCT# - * @name propertyEditors - */ - ['propertyEditors', () => new ViewRegistry()], - - /** - * Registry for views which should appear in the toolbar area while - * editing. These views will be chosen based on the selection state. - * - * @type {module:openmct.ToolbarRegistry} - * @memberof module:openmct.MCT# - * @name toolbars - */ - ['toolbars', () => new ToolbarRegistry()], - - /** - * Registry for domain object types which may exist within this - * instance of Open MCT. - * - * @type {module:openmct.TypeRegistry} - * @memberof module:openmct.MCT# - * @name types - */ - ['types', () => new api.TypeRegistry()], - - /** - * An interface for interacting with domain objects and the domain - * object hierarchy. - * - * @type {module:openmct.ObjectAPI} - * @memberof module:openmct.MCT# - * @name objects - */ - ['objects', () => new api.ObjectAPI.default(this.types, this)], - - /** - * An interface for retrieving and interpreting telemetry data associated - * with a domain object. - * - * @type {module:openmct.TelemetryAPI} - * @memberof module:openmct.MCT# - * @name telemetry - */ - ['telemetry', () => new api.TelemetryAPI.default(this)], - - /** - * An interface for creating new indicators and changing them dynamically. - * - * @type {module:openmct.IndicatorAPI} - * @memberof module:openmct.MCT# - * @name indicators - */ - ['indicators', () => new api.IndicatorAPI(this)], - - /** - * MCT's user awareness management, to enable user and - * role specific functionality. - * @type {module:openmct.UserAPI} - * @memberof module:openmct.MCT# - * @name user - */ - ['user', () => new api.UserAPI(this)], - - ['notifications', () => new api.NotificationAPI()], - - ['editor', () => new api.EditorAPI.default(this)], - - ['overlays', () => new OverlayAPI.default()], - - ['tooltips', () => new ToolTipAPI.default()], - - ['menus', () => new api.MenuAPI(this)], - - ['actions', () => new api.ActionsAPI(this)], - - ['status', () => new api.StatusAPI(this)], - - ['priority', () => api.PriorityAPI], - - ['router', () => new ApplicationRouter(this)], - - ['faults', () => new api.FaultManagementAPI.default(this)], - - ['forms', () => new api.FormsAPI.default(this)], - - ['branding', () => BrandingAPI.default], - - /** - * MCT's annotation API that enables - * human-created comments and categorization linked to data products - * @type {module:openmct.AnnotationAPI} - * @memberof module:openmct.MCT# - * @name annotation - */ - ['annotation', () => new api.AnnotationAPI(this)] - ].forEach((apiEntry) => { - const apiName = apiEntry[0]; - const apiObject = apiEntry[1](); - - Object.defineProperty(this, apiName, { - value: apiObject, - enumerable: false, - configurable: false, - writable: true - }); - }); + this.destroy = this.destroy.bind(this); + this.defaultClock = 'local'; + [ + /** + * Tracks current selection state of the application. + * @private + */ + ['selection', () => new Selection(this)], + + /** + * MCT's time conductor, which may be used to synchronize view contents + * for telemetry- or time-based views. + * @type {module:openmct.TimeConductor} + * @memberof module:openmct.MCT# + * @name conductor + */ + ['time', () => new api.TimeAPI(this)], + + /** + * An interface for interacting with the composition of domain objects. + * The composition of a domain object is the list of other domain + * objects it "contains" (for instance, that should be displayed + * beneath it in the tree.) + * + * `composition` may be called as a function, in which case it acts + * as [`composition.get`]{@link module:openmct.CompositionAPI#get}. + * + * @type {module:openmct.CompositionAPI} + * @memberof module:openmct.MCT# + * @name composition + */ + ['composition', () => new api.CompositionAPI(this)], + + /** + * Registry for views of domain objects which should appear in the + * main viewing area. + * + * @type {module:openmct.ViewRegistry} + * @memberof module:openmct.MCT# + * @name objectViews + */ + ['objectViews', () => new ViewRegistry()], + + /** + * Registry for views which should appear in the Inspector area. + * These views will be chosen based on the selection state. + * + * @type {module:openmct.InspectorViewRegistry} + * @memberof module:openmct.MCT# + * @name inspectorViews + */ + ['inspectorViews', () => new InspectorViewRegistry()], + + /** + * Registry for views which should appear in Edit Properties + * dialogs, and similar user interface elements used for + * modifying domain objects external to its regular views. + * + * @type {module:openmct.ViewRegistry} + * @memberof module:openmct.MCT# + * @name propertyEditors + */ + ['propertyEditors', () => new ViewRegistry()], + + /** + * Registry for views which should appear in the toolbar area while + * editing. These views will be chosen based on the selection state. + * + * @type {module:openmct.ToolbarRegistry} + * @memberof module:openmct.MCT# + * @name toolbars + */ + ['toolbars', () => new ToolbarRegistry()], + + /** + * Registry for domain object types which may exist within this + * instance of Open MCT. + * + * @type {module:openmct.TypeRegistry} + * @memberof module:openmct.MCT# + * @name types + */ + ['types', () => new api.TypeRegistry()], + + /** + * An interface for interacting with domain objects and the domain + * object hierarchy. + * + * @type {module:openmct.ObjectAPI} + * @memberof module:openmct.MCT# + * @name objects + */ + ['objects', () => new api.ObjectAPI(this.types, this)], + + /** + * An interface for retrieving and interpreting telemetry data associated + * with a domain object. + * + * @type {module:openmct.TelemetryAPI} + * @memberof module:openmct.MCT# + * @name telemetry + */ + ['telemetry', () => new api.TelemetryAPI(this)], + + /** + * An interface for creating new indicators and changing them dynamically. + * + * @type {module:openmct.IndicatorAPI} + * @memberof module:openmct.MCT# + * @name indicators + */ + ['indicators', () => new api.IndicatorAPI(this)], + + /** + * MCT's user awareness management, to enable user and + * role specific functionality. + * @type {module:openmct.UserAPI} + * @memberof module:openmct.MCT# + * @name user + */ + ['user', () => new api.UserAPI(this)], + + ['notifications', () => new api.NotificationAPI()], + + ['editor', () => new api.EditorAPI(this)], + + ['overlays', () => new OverlayAPI()], + + ['tooltips', () => new ToolTipAPI()], + + ['menus', () => new api.MenuAPI(this)], + + ['actions', () => new api.ActionsAPI(this)], + + ['status', () => new api.StatusAPI(this)], + + ['priority', () => api.PriorityAPI], + + ['router', () => new ApplicationRouter(this)], + + ['faults', () => new api.FaultManagementAPI(this)], + + ['forms', () => new api.FormsAPI(this)], + + ['branding', () => BrandingAPI], /** * MCT's annotation API that enables @@ -268,169 +223,186 @@ define([ * @memberof module:openmct.MCT# * @name annotation */ - this.annotation = new api.AnnotationAPI(this); - - // Plugins that are installed by default - this.install(this.plugins.Plot()); - this.install(this.plugins.TelemetryTable.default()); - this.install(PreviewPlugin.default()); - this.install(LicensesPlugin.default()); - this.install(RemoveActionPlugin.default()); - this.install(MoveActionPlugin.default()); - this.install(LinkActionPlugin.default()); - this.install(DuplicateActionPlugin.default()); - this.install(ExportAsJSONAction.default()); - this.install(ImportFromJSONAction.default()); - this.install(this.plugins.FormActions.default()); - this.install(this.plugins.FolderView()); - this.install(this.plugins.Tabs()); - this.install(ImageryPlugin.default()); - this.install(this.plugins.FlexibleLayout()); - this.install(this.plugins.GoToOriginalAction()); - this.install(this.plugins.OpenInNewTabAction()); - this.install(this.plugins.WebPage()); - this.install(this.plugins.Condition()); - this.install(this.plugins.ConditionWidget()); - this.install(this.plugins.URLTimeSettingsSynchronizer()); - this.install(this.plugins.NotificationIndicator()); - this.install(this.plugins.NewFolderAction()); - this.install(this.plugins.ViewDatumAction()); - this.install(this.plugins.ViewLargeAction()); - this.install(this.plugins.ObjectInterceptors()); - this.install(this.plugins.DeviceClassifier()); - this.install(this.plugins.UserIndicator()); - this.install(this.plugins.Gauge()); - this.install(this.plugins.InspectorViews()); - } - - MCT.prototype = Object.create(EventEmitter.prototype); - - MCT.prototype.MCT = MCT; + ['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 + }); + }); /** - * Set path to where assets are hosted. This should be the path to main.js. + * MCT's annotation API that enables + * human-created comments and categorization linked to data products + * @type {module:openmct.AnnotationAPI} * @memberof module:openmct.MCT# - * @method setAssetPath + * @name annotation */ - MCT.prototype.setAssetPath = function (assetPath) { - this._assetPath = assetPath; - }; + this.annotation = new api.AnnotationAPI(this); + + // Plugins that are installed by default + this.install(this.plugins.Plot()); + this.install(this.plugins.TelemetryTable()); + this.install(PreviewPlugin()); + this.install(LicensesPlugin()); + this.install(RemoveActionPlugin()); + this.install(MoveActionPlugin()); + this.install(LinkActionPlugin()); + this.install(DuplicateActionPlugin()); + this.install(ExportAsJSONAction()); + this.install(ImportFromJSONAction()); + this.install(this.plugins.FormActions()); + this.install(this.plugins.FolderView()); + this.install(this.plugins.Tabs()); + this.install(ImageryPlugin()); + this.install(this.plugins.FlexibleLayout()); + this.install(this.plugins.GoToOriginalAction()); + this.install(this.plugins.OpenInNewTabAction()); + this.install(this.plugins.WebPage()); + this.install(this.plugins.Condition()); + this.install(this.plugins.ConditionWidget()); + this.install(this.plugins.URLTimeSettingsSynchronizer()); + this.install(this.plugins.NotificationIndicator()); + this.install(this.plugins.NewFolderAction()); + this.install(this.plugins.ViewDatumAction()); + this.install(this.plugins.ViewLargeAction()); + this.install(this.plugins.ObjectInterceptors()); + this.install(this.plugins.DeviceClassifier()); + this.install(this.plugins.UserIndicator()); + this.install(this.plugins.Gauge()); + this.install(this.plugins.InspectorViews()); +} + +MCT.prototype = Object.create(EventEmitter.prototype); + +MCT.prototype.MCT = MCT; + +/** + * Set path to where assets are hosted. This should be the path to main.js. + * @memberof module:openmct.MCT# + * @method setAssetPath + */ +MCT.prototype.setAssetPath = function (assetPath) { + this._assetPath = assetPath; +}; + +/** + * Get path to where assets are hosted. + * @memberof module:openmct.MCT# + * @method getAssetPath + */ +MCT.prototype.getAssetPath = function () { + const assetPathLength = this._assetPath && this._assetPath.length; + if (!assetPathLength) { + return '/'; + } - /** - * Get path to where assets are hosted. - * @memberof module:openmct.MCT# - * @method getAssetPath - */ - MCT.prototype.getAssetPath = function () { - const assetPathLength = this._assetPath && this._assetPath.length; - if (!assetPathLength) { - return '/'; - } + if (this._assetPath[assetPathLength - 1] !== '/') { + return this._assetPath + '/'; + } - if (this._assetPath[assetPathLength - 1] !== '/') { - return this._assetPath + '/'; - } + return this._assetPath; +}; + +/** + * Start running Open MCT. This should be called only after any plugins + * have been installed. + * @fires module:openmct.MCT~start + * @memberof module:openmct.MCT# + * @method start + * @param {HTMLElement} [domElement] the DOM element in which to run + * MCT; if undefined, MCT will be run in the body of the document + */ +MCT.prototype.start = function ( + domElement = document.body.firstElementChild, + isHeadlessMode = false +) { + // Create element to mount Layout if it doesn't exist + if (domElement === null) { + domElement = document.createElement('div'); + document.body.appendChild(domElement); + } + domElement.id = 'openmct-app'; + + if (this.types.get('layout') === undefined) { + this.install( + this.plugins.DisplayLayout({ + showAsView: ['summary-widget'] + }) + ); + } - return this._assetPath; - }; + this.element = domElement; + + if (!this.time.getClock()) { + this.time.setClock(this.defaultClock); + } + + this.router.route(/^\/$/, () => { + this.router.setPath('/browse/'); + }); /** - * Start running Open MCT. This should be called only after any plugins - * have been installed. - * @fires module:openmct.MCT~start - * @memberof module:openmct.MCT# - * @method start - * @param {HTMLElement} [domElement] the DOM element in which to run - * MCT; if undefined, MCT will be run in the body of the document + * Fired by [MCT]{@link module:openmct.MCT} when the application + * is started. + * @event start + * @memberof module:openmct.MCT~ */ - MCT.prototype.start = function ( - domElement = document.body.firstElementChild, - isHeadlessMode = false - ) { - // Create element to mount Layout if it doesn't exist - if (domElement === null) { - domElement = document.createElement('div'); - document.body.appendChild(domElement); - } - domElement.id = 'openmct-app'; - - if (this.types.get('layout') === undefined) { - this.install( - this.plugins.DisplayLayout({ - showAsView: ['summary-widget'] - }) - ); - } - - this.element = domElement; - - if (!this.time.getClock()) { - this.time.setClock(this.defaultClock); - } - - this.router.route(/^\/$/, () => { - this.router.setPath('/browse/'); - }); - - /** - * Fired by [MCT]{@link module:openmct.MCT} when the application - * is started. - * @event start - * @memberof module:openmct.MCT~ - */ - if (!isHeadlessMode) { - const appLayout = Vue.createApp({ - components: { - Layout: Layout.default - }, - provide: { - openmct: Vue.markRaw(this) - }, - template: '' - }); - const component = appLayout.mount(domElement); - component.$nextTick(() => { - this.layout = component.$refs.layout; - this.app = appLayout; - Browse(this); - window.addEventListener('beforeunload', this.destroy); - this.router.start(); - this.emit('start'); - }); - } else { + if (!isHeadlessMode) { + const appLayout = Vue.createApp({ + components: { + Layout: Layout + }, + provide: { + openmct: Vue.markRaw(this) + }, + template: '' + }); + const component = appLayout.mount(domElement); + component.$nextTick(() => { + this.layout = component.$refs.layout; + this.app = appLayout; + Browse(this); window.addEventListener('beforeunload', this.destroy); - this.router.start(); this.emit('start'); - } - }; - - MCT.prototype.startHeadless = function () { - let unreachableNode = document.createElement('div'); - - return this.start(unreachableNode, true); - }; + }); + } else { + window.addEventListener('beforeunload', this.destroy); - /** - * Install a plugin in MCT. - * - * @param {Function} plugin a plugin install function which will be - * invoked with the mct instance. - * @memberof module:openmct.MCT# - */ - MCT.prototype.install = function (plugin) { - plugin(this); - }; + this.router.start(); + this.emit('start'); + } +}; - MCT.prototype.destroy = function () { - window.removeEventListener('beforeunload', this.destroy); - this.emit('destroy'); - this.router.destroy(); - }; +MCT.prototype.startHeadless = function () { + let unreachableNode = document.createElement('div'); - MCT.prototype.plugins = plugins; - MCT.prototype.components = components.default; + return this.start(unreachableNode, true); +}; - return MCT; -}); +/** + * Install a plugin in MCT. + * + * @param {Function} plugin a plugin install function which will be + * invoked with the mct instance. + * @memberof module:openmct.MCT# + */ +MCT.prototype.install = function (plugin) { + plugin(this); +}; + +MCT.prototype.destroy = function () { + window.removeEventListener('beforeunload', this.destroy); + this.emit('destroy'); + this.router.destroy(); +}; + +MCT.prototype.plugins = plugins; +MCT.prototype.components = components; diff --git a/src/MCTSpec.js b/src/MCTSpec.js index eaccac03fbd..f259ce413a6 100644 --- a/src/MCTSpec.js +++ b/src/MCTSpec.js @@ -20,96 +20,98 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define(['./plugins/plugins', 'utils/testing'], function (plugins, testUtils) { - describe('MCT', function () { - let openmct; - let mockPlugin; - let mockPlugin2; - let mockListener; - - beforeEach(function () { - mockPlugin = jasmine.createSpy('plugin'); - mockPlugin2 = jasmine.createSpy('plugin2'); - mockListener = jasmine.createSpy('listener'); - - openmct = testUtils.createOpenMct(); - - openmct.install(mockPlugin); - openmct.install(mockPlugin2); - openmct.on('start', mockListener); +import testUtils from 'utils/testing'; + +import plugins from './plugins/plugins'; + +describe('MCT', function () { + let openmct; + let mockPlugin; + let mockPlugin2; + let mockListener; + + beforeEach(function () { + mockPlugin = jasmine.createSpy('plugin'); + mockPlugin2 = jasmine.createSpy('plugin2'); + mockListener = jasmine.createSpy('listener'); + + openmct = testUtils.createOpenMct(); + + openmct.install(mockPlugin); + openmct.install(mockPlugin2); + openmct.on('start', mockListener); + }); + + // Clean up the dirty singleton. + afterEach(function () { + return testUtils.resetApplicationState(openmct); + }); + + it('exposes plugins', function () { + expect(openmct.plugins).toEqual(plugins); + }); + + it('does not issue a start event before started', function () { + expect(mockListener).not.toHaveBeenCalled(); + }); + + describe('start', function () { + let appHolder; + beforeEach(function (done) { + appHolder = document.createElement('div'); + openmct.on('start', done); + openmct.start(appHolder); + }); + + it('calls plugins for configuration', function () { + expect(mockPlugin).toHaveBeenCalledWith(openmct); + expect(mockPlugin2).toHaveBeenCalledWith(openmct); }); - // Clean up the dirty singleton. - afterEach(function () { - return testUtils.resetApplicationState(openmct); + it('emits a start event', function () { + expect(mockListener).toHaveBeenCalled(); }); - it('exposes plugins', function () { - expect(openmct.plugins).toEqual(plugins); + it('Renders the application into the provided container element', function () { + let openMctShellElements = appHolder.querySelectorAll('div.l-shell'); + expect(openMctShellElements.length).toBe(1); }); + }); - it('does not issue a start event before started', function () { - expect(mockListener).not.toHaveBeenCalled(); + describe('startHeadless', function () { + beforeEach(function (done) { + openmct.on('start', done); + openmct.startHeadless(); }); - describe('start', function () { - let appHolder; - beforeEach(function (done) { - appHolder = document.createElement('div'); - openmct.on('start', done); - openmct.start(appHolder); - }); - - it('calls plugins for configuration', function () { - expect(mockPlugin).toHaveBeenCalledWith(openmct); - expect(mockPlugin2).toHaveBeenCalledWith(openmct); - }); - - it('emits a start event', function () { - expect(mockListener).toHaveBeenCalled(); - }); - - it('Renders the application into the provided container element', function () { - let openMctShellElements = appHolder.querySelectorAll('div.l-shell'); - expect(openMctShellElements.length).toBe(1); - }); + it('calls plugins for configuration', function () { + expect(mockPlugin).toHaveBeenCalledWith(openmct); + expect(mockPlugin2).toHaveBeenCalledWith(openmct); }); - describe('startHeadless', function () { - beforeEach(function (done) { - openmct.on('start', done); - openmct.startHeadless(); - }); - - it('calls plugins for configuration', function () { - expect(mockPlugin).toHaveBeenCalledWith(openmct); - expect(mockPlugin2).toHaveBeenCalledWith(openmct); - }); - - it('emits a start event', function () { - expect(mockListener).toHaveBeenCalled(); - }); - - it('Does not render Open MCT', function () { - let openMctShellElements = document.body.querySelectorAll('div.l-shell'); - expect(openMctShellElements.length).toBe(0); - }); + it('emits a start event', function () { + expect(mockListener).toHaveBeenCalled(); }); - describe('setAssetPath', function () { - let testAssetPath; + it('Does not render Open MCT', function () { + let openMctShellElements = document.body.querySelectorAll('div.l-shell'); + expect(openMctShellElements.length).toBe(0); + }); + }); - it('configures the path for assets', function () { - testAssetPath = 'some/path/'; - openmct.setAssetPath(testAssetPath); - expect(openmct.getAssetPath()).toBe(testAssetPath); - }); + describe('setAssetPath', function () { + let testAssetPath; + + it('configures the path for assets', function () { + testAssetPath = 'some/path/'; + openmct.setAssetPath(testAssetPath); + expect(openmct.getAssetPath()).toBe(testAssetPath); + }); - it('adds a trailing /', function () { - testAssetPath = 'some/path'; - openmct.setAssetPath(testAssetPath); - expect(openmct.getAssetPath()).toBe(testAssetPath + '/'); - }); + it('adds a trailing /', function () { + testAssetPath = 'some/path'; + openmct.setAssetPath(testAssetPath); + expect(openmct.getAssetPath()).toBe(testAssetPath + '/'); }); }); }); diff --git a/src/api/api.js b/src/api/api.js index e13839e1375..e9d7fad84a6 100644 --- a/src/api/api.js +++ b/src/api/api.js @@ -20,57 +20,38 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define([ - './actions/ActionsAPI', - './composition/CompositionAPI', - './Editor', - './faultmanagement/FaultManagementAPI', - './forms/FormsAPI', - './indicators/IndicatorAPI', - './menu/MenuAPI', - './notifications/NotificationAPI', - './objects/ObjectAPI', - './priority/PriorityAPI', - './status/StatusAPI', - './telemetry/TelemetryAPI', - './time/TimeAPI', - './types/TypeRegistry', - './user/UserAPI', - './annotation/AnnotationAPI' -], function ( - ActionsAPI, - CompositionAPI, - EditorAPI, - FaultManagementAPI, - FormsAPI, - IndicatorAPI, - MenuAPI, - NotificationAPI, - ObjectAPI, - PriorityAPI, - StatusAPI, - TelemetryAPI, - TimeAPI, - TypeRegistry, - UserAPI, - AnnotationAPI -) { - return { - ActionsAPI: ActionsAPI.default, - CompositionAPI: CompositionAPI, - EditorAPI: EditorAPI, - FaultManagementAPI: FaultManagementAPI, - FormsAPI: FormsAPI, - IndicatorAPI: IndicatorAPI.default, - MenuAPI: MenuAPI.default, - NotificationAPI: NotificationAPI.default, - ObjectAPI: ObjectAPI, - PriorityAPI: PriorityAPI.default, - StatusAPI: StatusAPI.default, - TelemetryAPI: TelemetryAPI, - TimeAPI: TimeAPI.default, - TypeRegistry: TypeRegistry.default, - UserAPI: UserAPI.default, - AnnotationAPI: AnnotationAPI.default - }; -}); +import ActionsAPI from './actions/ActionsAPI'; +import AnnotationAPI from './annotation/AnnotationAPI'; +import CompositionAPI from './composition/CompositionAPI'; +import EditorAPI from './Editor'; +import FaultManagementAPI from './faultmanagement/FaultManagementAPI'; +import FormsAPI from './forms/FormsAPI'; +import IndicatorAPI from './indicators/IndicatorAPI'; +import MenuAPI from './menu/MenuAPI'; +import NotificationAPI from './notifications/NotificationAPI'; +import ObjectAPI from './objects/ObjectAPI'; +import PriorityAPI from './priority/PriorityAPI'; +import StatusAPI from './status/StatusAPI'; +import TelemetryAPI from './telemetry/TelemetryAPI'; +import TimeAPI from './time/TimeAPI'; +import TypeRegistry from './types/TypeRegistry'; +import UserAPI from './user/UserAPI'; + +export default { + ActionsAPI: ActionsAPI, + CompositionAPI: CompositionAPI, + EditorAPI: EditorAPI, + FaultManagementAPI: FaultManagementAPI, + FormsAPI: FormsAPI, + IndicatorAPI: IndicatorAPI, + MenuAPI: MenuAPI, + NotificationAPI: NotificationAPI, + ObjectAPI: ObjectAPI, + PriorityAPI: PriorityAPI, + StatusAPI: StatusAPI, + TelemetryAPI: TelemetryAPI, + TimeAPI: TimeAPI, + TypeRegistry: TypeRegistry, + UserAPI: UserAPI, + AnnotationAPI: AnnotationAPI +}; diff --git a/src/api/objects/object-utils.js b/src/api/objects/object-utils.js index 08d2c01ffc2..64e92254666 100644 --- a/src/api/objects/object-utils.js +++ b/src/api/objects/object-utils.js @@ -20,163 +20,161 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define([], function () { - /** - * Utility for checking if a thing is an Open MCT Identifier. - * @private - */ - function isIdentifier(thing) { - return ( - typeof thing === 'object' && - Object.prototype.hasOwnProperty.call(thing, 'key') && - Object.prototype.hasOwnProperty.call(thing, 'namespace') - ); - } - - /** - * Utility for checking if a thing is a key string. Not perfect. - * @private - */ - function isKeyString(thing) { - return typeof thing === 'string'; +/** + * Utility for checking if a thing is an Open MCT Identifier. + * @private + */ +function isIdentifier(thing) { + return ( + typeof thing === 'object' && + Object.prototype.hasOwnProperty.call(thing, 'key') && + Object.prototype.hasOwnProperty.call(thing, 'namespace') + ); +} + +/** + * Utility for checking if a thing is a key string. Not perfect. + * @private + */ +function isKeyString(thing) { + return typeof thing === 'string'; +} + +/** + * Convert a keyString into an Open MCT Identifier, ex: + * 'scratch:root' ==> {namespace: 'scratch', key: 'root'} + * + * Idempotent. + * + * @param keyString + * @returns identifier + */ +function parseKeyString(keyString) { + if (isIdentifier(keyString)) { + return keyString; } - /** - * Convert a keyString into an Open MCT Identifier, ex: - * 'scratch:root' ==> {namespace: 'scratch', key: 'root'} - * - * Idempotent. - * - * @param keyString - * @returns identifier - */ - function parseKeyString(keyString) { - if (isIdentifier(keyString)) { - return keyString; - } - - let namespace = ''; - let key = keyString; - for (let i = 0; i < key.length; i++) { - if (key[i] === '\\' && key[i + 1] === ':') { - i++; // skip escape character. - } else if (key[i] === ':') { - key = key.slice(i + 1); - break; - } - - namespace += key[i]; - } - - if (keyString === namespace) { - namespace = ''; + 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; } - return { - namespace: namespace, - key: key - }; + namespace += key[i]; } - /** - * 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 (keyString === namespace) { + namespace = ''; + } - if (!identifier.namespace) { - return identifier.key; - } + return { + namespace: namespace, + key: key + }; +} - return [identifier.namespace.replace(/:/g, '\\:'), identifier.key].join(':'); +/** + * 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'); } - /** - * 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); - } + if (isKeyString(identifier)) { + return identifier; + } - return model; + if (!identifier.namespace) { + return identifier.key; } - /** - * 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 [identifier.namespace.replace(/:/g, '\\:'), identifier.key].join(':'); +} - return model; +/** + * 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); } - /** - * 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; - } + return model; +} - /** - * 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); +/** + * 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); } - function refresh(oldObject, newObject) { - let deleted = _.difference(Object.keys(oldObject), Object.keys(newObject)); - deleted.forEach((propertyName) => delete oldObject[propertyName]); - Object.assign(oldObject, newObject); - } + return model; +} - return { - isIdentifier: isIdentifier, - toOldFormat: toOldFormat, - toNewFormat: toNewFormat, - makeKeyString: makeKeyString, - parseKeyString: parseKeyString, - equals: objectEquals, - identifierEquals: identifierEquals, - refresh: refresh - }; -}); +/** + * 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 +}; diff --git a/src/api/objects/test/object-utilsSpec.js b/src/api/objects/test/object-utilsSpec.js index 9555190e98f..3a9561272ad 100644 --- a/src/api/objects/test/object-utilsSpec.js +++ b/src/api/objects/test/object-utilsSpec.js @@ -1,89 +1,127 @@ -define(['objectUtils'], function (objectUtils) { - describe('objectUtils', function () { - describe('keyString util', function () { - const EXPECTATIONS = { - ROOT: { - namespace: '', - key: 'ROOT' - }, - mine: { - namespace: '', - key: 'mine' - }, - 'extended:something:with:colons': { - key: 'something:with:colons', - namespace: 'extended' - }, - 'https\\://some/url:resourceId': { - key: 'resourceId', - namespace: 'https://some/url' - }, - 'scratch:root': { - namespace: 'scratch', - key: 'root' - }, - 'thingy\\:thing:abc123': { - namespace: 'thingy:thing', - key: 'abc123' - } - }; +import objectUtils from './objectUtils'; - Object.keys(EXPECTATIONS).forEach(function (keyString) { - it('parses "' + keyString + '".', function () { - expect(objectUtils.parseKeyString(keyString)).toEqual(EXPECTATIONS[keyString]); - }); +describe('objectUtils', function () { + describe('keyString util', function () { + const EXPECTATIONS = { + ROOT: { + namespace: '', + key: 'ROOT' + }, + mine: { + namespace: '', + key: 'mine' + }, + 'extended:something:with:colons': { + key: 'something:with:colons', + namespace: 'extended' + }, + 'https\\://some/url:resourceId': { + key: 'resourceId', + namespace: 'https://some/url' + }, + 'scratch:root': { + namespace: 'scratch', + key: 'root' + }, + 'thingy\\:thing:abc123': { + namespace: 'thingy:thing', + key: 'abc123' + } + }; + + Object.keys(EXPECTATIONS).forEach(function (keyString) { + it('parses "' + keyString + '".', function () { + expect(objectUtils.parseKeyString(keyString)).toEqual(EXPECTATIONS[keyString]); + }); - it('parses and re-encodes "' + keyString + '"', function () { - const identifier = objectUtils.parseKeyString(keyString); - expect(objectUtils.makeKeyString(identifier)).toEqual(keyString); - }); + it('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); + 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); - }); + 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({ + describe('old object conversions', function () { + it('translate ids', function () { + expect( + objectUtils.toNewFormat( + { + prop: 'someValue' + }, + 'objId' + ) + ).toEqual({ + prop: 'someValue', + identifier: { + namespace: '', + key: 'objId' + } + }); + }); + + it('translates composition', function () { + expect( + objectUtils.toNewFormat( + { + prop: 'someValue', + composition: ['anotherObjectId', 'scratch:anotherObjectId'] + }, + 'objId' + ) + ).toEqual({ + prop: 'someValue', + composition: [ + { + namespace: '', + key: 'anotherObjectId' + }, + { + namespace: 'scratch', + key: 'anotherObjectId' + } + ], + identifier: { + namespace: '', + key: 'objId' + } + }); + }); + }); + + describe('new object conversions', function () { + it('removes ids', function () { + expect( + objectUtils.toOldFormat({ prop: 'someValue', identifier: { namespace: '', key: 'objId' } - }); + }) + ).toEqual({ + prop: 'someValue' }); + }); - it('translates composition', function () { - expect( - objectUtils.toNewFormat( - { - prop: 'someValue', - composition: ['anotherObjectId', 'scratch:anotherObjectId'] - }, - 'objId' - ) - ).toEqual({ + it('translates composition', function () { + expect( + objectUtils.toOldFormat({ prop: 'someValue', composition: [ { @@ -99,48 +137,10 @@ define(['objectUtils'], function (objectUtils) { namespace: '', key: 'objId' } - }); - }); - }); - - describe('new object conversions', function () { - it('removes ids', function () { - expect( - objectUtils.toOldFormat({ - prop: 'someValue', - identifier: { - namespace: '', - key: 'objId' - } - }) - ).toEqual({ - prop: 'someValue' - }); - }); - - it('translates composition', function () { - expect( - objectUtils.toOldFormat({ - prop: 'someValue', - composition: [ - { - namespace: '', - key: 'anotherObjectId' - }, - { - namespace: 'scratch', - key: 'anotherObjectId' - } - ], - identifier: { - namespace: '', - key: 'objId' - } - }) - ).toEqual({ - prop: 'someValue', - composition: ['anotherObjectId', 'scratch:anotherObjectId'] - }); + }) + ).toEqual({ + prop: 'someValue', + composition: ['anotherObjectId', 'scratch:anotherObjectId'] }); }); }); diff --git a/src/api/telemetry/DefaultMetadataProvider.js b/src/api/telemetry/DefaultMetadataProvider.js index 15ca73c168e..29a3ce2e8ae 100644 --- a/src/api/telemetry/DefaultMetadataProvider.js +++ b/src/api/telemetry/DefaultMetadataProvider.js @@ -20,17 +20,19 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define(['lodash'], function (_) { - /** - * This is the default metadata provider; for any object with a "telemetry" - * property, this provider will return the value of that property as the - * telemetry metadata. - * - * This provider also implements legacy support for telemetry metadata - * defined on the type. Telemetry metadata definitions on type will be - * depreciated in the future. - */ - function DefaultMetadataProvider(openmct) { +import _ from 'lodash'; + +/** + * This is the default metadata provider; for any object with a "telemetry" + * property, this provider will return the value of that property as the + * telemetry metadata. + * + * This provider also implements legacy support for telemetry metadata + * defined on the type. Telemetry metadata definitions on type will be + * depreciated in the future. + */ +export default class DefaultMetadataProvider { + constructor(openmct) { this.openmct = openmct; } @@ -38,65 +40,14 @@ define(['lodash'], function (_) { * Applies to any domain object with a telemetry property, or whose type * definition has a telemetry property. */ - DefaultMetadataProvider.prototype.supportsMetadata = function (domainObject) { + supportsMetadata(domainObject) { return Boolean(domainObject.telemetry) || Boolean(this.typeHasTelemetry(domainObject)); - }; - - /** - * Retrieves valueMetadata from legacy metadata. - * @private - */ - function valueMetadatasFromOldFormat(metadata) { - const valueMetadatas = []; - - valueMetadatas.push({ - key: 'name', - name: 'Name' - }); - - metadata.domains.forEach(function (domain, index) { - const valueMetadata = _.clone(domain); - valueMetadata.hints = { - domain: index + 1 - }; - valueMetadatas.push(valueMetadata); - }); - - metadata.ranges.forEach(function (range, index) { - const valueMetadata = _.clone(range); - valueMetadata.hints = { - range: index, - priority: index + metadata.domains.length + 1 - }; - - if (valueMetadata.type === 'enum') { - valueMetadata.key = 'enum'; - valueMetadata.hints.y -= 10; - valueMetadata.hints.range -= 10; - valueMetadata.enumerations = _.sortBy( - valueMetadata.enumerations.map(function (e) { - return { - string: e.string, - value: Number(e.value) - }; - }), - 'e.value' - ); - valueMetadata.values = valueMetadata.enumerations.map((e) => e.value); - valueMetadata.max = Math.max(valueMetadata.values); - valueMetadata.min = Math.min(valueMetadata.values); - } - - valueMetadatas.push(valueMetadata); - }); - - return valueMetadatas; } /** * Returns telemetry metadata for a given domain object. */ - DefaultMetadataProvider.prototype.getMetadata = function (domainObject) { + getMetadata(domainObject) { const metadata = domainObject.telemetry || {}; if (this.typeHasTelemetry(domainObject)) { const typeMetadata = this.openmct.types.get(domainObject.type).definition.telemetry; @@ -109,16 +60,65 @@ define(['lodash'], function (_) { } return metadata; - }; + } /** * @private */ - DefaultMetadataProvider.prototype.typeHasTelemetry = function (domainObject) { + typeHasTelemetry(domainObject) { const type = this.openmct.types.get(domainObject.type); return Boolean(type.definition.telemetry); - }; + } +} + +/** + * 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 DefaultMetadataProvider; -}); + return valueMetadatas; +} diff --git a/src/api/telemetry/TelemetryMetadataManager.js b/src/api/telemetry/TelemetryMetadataManager.js index 20480597970..547b3e2d027 100644 --- a/src/api/telemetry/TelemetryMetadataManager.js +++ b/src/api/telemetry/TelemetryMetadataManager.js @@ -20,143 +20,141 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define(['lodash'], function (_) { - function applyReasonableDefaults(valueMetadata, index) { - valueMetadata.source = valueMetadata.source || valueMetadata.key; - valueMetadata.hints = valueMetadata.hints || {}; +import _ from 'lodash'; - if (Object.prototype.hasOwnProperty.call(valueMetadata.hints, 'x')) { - if (!Object.prototype.hasOwnProperty.call(valueMetadata.hints, 'domain')) { - valueMetadata.hints.domain = valueMetadata.hints.x; - } +function applyReasonableDefaults(valueMetadata, index) { + valueMetadata.source = valueMetadata.source || valueMetadata.key; + valueMetadata.hints = valueMetadata.hints || {}; - delete valueMetadata.hints.x; + if (Object.prototype.hasOwnProperty.call(valueMetadata.hints, 'x')) { + if (!Object.prototype.hasOwnProperty.call(valueMetadata.hints, 'domain')) { + valueMetadata.hints.domain = valueMetadata.hints.x; } - if (Object.prototype.hasOwnProperty.call(valueMetadata.hints, 'y')) { - if (!Object.prototype.hasOwnProperty.call(valueMetadata.hints, 'range')) { - valueMetadata.hints.range = valueMetadata.hints.y; - } + delete valueMetadata.hints.x; + } - delete valueMetadata.hints.y; + if (Object.prototype.hasOwnProperty.call(valueMetadata.hints, 'y')) { + if (!Object.prototype.hasOwnProperty.call(valueMetadata.hints, 'range')) { + valueMetadata.hints.range = 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; - } + delete valueMetadata.hints.y; + } - if (!Object.prototype.hasOwnProperty.call(valueMetadata, 'min')) { - valueMetadata.min = Math.min(valueMetadata.values) - 1; - } + if (valueMetadata.format === 'enum') { + if (!valueMetadata.values) { + valueMetadata.values = valueMetadata.enumerations.map((e) => e.value); } - if (!Object.prototype.hasOwnProperty.call(valueMetadata.hints, 'priority')) { - valueMetadata.hints.priority = index; + if (!Object.prototype.hasOwnProperty.call(valueMetadata, 'max')) { + valueMetadata.max = Math.max(valueMetadata.values) + 1; } - return valueMetadata; + if (!Object.prototype.hasOwnProperty.call(valueMetadata, 'min')) { + valueMetadata.min = Math.min(valueMetadata.values) - 1; + } } - /** - * Utility class for handling and inspecting telemetry metadata. Applies - * reasonable defaults to simplify the task of providing metadata, while - * also providing methods for interrogating telemetry metadata. - */ - function TelemetryMetadataManager(metadata) { - this.metadata = metadata; - - this.valueMetadatas = this.metadata.values - ? this.metadata.values.map(applyReasonableDefaults) - : []; + if (!Object.prototype.hasOwnProperty.call(valueMetadata.hints, 'priority')) { + valueMetadata.hints.priority = index; } - /** - * 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); - } + 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); + } - 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; - } + function hasHints(metadata) { + return hints.every(hasHint, metadata); + } - return (metadata.format || metadata.formatString).match(regex) !== null; - }; + 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; + } - TelemetryMetadataManager.prototype.getFilterableValues = function () { - return this.valueMetadatas.filter( - (metadatum) => metadatum.filters && metadatum.filters.length > 0 - ); - }; + return (metadata.format || metadata.formatString).match(regex) !== null; +}; - TelemetryMetadataManager.prototype.getUseToUpdateInPlaceValue = function () { - return this.valueMetadatas.find(this.isInPlaceUpdateValue); - }; +TelemetryMetadataManager.prototype.getFilterableValues = function () { + return this.valueMetadatas.filter( + (metadatum) => metadatum.filters && metadatum.filters.length > 0 + ); +}; - TelemetryMetadataManager.prototype.isInPlaceUpdateValue = function (metadatum) { - return metadatum.useToUpdateInPlace === true; - }; +TelemetryMetadataManager.prototype.getUseToUpdateInPlaceValue = function () { + return this.valueMetadatas.find(this.isInPlaceUpdateValue); +}; - TelemetryMetadataManager.prototype.getDefaultDisplayValue = function () { - let valueMetadata = this.valuesForHints(['range'])[0]; +TelemetryMetadataManager.prototype.isInPlaceUpdateValue = function (metadatum) { + return metadatum.useToUpdateInPlace === true; +}; - if (valueMetadata === undefined) { - valueMetadata = this.values().filter((values) => { - return !values.hints.domain; - })[0]; - } +TelemetryMetadataManager.prototype.getDefaultDisplayValue = function () { + let valueMetadata = this.valuesForHints(['range'])[0]; - if (valueMetadata === undefined) { - valueMetadata = this.values()[0]; - } + if (valueMetadata === undefined) { + valueMetadata = this.values().filter((values) => { + return !values.hints.domain; + })[0]; + } - return valueMetadata; - }; + if (valueMetadata === undefined) { + valueMetadata = this.values()[0]; + } - return TelemetryMetadataManager; -}); + return valueMetadata; +}; diff --git a/src/api/telemetry/TelemetryValueFormatter.js b/src/api/telemetry/TelemetryValueFormatter.js index f97d650ebe1..f4aad8f6e03 100644 --- a/src/api/telemetry/TelemetryValueFormatter.js +++ b/src/api/telemetry/TelemetryValueFormatter.js @@ -20,133 +20,132 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define(['lodash', 'printj'], function (_, printj) { - // TODO: needs reference to formatService; - function TelemetryValueFormatter(valueMetadata, formatMap) { - const numberFormatter = { - parse: function (x) { - return Number(x); - }, - format: function (x) { - return x; - }, - validate: function (x) { - return true; - } - }; - - this.valueMetadata = valueMetadata; - - function getNonArrayValue(value) { - //metadata format could have array formats ex. string[]/number[] - const arrayRegex = /\[\]$/g; - if (value && value.match(arrayRegex)) { - return value.replace(arrayRegex, ''); - } - - return value; +import _ from 'lodash'; +import printj from 'printj'; + +// TODO: needs reference to formatService; +export default function TelemetryValueFormatter(valueMetadata, formatMap) { + const numberFormatter = { + parse: function (x) { + return Number(x); + }, + format: function (x) { + return x; + }, + validate: function (x) { + return true; } + }; - let valueMetadataFormat = getNonArrayValue(valueMetadata.format); - - //Is there an existing formatter for the format specified? If not, default to number format - this.formatter = formatMap.get(valueMetadataFormat) || numberFormatter; + this.valueMetadata = valueMetadata; - if (valueMetadataFormat === 'enum') { - this.formatter = {}; - this.enumerations = valueMetadata.enumerations.reduce( - function (vm, e) { - vm.byValue[e.value] = e.string; - vm.byString[e.string] = e.value; + function getNonArrayValue(value) { + //metadata format could have array formats ex. string[]/number[] + const arrayRegex = /\[\]$/g; + if (value && value.match(arrayRegex)) { + return value.replace(arrayRegex, ''); + } - return vm; - }, - { - byValue: {}, - byString: {} - } - ); - this.formatter.format = function (value) { - if (Object.prototype.hasOwnProperty.call(this.enumerations.byValue, value)) { - return this.enumerations.byValue[value]; - } + return value; + } - return value; - }.bind(this); - this.formatter.parse = function (string) { - if (typeof string === 'string') { - if (Object.prototype.hasOwnProperty.call(this.enumerations.byString, string)) { - return this.enumerations.byString[string]; - } - } + let valueMetadataFormat = getNonArrayValue(valueMetadata.format); - return Number(string); - }.bind(this); - } + //Is there an existing formatter for the format specified? If not, default to number format + this.formatter = formatMap.get(valueMetadataFormat) || numberFormatter; - // Check for formatString support once instead of per format call. - if (valueMetadata.formatString) { - const baseFormat = this.formatter.format; - const formatString = getNonArrayValue(valueMetadata.formatString); - this.formatter.format = function (value) { - return printj.sprintf(formatString, baseFormat.call(this, value)); - }; - } + if (valueMetadataFormat === 'enum') { + this.formatter = {}; + this.enumerations = valueMetadata.enumerations.reduce( + function (vm, e) { + vm.byValue[e.value] = e.string; + vm.byString[e.string] = e.value; - if (valueMetadataFormat === 'string') { - this.formatter.parse = function (value) { - if (value === undefined) { - return ''; - } + return vm; + }, + { + byValue: {}, + byString: {} + } + ); + this.formatter.format = function (value) { + if (Object.prototype.hasOwnProperty.call(this.enumerations.byValue, value)) { + return this.enumerations.byValue[value]; + } - if (typeof value === 'string') { - return value; - } else { - return value.toString(); + return value; + }.bind(this); + this.formatter.parse = function (string) { + if (typeof string === 'string') { + if (Object.prototype.hasOwnProperty.call(this.enumerations.byString, string)) { + return this.enumerations.byString[string]; } - }; + } - this.formatter.format = function (value) { - return value; - }; + return Number(string); + }.bind(this); + } - this.formatter.validate = function (value) { - return typeof value === 'string'; - }; - } + // Check for formatString support once instead of per format call. + if (valueMetadata.formatString) { + const baseFormat = this.formatter.format; + const formatString = getNonArrayValue(valueMetadata.formatString); + this.formatter.format = function (value) { + return printj.sprintf(formatString, baseFormat.call(this, value)); + }; } - TelemetryValueFormatter.prototype.parse = function (datum) { - const isDatumArray = Array.isArray(datum); - if (_.isObject(datum)) { - const objectDatum = isDatumArray ? datum : datum[this.valueMetadata.source]; - if (Array.isArray(objectDatum)) { - return objectDatum.map((item) => { - return this.formatter.parse(item); - }); - } else { - return this.formatter.parse(objectDatum); + if (valueMetadataFormat === 'string') { + this.formatter.parse = function (value) { + if (value === undefined) { + return ''; } - } - return this.formatter.parse(datum); - }; - - TelemetryValueFormatter.prototype.format = function (datum) { - const isDatumArray = Array.isArray(datum); - if (_.isObject(datum)) { - const objectDatum = isDatumArray ? datum : datum[this.valueMetadata.source]; - if (Array.isArray(objectDatum)) { - return objectDatum.map((item) => { - return this.formatter.format(item); - }); + if (typeof value === 'string') { + return value; } else { - return this.formatter.format(objectDatum); + return value.toString(); } + }; + + this.formatter.format = function (value) { + return value; + }; + + this.formatter.validate = function (value) { + return typeof value === 'string'; + }; + } +} + +TelemetryValueFormatter.prototype.parse = function (datum) { + const isDatumArray = Array.isArray(datum); + if (_.isObject(datum)) { + const objectDatum = isDatumArray ? datum : datum[this.valueMetadata.source]; + if (Array.isArray(objectDatum)) { + return objectDatum.map((item) => { + return this.formatter.parse(item); + }); + } else { + return this.formatter.parse(objectDatum); } + } - return this.formatter.format(datum); - }; + return this.formatter.parse(datum); +}; + +TelemetryValueFormatter.prototype.format = function (datum) { + const isDatumArray = Array.isArray(datum); + if (_.isObject(datum)) { + const objectDatum = isDatumArray ? datum : datum[this.valueMetadata.source]; + if (Array.isArray(objectDatum)) { + return objectDatum.map((item) => { + return this.formatter.format(item); + }); + } else { + return this.formatter.format(objectDatum); + } + } - return TelemetryValueFormatter; -}); + return this.formatter.format(datum); +}; diff --git a/src/plugins/URLIndicatorPlugin/URLIndicator.js b/src/plugins/URLIndicatorPlugin/URLIndicator.js index f8a35932355..3523d6e4e79 100644 --- a/src/plugins/URLIndicatorPlugin/URLIndicator.js +++ b/src/plugins/URLIndicatorPlugin/URLIndicator.js @@ -20,96 +20,92 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define([], function () { - // Set of connection states; changing among these states will be - // reflected in the indicator's appearance. - // CONNECTED: Everything nominal, expect to be able to read/write. - // DISCONNECTED: HTTP failed; maybe misconfigured, disconnected. - // PENDING: Still trying to connect, and haven't failed yet. - const CONNECTED = { - statusClass: 's-status-on' - }; - const PENDING = { - statusClass: 's-status-warning-lo' - }; - const DISCONNECTED = { - statusClass: 's-status-warning-hi' - }; - function URLIndicator(options, simpleIndicator) { - this.bindMethods(); - this.count = 0; +// Set of connection states; changing among these states will be +// reflected in the indicator's appearance. +// CONNECTED: Everything nominal, expect to be able to read/write. +// DISCONNECTED: HTTP failed; maybe misconfigured, disconnected. +// PENDING: Still trying to connect, and haven't failed yet. +const CONNECTED = { + statusClass: 's-status-on' +}; +const PENDING = { + statusClass: 's-status-warning-lo' +}; +const DISCONNECTED = { + statusClass: 's-status-warning-hi' +}; +export default function URLIndicator(options, simpleIndicator) { + this.bindMethods(); + this.count = 0; - this.indicator = simpleIndicator; - this.setDefaultsFromOptions(options); - this.setIndicatorToState(PENDING); + this.indicator = simpleIndicator; + this.setDefaultsFromOptions(options); + this.setIndicatorToState(PENDING); - this.fetchUrl(); - setInterval(this.fetchUrl, this.interval); - } + this.fetchUrl(); + setInterval(this.fetchUrl, this.interval); +} - URLIndicator.prototype.setIndicatorToState = function (state) { - switch (state) { - case CONNECTED: { - this.indicator.text(this.label + ' is connected'); - this.indicator.description( - this.label + ' is online, checking status every ' + this.interval + ' milliseconds.' - ); - break; - } +URLIndicator.prototype.setIndicatorToState = function (state) { + switch (state) { + case CONNECTED: { + this.indicator.text(this.label + ' is connected'); + this.indicator.description( + this.label + ' is online, checking status every ' + this.interval + ' milliseconds.' + ); + break; + } - case PENDING: { - this.indicator.text('Checking status of ' + this.label + ' please stand by...'); - this.indicator.description('Checking status of ' + this.label + ' please stand by...'); - break; - } + case 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; - } + 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); - }; + this.indicator.statusClass(state.statusClass); +}; - URLIndicator.prototype.fetchUrl = function () { - fetch(this.URLpath) - .then((response) => { - if (response.ok) { - this.handleSuccess(); - } else { - this.handleError(); - } - }) - .catch((error) => { +URLIndicator.prototype.fetchUrl = function () { + fetch(this.URLpath) + .then((response) => { + if (response.ok) { + this.handleSuccess(); + } else { this.handleError(); - }); - }; - - URLIndicator.prototype.handleError = function (e) { - this.setIndicatorToState(DISCONNECTED); - }; + } + }) + .catch((error) => { + this.handleError(); + }); +}; - URLIndicator.prototype.handleSuccess = function () { - this.setIndicatorToState(CONNECTED); - }; +URLIndicator.prototype.handleError = function (e) { + this.setIndicatorToState(DISCONNECTED); +}; - URLIndicator.prototype.setDefaultsFromOptions = function (options) { - this.URLpath = options.url; - this.label = options.label || options.url; - this.interval = options.interval || 10000; - this.indicator.iconClass(options.iconClass || 'icon-chain-links'); - }; +URLIndicator.prototype.handleSuccess = function () { + this.setIndicatorToState(CONNECTED); +}; - URLIndicator.prototype.bindMethods = function () { - this.fetchUrl = this.fetchUrl.bind(this); - this.handleSuccess = this.handleSuccess.bind(this); - this.handleError = this.handleError.bind(this); - this.setIndicatorToState = this.setIndicatorToState.bind(this); - }; +URLIndicator.prototype.setDefaultsFromOptions = function (options) { + this.URLpath = options.url; + this.label = options.label || options.url; + this.interval = options.interval || 10000; + this.indicator.iconClass(options.iconClass || 'icon-chain-links'); +}; - return URLIndicator; -}); +URLIndicator.prototype.bindMethods = function () { + this.fetchUrl = this.fetchUrl.bind(this); + this.handleSuccess = this.handleSuccess.bind(this); + this.handleError = this.handleError.bind(this); + this.setIndicatorToState = this.setIndicatorToState.bind(this); +}; diff --git a/src/plugins/URLIndicatorPlugin/URLIndicatorPlugin.js b/src/plugins/URLIndicatorPlugin/URLIndicatorPlugin.js index de7ed0d2629..a8ed6ffd13d 100644 --- a/src/plugins/URLIndicatorPlugin/URLIndicatorPlugin.js +++ b/src/plugins/URLIndicatorPlugin/URLIndicatorPlugin.js @@ -19,15 +19,15 @@ * this source code distribution or the Licensing information page available * at runtime from the About dialog for additional information. *****************************************************************************/ -define(['./URLIndicator'], function URLIndicatorPlugin(URLIndicator) { - return function (opts) { - return function install(openmct) { - const simpleIndicator = openmct.indicators.simpleIndicator(); - const urlIndicator = new URLIndicator(opts, simpleIndicator); +import URLIndicator from './URLIndicator'; - openmct.indicators.add(simpleIndicator); +export default function URLIndicatorPlugin(opts) { + return function install(openmct) { + const simpleIndicator = openmct.indicators.simpleIndicator(); + const urlIndicator = new URLIndicator(opts, simpleIndicator); - return urlIndicator; - }; + openmct.indicators.add(simpleIndicator); + + return urlIndicator; }; -}); +} diff --git a/src/plugins/URLIndicatorPlugin/URLIndicatorSpec.js b/src/plugins/URLIndicatorPlugin/URLIndicatorSpec.js index 91550334946..45e052de978 100644 --- a/src/plugins/URLIndicatorPlugin/URLIndicatorSpec.js +++ b/src/plugins/URLIndicatorPlugin/URLIndicatorSpec.js @@ -20,118 +20,115 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define(['utils/testing', './URLIndicator', './URLIndicatorPlugin', '../../MCT'], function ( - testingUtils, - URLIndicator, - URLIndicatorPlugin, - MCT -) { - describe('The URLIndicator', function () { - let openmct; - let indicatorElement; - let pluginOptions; - let urlIndicator; // eslint-disable-line - let fetchSpy; +import testingUtils from 'utils/testing'; - beforeEach(function () { - jasmine.clock().install(); - openmct = new testingUtils.createOpenMct(); - spyOn(openmct.indicators, 'add'); - fetchSpy = spyOn(window, 'fetch').and.callFake(() => - Promise.resolve({ - ok: true - }) - ); - }); +import URLIndicatorPlugin from './URLIndicatorPlugin'; - afterEach(function () { - if (window.fetch.restore) { - window.fetch.restore(); - } +describe('The URLIndicator', function () { + let openmct; + let indicatorElement; + let pluginOptions; + let urlIndicator; // eslint-disable-line + let fetchSpy; - jasmine.clock().uninstall(); + beforeEach(function () { + jasmine.clock().install(); + openmct = new testingUtils.createOpenMct(); + spyOn(openmct.indicators, 'add'); + fetchSpy = spyOn(window, 'fetch').and.callFake(() => + Promise.resolve({ + ok: true + }) + ); + }); - return testingUtils.resetApplicationState(openmct); - }); + afterEach(function () { + if (window.fetch.restore) { + window.fetch.restore(); + } + + jasmine.clock().uninstall(); + + return testingUtils.resetApplicationState(openmct); + }); + + describe('on initialization', function () { + describe('with default options', function () { + beforeEach(function () { + pluginOptions = { + url: 'someURL' + }; + urlIndicator = URLIndicatorPlugin(pluginOptions)(openmct); + indicatorElement = openmct.indicators.add.calls.mostRecent().args[0].element; + }); - describe('on initialization', function () { - describe('with default options', function () { - beforeEach(function () { - pluginOptions = { - url: 'someURL' - }; - urlIndicator = URLIndicatorPlugin(pluginOptions)(openmct); - indicatorElement = openmct.indicators.add.calls.mostRecent().args[0].element; - }); - - it('has a default icon class if none supplied', function () { - expect(indicatorElement.classList.contains('icon-chain-links')).toBe(true); - }); - - it('defaults to the URL if no label supplied', function () { - expect(indicatorElement.textContent.indexOf(pluginOptions.url) >= 0).toBe(true); - }); + it('has a default icon class if none supplied', function () { + expect(indicatorElement.classList.contains('icon-chain-links')).toBe(true); }); - describe('with custom options', function () { - beforeEach(function () { - pluginOptions = { - url: 'customURL', - interval: 1814, - iconClass: 'iconClass-checked', - label: 'custom label' - }; - urlIndicator = URLIndicatorPlugin(pluginOptions)(openmct); - indicatorElement = openmct.indicators.add.calls.mostRecent().args[0].element; - }); - - it('uses the custom iconClass', function () { - expect(indicatorElement.classList.contains('iconClass-checked')).toBe(true); - }); - it('uses custom interval', function () { - expect(window.fetch).toHaveBeenCalledTimes(1); - jasmine.clock().tick(1); - expect(window.fetch).toHaveBeenCalledTimes(1); - jasmine.clock().tick(pluginOptions.interval + 1); - expect(window.fetch).toHaveBeenCalledTimes(2); - }); - it('uses custom label if supplied in initialization', function () { - expect(indicatorElement.textContent.indexOf(pluginOptions.label) >= 0).toBe(true); - }); + it('defaults to the URL if no label supplied', function () { + expect(indicatorElement.textContent.indexOf(pluginOptions.url) >= 0).toBe(true); }); }); - describe('when running', function () { + describe('with custom options', function () { beforeEach(function () { pluginOptions = { - url: 'someURL', - interval: 100 + url: 'customURL', + interval: 1814, + iconClass: 'iconClass-checked', + label: 'custom label' }; 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('uses the custom iconClass', function () { + expect(indicatorElement.classList.contains('iconClass-checked')).toBe(true); }); - - it('indicates success if connection is nominal', async function () { + it('uses custom interval', function () { + expect(window.fetch).toHaveBeenCalledTimes(1); + jasmine.clock().tick(1); + expect(window.fetch).toHaveBeenCalledTimes(1); jasmine.clock().tick(pluginOptions.interval + 1); - await urlIndicator.fetchUrl(); - expect(indicatorElement.classList.contains('s-status-on')).toBe(true); + expect(window.fetch).toHaveBeenCalledTimes(2); }); - - 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); + 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); + }); + }); }); diff --git a/src/plugins/autoflow/AutoflowTabularConstants.js b/src/plugins/autoflow/AutoflowTabularConstants.js index c16c55322ea..3a1b1e14bee 100644 --- a/src/plugins/autoflow/AutoflowTabularConstants.js +++ b/src/plugins/autoflow/AutoflowTabularConstants.js @@ -20,15 +20,13 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define([], function () { - /** - * Constant values used by the Autoflow Tabular View. - */ - return { - ROW_HEIGHT: 16, - SLIDER_HEIGHT: 10, - INITIAL_COLUMN_WIDTH: 225, - MAX_COLUMN_WIDTH: 525, - COLUMN_WIDTH_STEP: 25 - }; -}); +/** + * Constant values used by the Autoflow Tabular View. + */ +export default { + ROW_HEIGHT: 16, + SLIDER_HEIGHT: 10, + INITIAL_COLUMN_WIDTH: 225, + MAX_COLUMN_WIDTH: 525, + COLUMN_WIDTH_STEP: 25 +}; diff --git a/src/plugins/autoflow/AutoflowTabularController.js b/src/plugins/autoflow/AutoflowTabularController.js index e24a057bb29..b761f4c838a 100644 --- a/src/plugins/autoflow/AutoflowTabularController.js +++ b/src/plugins/autoflow/AutoflowTabularController.js @@ -20,104 +20,102 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define(['./AutoflowTabularRowController'], function (AutoflowTabularRowController) { - /** - * Controller for an Autoflow Tabular View. Subscribes to telemetry - * associated with children of the domain object and passes that - * information on to the view. - * - * @param {DomainObject} domainObject the object being viewed - * @param {*} data the view data - * @param openmct a reference to the openmct application - */ - function AutoflowTabularController(domainObject, data, openmct) { - this.composition = openmct.composition.get(domainObject); - this.data = data; - this.openmct = openmct; +import AutoflowTabularRowController from './AutoflowTabularRowController'; - this.rows = {}; - this.controllers = {}; - - this.addRow = this.addRow.bind(this); - this.removeRow = this.removeRow.bind(this); - } +/** + * 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; - /** - * 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; - }; + this.rows = {}; + this.controllers = {}; - /** - * 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(':'); + this.addRow = this.addRow.bind(this); + this.removeRow = this.removeRow.bind(this); +} - if (!this.rows[id]) { - this.rows[id] = { - classes: '', - name: childObject.name, - value: undefined - }; - this.controllers[id] = new AutoflowTabularRowController( - childObject, - this.rows[id], - this.openmct, - this.trackLastUpdated.bind(this) - ); - this.controllers[id].activate(); - this.data.items.push(this.rows[id]); - } - }; +/** + * Set the "Last Updated" value to be displayed. + * @param {String} value the value to display + * @private + */ +AutoflowTabularController.prototype.trackLastUpdated = function (value) { + this.data.updated = value; +}; - /** - * Respond to an `remove` event from composition by removing any - * related row. - * @private - */ - AutoflowTabularController.prototype.removeRow = function (identifier) { - const id = [identifier.namespace, identifier.key].join(':'); +/** + * Respond to an `add` event from composition by adding a new row. + * @private + */ +AutoflowTabularController.prototype.addRow = function (childObject) { + const identifier = childObject.identifier; + const id = [identifier.namespace, identifier.key].join(':'); - if (this.rows[id]) { - this.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]; - } - }; + 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]); + } +}; - /** - * 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(); - }; +/** + * 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(':'); - /** - * Destroy this controller; detach any associated resources. - */ - AutoflowTabularController.prototype.destroy = function () { - Object.keys(this.controllers).forEach( - function (id) { - this.controllers[id].destroy(); + if (this.rows[id]) { + this.data.items = this.data.items.filter( + function (item) { + return item !== this.rows[id]; }.bind(this) ); - this.controllers = {}; - this.composition.off('add', this.addRow); - this.composition.off('remove', this.removeRow); - }; + this.controllers[id].destroy(); + delete this.controllers[id]; + delete this.rows[id]; + } +}; - return AutoflowTabularController; -}); +/** + * Activate this controller; begin listening for changes. + */ +AutoflowTabularController.prototype.activate = function () { + this.composition.on('add', this.addRow); + this.composition.on('remove', this.removeRow); + this.composition.load(); +}; + +/** + * Destroy this controller; detach any associated resources. + */ +AutoflowTabularController.prototype.destroy = function () { + Object.keys(this.controllers).forEach( + function (id) { + this.controllers[id].destroy(); + }.bind(this) + ); + this.controllers = {}; + this.composition.off('add', this.addRow); + this.composition.off('remove', this.removeRow); +}; diff --git a/src/plugins/autoflow/AutoflowTabularPlugin.js b/src/plugins/autoflow/AutoflowTabularPlugin.js index c289e5c1106..d65501aaaa2 100644 --- a/src/plugins/autoflow/AutoflowTabularPlugin.js +++ b/src/plugins/autoflow/AutoflowTabularPlugin.js @@ -20,23 +20,23 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define(['./AutoflowTabularView'], function (AutoflowTabularView) { - return function (options) { - return function (openmct) { - const views = openmct.mainViews || openmct.objectViews; +import AutoflowTabularView from './AutoflowTabularView'; - views.addProvider({ - name: 'Autoflow Tabular', - key: 'autoflow', - cssClass: 'icon-packet', - description: 'A tabular view of packet contents.', - canView: function (d) { - return !options || options.type === d.type; - }, - view: function (domainObject) { - return new AutoflowTabularView(domainObject, openmct, document); - } - }); - }; +export default function (options) { + return function (openmct) { + const views = openmct.mainViews || openmct.objectViews; + + views.addProvider({ + name: 'Autoflow Tabular', + key: 'autoflow', + cssClass: 'icon-packet', + description: 'A tabular view of packet contents.', + canView: function (d) { + return !options || options.type === d.type; + }, + view: function (domainObject) { + return new AutoflowTabularView(domainObject, openmct, document); + } + }); }; -}); +} diff --git a/src/plugins/autoflow/AutoflowTabularRowController.js b/src/plugins/autoflow/AutoflowTabularRowController.js index 4b8fda76bac..10a921da74a 100644 --- a/src/plugins/autoflow/AutoflowTabularRowController.js +++ b/src/plugins/autoflow/AutoflowTabularRowController.js @@ -20,76 +20,72 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define([], function () { - /** - * Controller for individual rows of an Autoflow Tabular View. - * Subscribes to telemetry and updates row data. - * - * @param {DomainObject} domainObject the object being viewed - * @param {*} data the view data - * @param openmct a reference to the openmct application - * @param {Function} callback a callback to invoke with "last updated" timestamps - */ - function AutoflowTabularRowController(domainObject, data, openmct, callback) { - this.domainObject = domainObject; - this.data = data; - this.openmct = openmct; - this.callback = callback; - - this.metadata = this.openmct.telemetry.getMetadata(this.domainObject); - this.ranges = this.metadata.valuesForHints(['range']); - this.domains = this.metadata.valuesForHints(['domain']); - this.rangeFormatter = this.openmct.telemetry.getValueFormatter(this.ranges[0]); - this.domainFormatter = this.openmct.telemetry.getValueFormatter(this.domains[0]); - this.evaluator = this.openmct.telemetry.limitEvaluator(this.domainObject); +/** + * Controller for individual rows of an Autoflow Tabular View. + * Subscribes to telemetry and updates row data. + * + * @param {DomainObject} domainObject the object being viewed + * @param {*} data the view data + * @param openmct a reference to the openmct application + * @param {Function} callback a callback to invoke with "last updated" timestamps + */ +export default function AutoflowTabularRowController(domainObject, data, openmct, callback) { + this.domainObject = domainObject; + this.data = data; + this.openmct = openmct; + this.callback = callback; - this.initialized = false; - } + this.metadata = this.openmct.telemetry.getMetadata(this.domainObject); + this.ranges = this.metadata.valuesForHints(['range']); + this.domains = this.metadata.valuesForHints(['domain']); + this.rangeFormatter = this.openmct.telemetry.getValueFormatter(this.ranges[0]); + this.domainFormatter = this.openmct.telemetry.getValueFormatter(this.domains[0]); + this.evaluator = this.openmct.telemetry.limitEvaluator(this.domainObject); - /** - * Update row to reflect incoming telemetry data. - * @private - */ - AutoflowTabularRowController.prototype.updateRowData = function (datum) { - const violations = this.evaluator.evaluate(datum, this.ranges[0]); + this.initialized = false; +} - this.initialized = true; - this.data.classes = violations ? violations.cssClass : ''; - this.data.value = this.rangeFormatter.format(datum); - this.callback(this.domainFormatter.format(datum)); - }; +/** + * Update row to reflect incoming telemetry data. + * @private + */ +AutoflowTabularRowController.prototype.updateRowData = function (datum) { + const violations = this.evaluator.evaluate(datum, this.ranges[0]); - /** - * Activate this controller; begin listening for changes. - */ - AutoflowTabularRowController.prototype.activate = function () { - this.unsubscribe = this.openmct.telemetry.subscribe( - this.domainObject, - this.updateRowData.bind(this) - ); + this.initialized = true; + this.data.classes = violations ? violations.cssClass : ''; + this.data.value = this.rangeFormatter.format(datum); + this.callback(this.domainFormatter.format(datum)); +}; - 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) - ); - }; +/** + * Activate this controller; begin listening for changes. + */ +AutoflowTabularRowController.prototype.activate = function () { + this.unsubscribe = this.openmct.telemetry.subscribe( + this.domainObject, + this.updateRowData.bind(this) + ); - /** - * Destroy this controller; detach any associated resources. - */ - AutoflowTabularRowController.prototype.destroy = function () { - if (this.unsubscribe) { - this.unsubscribe(); - } + 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) + ); +}; - return AutoflowTabularRowController; -}); +/** + * Destroy this controller; detach any associated resources. + */ +AutoflowTabularRowController.prototype.destroy = function () { + if (this.unsubscribe) { + this.unsubscribe(); + } +}; diff --git a/src/plugins/autoflow/AutoflowTabularView.js b/src/plugins/autoflow/AutoflowTabularView.js index ee6bb78f999..8d039158266 100644 --- a/src/plugins/autoflow/AutoflowTabularView.js +++ b/src/plugins/autoflow/AutoflowTabularView.js @@ -20,96 +20,92 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define([ - './AutoflowTabularController', - './AutoflowTabularConstants', - './VueView', - './autoflow-tabular.html' -], function (AutoflowTabularController, AutoflowTabularConstants, VueView, autoflowTemplate) { - const ROW_HEIGHT = AutoflowTabularConstants.ROW_HEIGHT; - const SLIDER_HEIGHT = AutoflowTabularConstants.SLIDER_HEIGHT; - const INITIAL_COLUMN_WIDTH = AutoflowTabularConstants.INITIAL_COLUMN_WIDTH; - const MAX_COLUMN_WIDTH = AutoflowTabularConstants.MAX_COLUMN_WIDTH; - const COLUMN_WIDTH_STEP = AutoflowTabularConstants.COLUMN_WIDTH_STEP; +import * as autoflowTemplate from './autoflow-tabular.html'; +import AutoflowTabularConstants from './AutoflowTabularConstants'; +import AutoflowTabularController from './AutoflowTabularController'; +import VueView from './VueView'; - /** - * Implements the Autoflow Tabular view of a domain object. - */ - function AutoflowTabularView(domainObject, openmct) { - const data = { - items: [], - columns: [], - width: INITIAL_COLUMN_WIDTH, - filter: '', - updated: 'No updates', - rowCount: 1 - }; - const controller = new AutoflowTabularController(domainObject, data, openmct); - let interval; +const ROW_HEIGHT = AutoflowTabularConstants.ROW_HEIGHT; +const SLIDER_HEIGHT = AutoflowTabularConstants.SLIDER_HEIGHT; +const INITIAL_COLUMN_WIDTH = AutoflowTabularConstants.INITIAL_COLUMN_WIDTH; +const MAX_COLUMN_WIDTH = AutoflowTabularConstants.MAX_COLUMN_WIDTH; +const COLUMN_WIDTH_STEP = AutoflowTabularConstants.COLUMN_WIDTH_STEP; - VueView.call(this, { - data: data, - methods: { - increaseColumnWidth: function () { - data.width += COLUMN_WIDTH_STEP; - data.width = data.width > MAX_COLUMN_WIDTH ? INITIAL_COLUMN_WIDTH : data.width; - }, - reflow: function () { - let column = []; - let index = 0; - const filteredItems = data.items.filter(function (item) { - return item.name.toLowerCase().indexOf(data.filter.toLowerCase()) !== -1; - }); +/** + * Implements the Autoflow Tabular view of a domain object. + */ +export default function AutoflowTabularView(domainObject, openmct) { + const data = { + items: [], + columns: [], + width: INITIAL_COLUMN_WIDTH, + filter: '', + updated: 'No updates', + rowCount: 1 + }; + const controller = new AutoflowTabularController(domainObject, data, openmct); + let interval; - data.columns = []; - - while (index < filteredItems.length) { - if (column.length >= data.rowCount) { - data.columns.push(column); - column = []; - } + 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; + }); - column.push(filteredItems[index]); - index += 1; - } + data.columns = []; - if (column.length > 0) { + while (index < filteredItems.length) { + if (column.length >= data.rowCount) { data.columns.push(column); + column = []; } - } - }, - watch: { - filter: 'reflow', - items: 'reflow', - rowCount: 'reflow' - }, - template: autoflowTemplate, - unmounted: function () { - controller.destroy(); - if (interval) { - clearInterval(interval); - interval = undefined; + column.push(filteredItems[index]); + index += 1; } - }, - 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); + if (column.length > 0) { + data.columns.push(column); + } + } + }, + watch: { + filter: 'reflow', + items: 'reflow', + rowCount: 'reflow' + }, + template: autoflowTemplate, + unmounted: function () { + controller.destroy(); - interval = setInterval(updateRowHeight, 50); - this.$nextTick(updateRowHeight); + if (interval) { + clearInterval(interval); + 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); - AutoflowTabularView.prototype = Object.create(VueView.default.prototype); + interval = setInterval(updateRowHeight, 50); + this.$nextTick(updateRowHeight); + } + }); +} - return AutoflowTabularView; -}); +AutoflowTabularView.prototype = Object.create(VueView.prototype); diff --git a/src/plugins/autoflow/dom-observer.js b/src/plugins/autoflow/dom-observer.js index 98b600f285d..01caa636833 100644 --- a/src/plugins/autoflow/dom-observer.js +++ b/src/plugins/autoflow/dom-observer.js @@ -20,44 +20,40 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define([], function () { - function DOMObserver(element) { - this.element = element; - this.observers = []; - } +export default function DOMObserver(element) { + this.element = element; + this.observers = []; +} - DOMObserver.prototype.when = function (latchFunction) { - return new Promise( - function (resolve, reject) { - //Test latch function at least once - if (latchFunction()) { - resolve(); - } else { - //Latch condition not true yet, create observer on DOM and test again on change. - const config = { - attributes: true, - childList: true, - subtree: true - }; - const observer = new MutationObserver(function () { - if (latchFunction()) { - resolve(); - } - }); - observer.observe(this.element, config); - this.observers.push(observer); - } - }.bind(this) - ); - }; +DOMObserver.prototype.when = function (latchFunction) { + return new Promise( + function (resolve, reject) { + //Test latch function at least once + if (latchFunction()) { + resolve(); + } else { + //Latch condition not true yet, create observer on DOM and test again on change. + const config = { + attributes: true, + childList: true, + subtree: true + }; + const observer = new MutationObserver(function () { + if (latchFunction()) { + resolve(); + } + }); + observer.observe(this.element, config); + this.observers.push(observer); + } + }.bind(this) + ); +}; - DOMObserver.prototype.destroy = function () { - this.observers.forEach( - function (observer) { - observer.disconnect(); - }.bind(this) - ); - }; - - return DOMObserver; -}); +DOMObserver.prototype.destroy = function () { + this.observers.forEach( + function (observer) { + observer.disconnect(); + }.bind(this) + ); +}; diff --git a/src/plugins/displayLayout/DisplayLayoutToolbar.js b/src/plugins/displayLayout/DisplayLayoutToolbar.js index 5e7d4f315fb..7699c46ce40 100644 --- a/src/plugins/displayLayout/DisplayLayoutToolbar.js +++ b/src/plugins/displayLayout/DisplayLayoutToolbar.js @@ -20,836 +20,827 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define(['lodash'], function (_) { - function DisplayLayoutToolbar(openmct) { - return { - name: 'Display Layout Toolbar', - key: 'layout', - description: 'A toolbar for objects inside a display layout.', - forSelection: function (selection) { - if (!selection || selection.length === 0) { - return false; - } - - let selectionPath = selection[0]; - let selectedObject = selectionPath[0]; - let selectedParent = selectionPath[1]; - - // Apply the layout toolbar if the selected object is inside a layout, or the main layout is selected. - return ( - (selectedParent && - selectedParent.context.item && - selectedParent.context.item.type === 'layout') || - (selectedObject.context.item && selectedObject.context.item.type === 'layout') - ); - }, - toolbar: function (selectedObjects) { - const DIALOG_FORM = { - text: { - title: 'Text Element Properties', - sections: [ - { - rows: [ - { - key: 'text', - control: 'textfield', - name: 'Text', - required: true, - cssClass: 'l-input-lg' - } - ] - } - ] - }, - image: { - title: 'Image Properties', - sections: [ - { - rows: [ - { - key: 'url', - control: 'textfield', - name: 'Image URL', - cssClass: 'l-input-lg', - required: true - } - ] - } - ] - } - }; - const VIEW_TYPES = { - 'telemetry-view': { - value: 'telemetry-view', - name: 'Alphanumeric', - class: 'icon-alphanumeric' - }, - 'telemetry.plot.overlay': { - value: 'telemetry.plot.overlay', - name: 'Overlay Plot', - class: 'icon-plot-overlay' - }, - 'telemetry.plot.stacked': { - value: 'telemetry.plot.stacked', - name: 'Stacked Plot', - class: 'icon-plot-stacked' - }, - table: { - value: 'table', - name: 'Table', - class: 'icon-tabular-scrolling' - } - }; - const APPLICABLE_VIEWS = { - 'telemetry-view': [ - VIEW_TYPES['telemetry.plot.overlay'], - VIEW_TYPES['telemetry.plot.stacked'], - VIEW_TYPES.table - ], - 'telemetry.plot.overlay': [ - VIEW_TYPES['telemetry.plot.stacked'], - VIEW_TYPES.table, - VIEW_TYPES['telemetry-view'] - ], - 'telemetry.plot.stacked': [ - VIEW_TYPES['telemetry.plot.overlay'], - VIEW_TYPES.table, - VIEW_TYPES['telemetry-view'] - ], - table: [ - VIEW_TYPES['telemetry.plot.overlay'], - VIEW_TYPES['telemetry.plot.stacked'], - VIEW_TYPES['telemetry-view'] - ], - 'telemetry-view-multi': [ - VIEW_TYPES['telemetry.plot.overlay'], - VIEW_TYPES['telemetry.plot.stacked'], - VIEW_TYPES.table - ], - 'telemetry.plot.overlay-multi': [VIEW_TYPES['telemetry.plot.stacked']] - }; - - function getPath(selectionPath) { - return `configuration.items[${selectionPath[0].context.index}]`; - } - - function getAllOfType(selection, specificType) { - return selection.filter((selectionPath) => { - let type = selectionPath[0].context.layoutItem.type; - - return type === specificType; - }); - } - - function getAllTypes(selection) { - return selection.filter((selectionPath) => { - let type = selectionPath[0].context.layoutItem.type; - - return ( - type === 'text-view' || - type === 'telemetry-view' || - type === 'box-view' || - type === 'ellipse-view' || - type === 'image-view' || - type === 'line-view' || - type === 'subobject-view' - ); - }); - } - - function getAddButton(selection, selectionPath) { - if (selection.length === 1) { - selectionPath = selectionPath || selection[0]; +import _ from 'lodash'; + +export default function DisplayLayoutToolbar(openmct) { + return { + name: 'Display Layout Toolbar', + key: 'layout', + description: 'A toolbar for objects inside a display layout.', + forSelection: function (selection) { + if (!selection || selection.length === 0) { + return false; + } - return { - control: 'menu', - domainObject: selectionPath[0].context.item, - method: function (option) { - let name = option.name.toLowerCase(); - let form = DIALOG_FORM[name]; - if (form) { - showForm(form, name, selectionPath); - } else { - selectionPath[0].context.addElement(name); - } - }, - key: 'add', - icon: 'icon-plus', - label: 'Add', - options: [ - { - name: 'Box', - class: 'icon-box-round-corners' - }, - { - name: 'Ellipse', - class: 'icon-circle' - }, - { - name: 'Line', - class: 'icon-line-horz' - }, + let selectionPath = selection[0]; + let selectedObject = selectionPath[0]; + let selectedParent = selectionPath[1]; + + // Apply the layout toolbar if the selected object is inside a layout, or the main layout is selected. + return ( + (selectedParent && + selectedParent.context.item && + selectedParent.context.item.type === 'layout') || + (selectedObject.context.item && selectedObject.context.item.type === 'layout') + ); + }, + toolbar: function (selectedObjects) { + const DIALOG_FORM = { + text: { + title: 'Text Element Properties', + sections: [ + { + rows: [ { + key: 'text', + control: 'textfield', name: 'Text', - class: 'icon-font' - }, + required: true, + cssClass: 'l-input-lg' + } + ] + } + ] + }, + image: { + title: 'Image Properties', + sections: [ + { + rows: [ { - name: 'Image', - class: 'icon-image' + key: 'url', + control: 'textfield', + name: 'Image URL', + cssClass: 'l-input-lg', + required: true } ] - }; - } + } + ] } - - function getToggleFrameButton(selectedParent, selection) { - return { - control: 'toggle-button', - domainObject: selectedParent, - applicableSelectedItems: selection.filter( - (selectionPath) => selectionPath[0].context.layoutItem.type === 'subobject-view' - ), - property: function (selectionPath) { - return getPath(selectionPath) + '.hasFrame'; - }, - options: [ - { - value: false, - icon: 'icon-frame-hide', - title: 'Frame visible', - label: 'Hide frame' - }, - { - value: true, - icon: 'icon-frame-show', - title: 'Frame hidden', - label: 'Show frame' - } - ] - }; + }; + const VIEW_TYPES = { + 'telemetry-view': { + value: 'telemetry-view', + name: 'Alphanumeric', + class: 'icon-alphanumeric' + }, + 'telemetry.plot.overlay': { + value: 'telemetry.plot.overlay', + name: 'Overlay Plot', + class: 'icon-plot-overlay' + }, + 'telemetry.plot.stacked': { + value: 'telemetry.plot.stacked', + name: 'Stacked Plot', + class: 'icon-plot-stacked' + }, + table: { + value: 'table', + name: 'Table', + class: 'icon-tabular-scrolling' } + }; + const APPLICABLE_VIEWS = { + 'telemetry-view': [ + VIEW_TYPES['telemetry.plot.overlay'], + VIEW_TYPES['telemetry.plot.stacked'], + VIEW_TYPES.table + ], + 'telemetry.plot.overlay': [ + VIEW_TYPES['telemetry.plot.stacked'], + VIEW_TYPES.table, + VIEW_TYPES['telemetry-view'] + ], + 'telemetry.plot.stacked': [ + VIEW_TYPES['telemetry.plot.overlay'], + VIEW_TYPES.table, + VIEW_TYPES['telemetry-view'] + ], + table: [ + VIEW_TYPES['telemetry.plot.overlay'], + VIEW_TYPES['telemetry.plot.stacked'], + VIEW_TYPES['telemetry-view'] + ], + 'telemetry-view-multi': [ + VIEW_TYPES['telemetry.plot.overlay'], + VIEW_TYPES['telemetry.plot.stacked'], + VIEW_TYPES.table + ], + 'telemetry.plot.overlay-multi': [VIEW_TYPES['telemetry.plot.stacked']] + }; + + function getPath(selectionPath) { + return `configuration.items[${selectionPath[0].context.index}]`; + } - function getRemoveButton(selectedParent, selectionPath, selection) { - return { - control: 'button', - domainObject: selectedParent, - icon: 'icon-trash', - title: 'Delete the selected object', - method: function () { - let removeItem = selectionPath[1].context.removeItem; - let prompt = openmct.overlays.dialog({ - iconClass: 'alert', - message: `Warning! This action will remove this item from the Display Layout. Do you want to continue?`, - buttons: [ - { - label: 'OK', - emphasis: 'true', - callback: function () { - removeItem(getAllTypes(selection)); - prompt.dismiss(); - } - }, - { - label: 'Cancel', - callback: function () { - prompt.dismiss(); - } - } - ] - }); - } - }; - } + function getAllOfType(selection, specificType) { + return selection.filter((selectionPath) => { + let type = selectionPath[0].context.layoutItem.type; + + return type === specificType; + }); + } + + function getAllTypes(selection) { + return selection.filter((selectionPath) => { + let type = selectionPath[0].context.layoutItem.type; + + return ( + type === 'text-view' || + type === 'telemetry-view' || + type === 'box-view' || + type === 'ellipse-view' || + type === 'image-view' || + type === 'line-view' || + type === 'subobject-view' + ); + }); + } + + function getAddButton(selection, selectionPath) { + if (selection.length === 1) { + selectionPath = selectionPath || selection[0]; - function getStackOrder(selectedParent, selectionPath) { return { control: 'menu', - domainObject: selectedParent, - icon: 'icon-layers', - title: 'Move the selected object above or below other objects', + domainObject: selectionPath[0].context.item, + method: function (option) { + let name = option.name.toLowerCase(); + let form = DIALOG_FORM[name]; + if (form) { + showForm(form, name, selectionPath); + } else { + selectionPath[0].context.addElement(name); + } + }, + key: 'add', + icon: 'icon-plus', + label: 'Add', options: [ { - name: 'Move to Top', - value: 'top', - class: 'icon-arrow-double-up' + name: 'Box', + class: 'icon-box-round-corners' + }, + { + name: 'Ellipse', + class: 'icon-circle' }, { - name: 'Move Up', - value: 'up', - class: 'icon-arrow-up' + name: 'Line', + class: 'icon-line-horz' }, { - name: 'Move Down', - value: 'down', - class: 'icon-arrow-down' + name: 'Text', + class: 'icon-font' }, { - name: 'Move to Bottom', - value: 'bottom', - class: 'icon-arrow-double-down' + name: 'Image', + class: 'icon-image' } - ], - method: function (option) { - selectionPath[1].context.orderItem(option.value, getAllTypes(selectedObjects)); - } + ] }; } + } - function getXInput(selectedParent, selection) { - if (selection.length === 1) { - return { - control: 'input', - type: 'number', - domainObject: selectedParent, - applicableSelectedItems: getAllTypes(selection), - property: function (selectionPath) { - return getPath(selectionPath) + '.x'; - }, - label: 'X:', - title: 'X position' - }; - } - } + function getToggleFrameButton(selectedParent, selection) { + return { + control: 'toggle-button', + domainObject: selectedParent, + applicableSelectedItems: selection.filter( + (selectionPath) => selectionPath[0].context.layoutItem.type === 'subobject-view' + ), + property: function (selectionPath) { + return getPath(selectionPath) + '.hasFrame'; + }, + options: [ + { + value: false, + icon: 'icon-frame-hide', + title: 'Frame visible', + label: 'Hide frame' + }, + { + value: true, + icon: 'icon-frame-show', + title: 'Frame hidden', + label: 'Show frame' + } + ] + }; + } - function getYInput(selectedParent, selection) { - if (selection.length === 1) { - return { - control: 'input', - type: 'number', - domainObject: selectedParent, - applicableSelectedItems: getAllTypes(selection), - property: function (selectionPath) { - return getPath(selectionPath) + '.y'; - }, - label: 'Y:', - title: 'Y position' - }; + function getRemoveButton(selectedParent, selectionPath, selection) { + return { + control: 'button', + domainObject: selectedParent, + icon: 'icon-trash', + title: 'Delete the selected object', + method: function () { + let removeItem = selectionPath[1].context.removeItem; + let prompt = openmct.overlays.dialog({ + iconClass: 'alert', + message: `Warning! This action will remove this item from the Display Layout. Do you want to continue?`, + buttons: [ + { + label: 'OK', + emphasis: 'true', + callback: function () { + removeItem(getAllTypes(selection)); + prompt.dismiss(); + } + }, + { + label: 'Cancel', + callback: function () { + prompt.dismiss(); + } + } + ] + }); } - } + }; + } - function getWidthInput(selectedParent, selection) { - if (selection.length === 1) { - return { - control: 'input', - type: 'number', - domainObject: selectedParent, - applicableSelectedItems: getAllTypes(selection), - property: function (selectionPath) { - return getPath(selectionPath) + '.width'; - }, - label: 'W:', - title: 'Resize object width' - }; + function getStackOrder(selectedParent, selectionPath) { + return { + control: 'menu', + domainObject: selectedParent, + icon: 'icon-layers', + title: 'Move the selected object above or below other objects', + options: [ + { + name: 'Move to Top', + value: 'top', + class: 'icon-arrow-double-up' + }, + { + name: 'Move Up', + value: 'up', + class: 'icon-arrow-up' + }, + { + name: 'Move Down', + value: 'down', + class: 'icon-arrow-down' + }, + { + name: 'Move to Bottom', + value: 'bottom', + class: 'icon-arrow-double-down' + } + ], + method: function (option) { + selectionPath[1].context.orderItem(option.value, getAllTypes(selectedObjects)); } + }; + } + + function getXInput(selectedParent, selection) { + if (selection.length === 1) { + return { + control: 'input', + type: 'number', + domainObject: selectedParent, + applicableSelectedItems: getAllTypes(selection), + property: function (selectionPath) { + return getPath(selectionPath) + '.x'; + }, + label: 'X:', + title: 'X position' + }; } + } - function getHeightInput(selectedParent, selection) { - if (selection.length === 1) { - return { - control: 'input', - type: 'number', - domainObject: selectedParent, - applicableSelectedItems: getAllTypes(selection), - property: function (selectionPath) { - return getPath(selectionPath) + '.height'; - }, - label: 'H:', - title: 'Resize object height' - }; - } + function getYInput(selectedParent, selection) { + if (selection.length === 1) { + return { + control: 'input', + type: 'number', + domainObject: selectedParent, + applicableSelectedItems: getAllTypes(selection), + property: function (selectionPath) { + return getPath(selectionPath) + '.y'; + }, + label: 'Y:', + title: 'Y position' + }; } + } - function getX2Input(selectedParent, selection) { - if (selection.length === 1) { - return { - control: 'input', - type: 'number', - domainObject: selectedParent, - applicableSelectedItems: selection.filter((selectionPath) => { - return selectionPath[0].context.layoutItem.type === 'line-view'; - }), - property: function (selectionPath) { - return getPath(selectionPath) + '.x2'; - }, - label: 'X2:', - title: 'X2 position' - }; - } + function getWidthInput(selectedParent, selection) { + if (selection.length === 1) { + return { + control: 'input', + type: 'number', + domainObject: selectedParent, + applicableSelectedItems: getAllTypes(selection), + property: function (selectionPath) { + return getPath(selectionPath) + '.width'; + }, + label: 'W:', + title: 'Resize object width' + }; } + } - function getY2Input(selectedParent, selection) { - if (selection.length === 1) { - return { - control: 'input', - type: 'number', - domainObject: selectedParent, - applicableSelectedItems: selection.filter((selectionPath) => { - return selectionPath[0].context.layoutItem.type === 'line-view'; - }), - property: function (selectionPath) { - return getPath(selectionPath) + '.y2'; - }, - label: 'Y2:', - title: 'Y2 position' - }; - } + function getHeightInput(selectedParent, selection) { + if (selection.length === 1) { + return { + control: 'input', + type: 'number', + domainObject: selectedParent, + applicableSelectedItems: getAllTypes(selection), + property: function (selectionPath) { + return getPath(selectionPath) + '.height'; + }, + label: 'H:', + title: 'Resize object height' + }; } + } - function getTextButton(selectedParent, selection) { + function getX2Input(selectedParent, selection) { + if (selection.length === 1) { return { - control: 'button', + control: 'input', + type: 'number', domainObject: selectedParent, applicableSelectedItems: selection.filter((selectionPath) => { - return selectionPath[0].context.layoutItem.type === 'text-view'; + return selectionPath[0].context.layoutItem.type === 'line-view'; }), property: function (selectionPath) { - return getPath(selectionPath); + return getPath(selectionPath) + '.x2'; }, - icon: 'icon-pencil', - title: 'Edit text properties', - label: 'Edit text', - dialog: DIALOG_FORM.text + label: 'X2:', + title: 'X2 position' }; } + } - function getTelemetryValueMenu(selectionPath, selection) { - if (selection.length === 1) { - return { - control: 'select-menu', - domainObject: selectionPath[1].context.item, - applicableSelectedItems: selection.filter((path) => { - return path[0].context.layoutItem.type === 'telemetry-view'; - }), - property: function (path) { - return getPath(path) + '.value'; - }, - title: 'Set value', - options: openmct.telemetry - .getMetadata(selectionPath[0].context.item) - .values() - .map((value) => { - return { - name: value.name, - value: value.key - }; - }) - }; - } - } - - function getDisplayModeMenu(selectedParent, selection) { - if (selection.length === 1) { - return { - control: 'select-menu', - domainObject: selectedParent, - applicableSelectedItems: selection.filter((selectionPath) => { - return selectionPath[0].context.layoutItem.type === 'telemetry-view'; - }), - property: function (selectionPath) { - return getPath(selectionPath) + '.displayMode'; - }, - title: 'Set display mode', - options: [ - { - name: 'Label + Value', - value: 'all' - }, - { - name: 'Label only', - value: 'label' - }, - { - name: 'Value only', - value: 'value' - } - ] - }; - } - } - - function getDuplicateButton(selectedParent, selectionPath, selection) { + function getY2Input(selectedParent, selection) { + if (selection.length === 1) { return { - control: 'button', + control: 'input', + type: 'number', domainObject: selectedParent, - icon: 'icon-duplicate', - title: 'Duplicate the selected object', - method: function () { - let duplicateItem = selectionPath[1].context.duplicateItem; - - duplicateItem(selection); - } + applicableSelectedItems: selection.filter((selectionPath) => { + return selectionPath[0].context.layoutItem.type === 'line-view'; + }), + property: function (selectionPath) { + return getPath(selectionPath) + '.y2'; + }, + label: 'Y2:', + title: 'Y2 position' }; } + } - function getPropertyFromPath(object, path) { - let splitPath = path.split('.'); - let property = Object.assign({}, object); - - while (splitPath.length && property) { - property = property[splitPath.shift()]; - } - - return property; - } - - function areAllViews(type, path, selection) { - let allTelemetry = true; - - selection.forEach((selectedItem) => { - let selectedItemContext = selectedItem[0].context; - - if (getPropertyFromPath(selectedItemContext, path) !== type) { - allTelemetry = false; - } - }); + function getTextButton(selectedParent, selection) { + return { + control: 'button', + domainObject: selectedParent, + applicableSelectedItems: selection.filter((selectionPath) => { + return selectionPath[0].context.layoutItem.type === 'text-view'; + }), + property: function (selectionPath) { + return getPath(selectionPath); + }, + icon: 'icon-pencil', + title: 'Edit text properties', + label: 'Edit text', + dialog: DIALOG_FORM.text + }; + } - return allTelemetry; + function getTelemetryValueMenu(selectionPath, selection) { + if (selection.length === 1) { + return { + control: 'select-menu', + domainObject: selectionPath[1].context.item, + applicableSelectedItems: selection.filter((path) => { + return path[0].context.layoutItem.type === 'telemetry-view'; + }), + property: function (path) { + return getPath(path) + '.value'; + }, + title: 'Set value', + options: openmct.telemetry + .getMetadata(selectionPath[0].context.item) + .values() + .map((value) => { + return { + name: value.name, + value: value.key + }; + }) + }; } + } - function getToggleUnitsButton(selectedParent, selection) { - let applicableItems = getAllOfType(selection, 'telemetry-view'); - applicableItems = unitsOnly(applicableItems); - if (!applicableItems.length) { - return; - } - + function getDisplayModeMenu(selectedParent, selection) { + if (selection.length === 1) { return { - control: 'toggle-button', + control: 'select-menu', domainObject: selectedParent, - applicableSelectedItems: applicableItems, + applicableSelectedItems: selection.filter((selectionPath) => { + return selectionPath[0].context.layoutItem.type === 'telemetry-view'; + }), property: function (selectionPath) { - return getPath(selectionPath) + '.showUnits'; + return getPath(selectionPath) + '.displayMode'; }, + title: 'Set display mode', options: [ { - value: true, - icon: 'icon-eye-open', - title: 'Show units', - label: 'Show units' + name: 'Label + Value', + value: 'all' }, { - value: false, - icon: 'icon-eye-disabled', - title: 'Hide units', - label: 'Hide units' + name: 'Label only', + value: 'label' + }, + { + name: 'Value only', + value: 'value' } ] }; } + } - function unitsOnly(items) { - let results = items.filter((item) => { - let currentItem = item[0]; - let metadata = openmct.telemetry.getMetadata(currentItem.context.item); - if (!metadata) { - return false; - } + function getDuplicateButton(selectedParent, selectionPath, selection) { + return { + control: 'button', + domainObject: selectedParent, + icon: 'icon-duplicate', + title: 'Duplicate the selected object', + method: function () { + let duplicateItem = selectionPath[1].context.duplicateItem; - let hasUnits = metadata.valueMetadatas.filter((metadatum) => metadatum.unit).length; + duplicateItem(selection); + } + }; + } - return hasUnits > 0; - }); + function getPropertyFromPath(object, path) { + let splitPath = path.split('.'); + let property = Object.assign({}, object); - return results; + while (splitPath.length && property) { + property = property[splitPath.shift()]; } - function getViewSwitcherMenu(selectedParent, selectionPath, selection) { - if (selection.length === 1) { - let displayLayoutContext = selectionPath[1].context; - let selectedItemContext = selectionPath[0].context; - let selectedItemType = selectedItemContext.item.type; + return property; + } - if (selectedItemContext.layoutItem.type === 'telemetry-view') { - selectedItemType = 'telemetry-view'; - } + function areAllViews(type, path, selection) { + let allTelemetry = true; - let viewOptions = APPLICABLE_VIEWS[selectedItemType]; - - if (viewOptions) { - return { - control: 'menu', - domainObject: selectedParent, - icon: 'icon-object', - title: 'Switch the way this telemetry is displayed', - label: 'View type', - options: viewOptions, - method: function (option) { - displayLayoutContext.switchViewType(selectedItemContext, option.value, selection); - } - }; - } - } else if (selection.length > 1) { - if (areAllViews('telemetry-view', 'layoutItem.type', selection)) { - let displayLayoutContext = selectionPath[1].context; - - return { - control: 'menu', - domainObject: selectedParent, - icon: 'icon-object', - title: 'Merge into a telemetry table or plot', - label: 'View type', - options: APPLICABLE_VIEWS['telemetry-view-multi'], - method: function (option) { - displayLayoutContext.mergeMultipleTelemetryViews(selection, option.value); - } - }; - } else if (areAllViews('telemetry.plot.overlay', 'item.type', selection)) { - let displayLayoutContext = selectionPath[1].context; - - return { - control: 'menu', - domainObject: selectedParent, - icon: 'icon-object', - title: 'Merge into a stacked plot', - options: APPLICABLE_VIEWS['telemetry.plot.overlay-multi'], - method: function (option) { - displayLayoutContext.mergeMultipleOverlayPlots(selection, option.value); - } - }; - } + selection.forEach((selectedItem) => { + let selectedItemContext = selectedItem[0].context; + + if (getPropertyFromPath(selectedItemContext, path) !== type) { + allTelemetry = false; } + }); + + return allTelemetry; + } + + function getToggleUnitsButton(selectedParent, selection) { + let applicableItems = getAllOfType(selection, 'telemetry-view'); + applicableItems = unitsOnly(applicableItems); + if (!applicableItems.length) { + return; } - function getToggleGridButton(selection, selectionPath) { - const ICON_GRID_SHOW = 'icon-grid-on'; - const ICON_GRID_HIDE = 'icon-grid-off'; + return { + control: 'toggle-button', + domainObject: selectedParent, + applicableSelectedItems: applicableItems, + property: function (selectionPath) { + return getPath(selectionPath) + '.showUnits'; + }, + options: [ + { + value: true, + icon: 'icon-eye-open', + title: 'Show units', + label: 'Show units' + }, + { + value: false, + icon: 'icon-eye-disabled', + title: 'Hide units', + label: 'Hide units' + } + ] + }; + } + + function unitsOnly(items) { + let results = items.filter((item) => { + let currentItem = item[0]; + let metadata = openmct.telemetry.getMetadata(currentItem.context.item); + if (!metadata) { + return false; + } + + let hasUnits = metadata.valueMetadatas.filter((metadatum) => metadatum.unit).length; - let displayLayoutContext; + return hasUnits > 0; + }); + + return results; + } - if (selection.length === 1 && selectionPath === undefined) { - displayLayoutContext = selection[0][0].context; - } else { - displayLayoutContext = selectionPath[1].context; + function getViewSwitcherMenu(selectedParent, selectionPath, selection) { + if (selection.length === 1) { + let displayLayoutContext = selectionPath[1].context; + let selectedItemContext = selectionPath[0].context; + let selectedItemType = selectedItemContext.item.type; + + if (selectedItemContext.layoutItem.type === 'telemetry-view') { + selectedItemType = 'telemetry-view'; } - return { - control: 'button', - domainObject: displayLayoutContext.item, - icon: ICON_GRID_SHOW, - method: function () { - displayLayoutContext.toggleGrid(); + let viewOptions = APPLICABLE_VIEWS[selectedItemType]; - this.icon = this.icon === ICON_GRID_SHOW ? ICON_GRID_HIDE : ICON_GRID_SHOW; - }, - secondary: true - }; - } + if (viewOptions) { + return { + control: 'menu', + domainObject: selectedParent, + icon: 'icon-object', + title: 'Switch the way this telemetry is displayed', + label: 'View type', + options: viewOptions, + method: function (option) { + displayLayoutContext.switchViewType(selectedItemContext, option.value, selection); + } + }; + } + } else if (selection.length > 1) { + if (areAllViews('telemetry-view', 'layoutItem.type', selection)) { + let displayLayoutContext = selectionPath[1].context; - function getSeparator() { - return { - control: 'separator' - }; + return { + control: 'menu', + domainObject: selectedParent, + icon: 'icon-object', + title: 'Merge into a telemetry table or plot', + label: 'View type', + options: APPLICABLE_VIEWS['telemetry-view-multi'], + method: function (option) { + displayLayoutContext.mergeMultipleTelemetryViews(selection, option.value); + } + }; + } else if (areAllViews('telemetry.plot.overlay', 'item.type', selection)) { + let displayLayoutContext = selectionPath[1].context; + + return { + control: 'menu', + domainObject: selectedParent, + icon: 'icon-object', + title: 'Merge into a stacked plot', + options: APPLICABLE_VIEWS['telemetry.plot.overlay-multi'], + method: function (option) { + displayLayoutContext.mergeMultipleOverlayPlots(selection, option.value); + } + }; + } } + } - function isMainLayoutSelected(selectionPath) { - let selectedObject = selectionPath[0].context.item; + function getToggleGridButton(selection, selectionPath) { + const ICON_GRID_SHOW = 'icon-grid-on'; + const ICON_GRID_HIDE = 'icon-grid-off'; - return ( - selectedObject && - selectedObject.type === 'layout' && - !selectionPath[0].context.layoutItem - ); - } + let displayLayoutContext; - function showForm(formStructure, name, selectionPath) { - openmct.forms.showForm(formStructure).then((changes) => { - selectionPath[0].context.addElement(name, changes); - }); + if (selection.length === 1 && selectionPath === undefined) { + displayLayoutContext = selection[0][0].context; + } else { + displayLayoutContext = selectionPath[1].context; } - if (isMainLayoutSelected(selectedObjects[0])) { - return [getToggleGridButton(selectedObjects), getAddButton(selectedObjects)]; - } + return { + control: 'button', + domainObject: displayLayoutContext.item, + icon: ICON_GRID_SHOW, + method: function () { + displayLayoutContext.toggleGrid(); - let toolbar = { - 'add-menu': [], - text: [], - url: [], - viewSwitcher: [], - 'toggle-frame': [], - 'display-mode': [], - 'telemetry-value': [], - style: [], - 'unit-toggle': [], - position: [], - duplicate: [], - remove: [], - 'toggle-grid': [] + this.icon = this.icon === ICON_GRID_SHOW ? ICON_GRID_HIDE : ICON_GRID_SHOW; + }, + secondary: true }; + } - selectedObjects.forEach((selectionPath) => { - let selectedParent = selectionPath[1].context.item; - let layoutItem = selectionPath[0].context.layoutItem; + function getSeparator() { + return { + control: 'separator' + }; + } - if (!layoutItem || selectedParent.locked) { - return; - } + function isMainLayoutSelected(selectionPath) { + let selectedObject = selectionPath[0].context.item; - if (layoutItem.type === 'subobject-view') { - if ( - toolbar['add-menu'].length === 0 && - selectionPath[0].context.item.type === 'layout' - ) { - toolbar['add-menu'] = [getAddButton(selectedObjects, selectionPath)]; - } + return ( + selectedObject && selectedObject.type === 'layout' && !selectionPath[0].context.layoutItem + ); + } - if (toolbar['toggle-frame'].length === 0) { - toolbar['toggle-frame'] = [getToggleFrameButton(selectedParent, selectedObjects)]; - } + function showForm(formStructure, name, selectionPath) { + openmct.forms.showForm(formStructure).then((changes) => { + selectionPath[0].context.addElement(name, changes); + }); + } - if (toolbar.position.length === 0) { - toolbar.position = [ - getStackOrder(selectedParent, selectionPath), - getSeparator(), - getXInput(selectedParent, selectedObjects), - getYInput(selectedParent, selectedObjects), - getHeightInput(selectedParent, selectedObjects), - getWidthInput(selectedParent, selectedObjects) - ]; - } + if (isMainLayoutSelected(selectedObjects[0])) { + return [getToggleGridButton(selectedObjects), getAddButton(selectedObjects)]; + } - if (toolbar.remove.length === 0) { - toolbar.remove = [getRemoveButton(selectedParent, selectionPath, selectedObjects)]; - } + let toolbar = { + 'add-menu': [], + text: [], + url: [], + viewSwitcher: [], + 'toggle-frame': [], + 'display-mode': [], + 'telemetry-value': [], + style: [], + 'unit-toggle': [], + position: [], + duplicate: [], + remove: [], + 'toggle-grid': [] + }; + + selectedObjects.forEach((selectionPath) => { + let selectedParent = selectionPath[1].context.item; + let layoutItem = selectionPath[0].context.layoutItem; + + if (!layoutItem || selectedParent.locked) { + return; + } - if (toolbar.viewSwitcher.length === 0) { - toolbar.viewSwitcher = [ - getViewSwitcherMenu(selectedParent, selectionPath, selectedObjects) - ]; - } - } else if (layoutItem.type === 'telemetry-view') { - if (toolbar['display-mode'].length === 0) { - toolbar['display-mode'] = [getDisplayModeMenu(selectedParent, selectedObjects)]; - } + if (layoutItem.type === 'subobject-view') { + if (toolbar['add-menu'].length === 0 && selectionPath[0].context.item.type === 'layout') { + toolbar['add-menu'] = [getAddButton(selectedObjects, selectionPath)]; + } - if (toolbar['telemetry-value'].length === 0) { - toolbar['telemetry-value'] = [getTelemetryValueMenu(selectionPath, selectedObjects)]; - } + if (toolbar['toggle-frame'].length === 0) { + toolbar['toggle-frame'] = [getToggleFrameButton(selectedParent, selectedObjects)]; + } - if (toolbar['unit-toggle'].length === 0) { - let toggleUnitsButton = getToggleUnitsButton(selectedParent, selectedObjects); - if (toggleUnitsButton) { - toolbar['unit-toggle'] = [toggleUnitsButton]; - } - } + if (toolbar.position.length === 0) { + toolbar.position = [ + getStackOrder(selectedParent, selectionPath), + getSeparator(), + getXInput(selectedParent, selectedObjects), + getYInput(selectedParent, selectedObjects), + getHeightInput(selectedParent, selectedObjects), + getWidthInput(selectedParent, selectedObjects) + ]; + } - if (toolbar.position.length === 0) { - toolbar.position = [ - getStackOrder(selectedParent, selectionPath), - getSeparator(), - getXInput(selectedParent, selectedObjects), - getYInput(selectedParent, selectedObjects), - getHeightInput(selectedParent, selectedObjects), - getWidthInput(selectedParent, selectedObjects) - ]; - } + if (toolbar.remove.length === 0) { + toolbar.remove = [getRemoveButton(selectedParent, selectionPath, selectedObjects)]; + } - if (toolbar.remove.length === 0) { - toolbar.remove = [getRemoveButton(selectedParent, selectionPath, selectedObjects)]; - } + if (toolbar.viewSwitcher.length === 0) { + toolbar.viewSwitcher = [ + getViewSwitcherMenu(selectedParent, selectionPath, selectedObjects) + ]; + } + } else if (layoutItem.type === 'telemetry-view') { + if (toolbar['display-mode'].length === 0) { + toolbar['display-mode'] = [getDisplayModeMenu(selectedParent, selectedObjects)]; + } - if (toolbar.viewSwitcher.length === 0) { - toolbar.viewSwitcher = [ - getViewSwitcherMenu(selectedParent, selectionPath, selectedObjects) - ]; - } - } else if (layoutItem.type === 'text-view') { - if (toolbar.position.length === 0) { - toolbar.position = [ - getStackOrder(selectedParent, selectionPath), - getSeparator(), - getXInput(selectedParent, selectedObjects), - getYInput(selectedParent, selectedObjects), - getHeightInput(selectedParent, selectedObjects), - getWidthInput(selectedParent, selectedObjects) - ]; - } + if (toolbar['telemetry-value'].length === 0) { + toolbar['telemetry-value'] = [getTelemetryValueMenu(selectionPath, selectedObjects)]; + } - if (toolbar.text.length === 0) { - toolbar.text = [getTextButton(selectedParent, selectedObjects)]; + if (toolbar['unit-toggle'].length === 0) { + let toggleUnitsButton = getToggleUnitsButton(selectedParent, selectedObjects); + if (toggleUnitsButton) { + toolbar['unit-toggle'] = [toggleUnitsButton]; } + } - if (toolbar.remove.length === 0) { - toolbar.remove = [getRemoveButton(selectedParent, selectionPath, selectedObjects)]; - } - } else if (layoutItem.type === 'box-view' || layoutItem.type === 'ellipse-view') { - if (toolbar.position.length === 0) { - toolbar.position = [ - getStackOrder(selectedParent, selectionPath), - getSeparator(), - getXInput(selectedParent, selectedObjects), - getYInput(selectedParent, selectedObjects), - getHeightInput(selectedParent, selectedObjects), - getWidthInput(selectedParent, selectedObjects) - ]; - } + if (toolbar.position.length === 0) { + toolbar.position = [ + getStackOrder(selectedParent, selectionPath), + getSeparator(), + getXInput(selectedParent, selectedObjects), + getYInput(selectedParent, selectedObjects), + getHeightInput(selectedParent, selectedObjects), + getWidthInput(selectedParent, selectedObjects) + ]; + } - if (toolbar.remove.length === 0) { - toolbar.remove = [getRemoveButton(selectedParent, selectionPath, selectedObjects)]; - } - } else if (layoutItem.type === 'image-view') { - if (toolbar.position.length === 0) { - toolbar.position = [ - getStackOrder(selectedParent, selectionPath), - getSeparator(), - getXInput(selectedParent, selectedObjects), - getYInput(selectedParent, selectedObjects), - getHeightInput(selectedParent, selectedObjects), - getWidthInput(selectedParent, selectedObjects) - ]; - } + if (toolbar.remove.length === 0) { + toolbar.remove = [getRemoveButton(selectedParent, selectionPath, selectedObjects)]; + } - if (toolbar.remove.length === 0) { - toolbar.remove = [getRemoveButton(selectedParent, selectionPath, selectedObjects)]; - } - } else if (layoutItem.type === 'line-view') { - if (toolbar.position.length === 0) { - toolbar.position = [ - getStackOrder(selectedParent, selectionPath), - getSeparator(), - getXInput(selectedParent, selectedObjects), - getYInput(selectedParent, selectedObjects), - getX2Input(selectedParent, selectedObjects), - getY2Input(selectedParent, selectedObjects) - ]; - } + if (toolbar.viewSwitcher.length === 0) { + toolbar.viewSwitcher = [ + getViewSwitcherMenu(selectedParent, selectionPath, selectedObjects) + ]; + } + } else if (layoutItem.type === 'text-view') { + if (toolbar.position.length === 0) { + toolbar.position = [ + getStackOrder(selectedParent, selectionPath), + getSeparator(), + getXInput(selectedParent, selectedObjects), + getYInput(selectedParent, selectedObjects), + getHeightInput(selectedParent, selectedObjects), + getWidthInput(selectedParent, selectedObjects) + ]; + } - if (toolbar.remove.length === 0) { - toolbar.remove = [getRemoveButton(selectedParent, selectionPath, selectedObjects)]; - } + if (toolbar.text.length === 0) { + toolbar.text = [getTextButton(selectedParent, selectedObjects)]; } - if (toolbar.duplicate.length === 0) { - toolbar.duplicate = [ - getDuplicateButton(selectedParent, selectionPath, selectedObjects) + if (toolbar.remove.length === 0) { + toolbar.remove = [getRemoveButton(selectedParent, selectionPath, selectedObjects)]; + } + } else if (layoutItem.type === 'box-view' || layoutItem.type === 'ellipse-view') { + if (toolbar.position.length === 0) { + toolbar.position = [ + getStackOrder(selectedParent, selectionPath), + getSeparator(), + getXInput(selectedParent, selectedObjects), + getYInput(selectedParent, selectedObjects), + getHeightInput(selectedParent, selectedObjects), + getWidthInput(selectedParent, selectedObjects) ]; } - if (toolbar['toggle-grid'].length === 0) { - toolbar['toggle-grid'] = [getToggleGridButton(selectedObjects, selectionPath)]; + if (toolbar.remove.length === 0) { + toolbar.remove = [getRemoveButton(selectedParent, selectionPath, selectedObjects)]; } - }); + } else if (layoutItem.type === 'image-view') { + if (toolbar.position.length === 0) { + toolbar.position = [ + getStackOrder(selectedParent, selectionPath), + getSeparator(), + getXInput(selectedParent, selectedObjects), + getYInput(selectedParent, selectedObjects), + getHeightInput(selectedParent, selectedObjects), + getWidthInput(selectedParent, selectedObjects) + ]; + } + + if (toolbar.remove.length === 0) { + toolbar.remove = [getRemoveButton(selectedParent, selectionPath, selectedObjects)]; + } + } else if (layoutItem.type === 'line-view') { + if (toolbar.position.length === 0) { + toolbar.position = [ + getStackOrder(selectedParent, selectionPath), + getSeparator(), + getXInput(selectedParent, selectedObjects), + getYInput(selectedParent, selectedObjects), + getX2Input(selectedParent, selectedObjects), + getY2Input(selectedParent, selectedObjects) + ]; + } + + if (toolbar.remove.length === 0) { + toolbar.remove = [getRemoveButton(selectedParent, selectionPath, selectedObjects)]; + } + } - let toolbarArray = Object.values(toolbar); + if (toolbar.duplicate.length === 0) { + toolbar.duplicate = [getDuplicateButton(selectedParent, selectionPath, selectedObjects)]; + } - return _.flatten( - toolbarArray.reduce((accumulator, group, index) => { - group = group.filter((control) => control !== undefined); + if (toolbar['toggle-grid'].length === 0) { + toolbar['toggle-grid'] = [getToggleGridButton(selectedObjects, selectionPath)]; + } + }); - if (group.length > 0) { - accumulator.push(group); + let toolbarArray = Object.values(toolbar); - if (index < toolbarArray.length - 1) { - accumulator.push(getSeparator()); - } - } + return _.flatten( + toolbarArray.reduce((accumulator, group, index) => { + group = group.filter((control) => control !== undefined); - return accumulator; - }, []) - ); - } - }; - } + if (group.length > 0) { + accumulator.push(group); + + if (index < toolbarArray.length - 1) { + accumulator.push(getSeparator()); + } + } - return DisplayLayoutToolbar; -}); + return accumulator; + }, []) + ); + } + }; +} diff --git a/src/plugins/displayLayout/DisplayLayoutType.js b/src/plugins/displayLayout/DisplayLayoutType.js index bf631fdf5f0..caae90c2b49 100644 --- a/src/plugins/displayLayout/DisplayLayoutType.js +++ b/src/plugins/displayLayout/DisplayLayoutType.js @@ -20,53 +20,49 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define(function () { - function DisplayLayoutType() { - return { - name: 'Display Layout', - creatable: true, - description: - 'Assemble other objects and components together into a reusable screen layout. Simply drag in the objects you want, position and size them. Save your design and view or edit it at any time.', - cssClass: 'icon-layout', - initialize(domainObject) { - domainObject.composition = []; - domainObject.configuration = { - items: [], - layoutGrid: [10, 10] - }; +export default function DisplayLayoutType() { + return { + name: 'Display Layout', + creatable: true, + description: + 'Assemble other objects and components together into a reusable screen layout. Simply drag in the objects you want, position and size them. Save your design and view or edit it at any time.', + cssClass: 'icon-layout', + initialize(domainObject) { + domainObject.composition = []; + domainObject.configuration = { + items: [], + layoutGrid: [10, 10] + }; + }, + form: [ + { + name: 'Horizontal grid (px)', + control: 'numberfield', + cssClass: 'l-input-sm l-numeric', + property: ['configuration', 'layoutGrid', 0], + required: true }, - form: [ - { - name: 'Horizontal grid (px)', - control: 'numberfield', - cssClass: 'l-input-sm l-numeric', - property: ['configuration', 'layoutGrid', 0], - required: true - }, - { - name: 'Vertical grid (px)', - control: 'numberfield', - cssClass: 'l-input-sm l-numeric', - property: ['configuration', 'layoutGrid', 1], - required: true - }, - { - name: 'Horizontal size (px)', - control: 'numberfield', - cssClass: 'l-input-sm l-numeric', - property: ['configuration', 'layoutDimensions', 0], - required: false - }, - { - name: 'Vertical size (px)', - control: 'numberfield', - cssClass: 'l-input-sm l-numeric', - property: ['configuration', 'layoutDimensions', 1], - required: false - } - ] - }; - } - - return DisplayLayoutType; -}); + { + name: 'Vertical grid (px)', + control: 'numberfield', + cssClass: 'l-input-sm l-numeric', + property: ['configuration', 'layoutGrid', 1], + required: true + }, + { + name: 'Horizontal size (px)', + control: 'numberfield', + cssClass: 'l-input-sm l-numeric', + property: ['configuration', 'layoutDimensions', 0], + required: false + }, + { + name: 'Vertical size (px)', + control: 'numberfield', + cssClass: 'l-input-sm l-numeric', + property: ['configuration', 'layoutDimensions', 1], + required: false + } + ] + }; +} diff --git a/src/plugins/displayLayout/LayoutDrag.js b/src/plugins/displayLayout/LayoutDrag.js index afbb464d346..50cd28425ed 100644 --- a/src/plugins/displayLayout/LayoutDrag.js +++ b/src/plugins/displayLayout/LayoutDrag.js @@ -20,93 +20,89 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define([], function () { - /** - * Handles drag interactions on frames in layouts. This will - * provides new positions/dimensions for frames based on - * relative pixel positions provided; these will take into account - * the grid size (in a snap-to sense) and will enforce some minimums - * on both position and dimensions. - * - * The provided position and dimensions factors will determine - * whether this is a move or a resize, and what type of resize it - * will be. For instance, a position factor of [1, 1] - * will move a frame along with the mouse as the drag - * proceeds, while a dimension factor of [0, 0] will leave - * dimensions unchanged. Combining these in different - * ways results in different handles; a position factor of - * [1, 0] and a dimensions factor of [-1, 0] will implement - * a left-edge resize, as the horizontal position will move - * with the mouse while the horizontal dimensions shrink in - * kind (and vertical properties remain unmodified.) - * - * @param {object} rawPosition the initial position/dimensions - * of the frame being interacted with - * @param {number[]} posFactor the position factor - * @param {number[]} dimFactor the dimensions factor - * @param {number[]} the size of each grid element, in pixels - * @constructor - * @memberof platform/features/layout - */ - function LayoutDrag(rawPosition, posFactor, dimFactor, gridSize) { - this.rawPosition = rawPosition; - this.posFactor = posFactor; - this.dimFactor = dimFactor; - this.gridSize = gridSize; - } +/** + * Handles drag interactions on frames in layouts. This will + * provides new positions/dimensions for frames based on + * relative pixel positions provided; these will take into account + * the grid size (in a snap-to sense) and will enforce some minimums + * on both position and dimensions. + * + * The provided position and dimensions factors will determine + * whether this is a move or a resize, and what type of resize it + * will be. For instance, a position factor of [1, 1] + * will move a frame along with the mouse as the drag + * proceeds, while a dimension factor of [0, 0] will leave + * dimensions unchanged. Combining these in different + * ways results in different handles; a position factor of + * [1, 0] and a dimensions factor of [-1, 0] will implement + * a left-edge resize, as the horizontal position will move + * with the mouse while the horizontal dimensions shrink in + * kind (and vertical properties remain unmodified.) + * + * @param {object} rawPosition the initial position/dimensions + * of the frame being interacted with + * @param {number[]} posFactor the position factor + * @param {number[]} dimFactor the dimensions factor + * @param {number[]} the size of each grid element, in pixels + * @constructor + * @memberof platform/features/layout + */ +export default function LayoutDrag(rawPosition, posFactor, dimFactor, gridSize) { + this.rawPosition = rawPosition; + this.posFactor = posFactor; + this.dimFactor = dimFactor; + this.gridSize = gridSize; +} - // Convert a delta from pixel coordinates to grid coordinates, - // rounding to whole-number grid coordinates. - function toGridDelta(gridSize, pixelDelta) { - return pixelDelta.map(function (v, i) { - return Math.round(v / gridSize[i]); - }); - } +// Convert a delta from pixel coordinates to grid coordinates, +// rounding to whole-number grid coordinates. +function toGridDelta(gridSize, pixelDelta) { + return pixelDelta.map(function (v, i) { + return Math.round(v / gridSize[i]); + }); +} - // Utility function to perform element-by-element multiplication - function multiply(array, factors) { - return array.map(function (v, i) { - return v * factors[i]; - }); - } +// Utility function to perform element-by-element multiplication +function multiply(array, factors) { + return array.map(function (v, i) { + return v * factors[i]; + }); +} - // Utility function to perform element-by-element addition - function add(array, other) { - return array.map(function (v, i) { - return v + other[i]; - }); - } +// Utility function to perform element-by-element addition +function add(array, other) { + return array.map(function (v, i) { + return v + other[i]; + }); +} - // Utility function to perform element-by-element max-choosing - function max(array, other) { - return array.map(function (v, i) { - return Math.max(v, other[i]); - }); - } +// Utility function to perform element-by-element max-choosing +function max(array, other) { + return array.map(function (v, i) { + return Math.max(v, other[i]); + }); +} - /** - * Get a new position object in grid coordinates, with - * position and dimensions both offset appropriately - * according to the factors supplied in the constructor. - * @param {number[]} pixelDelta the offset from the - * original position, in pixels - */ - LayoutDrag.prototype.getAdjustedPositionAndDimensions = function (pixelDelta) { - const gridDelta = toGridDelta(this.gridSize, pixelDelta); +/** + * Get a new position object in grid coordinates, with + * position and dimensions both offset appropriately + * according to the factors supplied in the constructor. + * @param {number[]} pixelDelta the offset from the + * original position, in pixels + */ +LayoutDrag.prototype.getAdjustedPositionAndDimensions = function (pixelDelta) { + const gridDelta = toGridDelta(this.gridSize, pixelDelta); - return { - position: max(add(this.rawPosition.position, multiply(gridDelta, this.posFactor)), [0, 0]), - dimensions: max(add(this.rawPosition.dimensions, multiply(gridDelta, this.dimFactor)), [1, 1]) - }; + return { + position: max(add(this.rawPosition.position, multiply(gridDelta, this.posFactor)), [0, 0]), + dimensions: max(add(this.rawPosition.dimensions, multiply(gridDelta, this.dimFactor)), [1, 1]) }; +}; - LayoutDrag.prototype.getAdjustedPosition = function (pixelDelta) { - const gridDelta = toGridDelta(this.gridSize, pixelDelta); +LayoutDrag.prototype.getAdjustedPosition = function (pixelDelta) { + const gridDelta = toGridDelta(this.gridSize, pixelDelta); - return { - position: max(add(this.rawPosition.position, multiply(gridDelta, this.posFactor)), [0, 0]) - }; + return { + position: max(add(this.rawPosition.position, multiply(gridDelta, this.posFactor)), [0, 0]) }; - - return LayoutDrag; -}); +}; diff --git a/src/plugins/filters/plugin.js b/src/plugins/filters/plugin.js index 4649b9271a7..0a32662acde 100644 --- a/src/plugins/filters/plugin.js +++ b/src/plugins/filters/plugin.js @@ -20,12 +20,12 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define(['./FiltersInspectorViewProvider'], function (FiltersInspectorViewProvider) { - return function plugin(supportedObjectTypesArray) { - return function install(openmct) { - openmct.inspectorViews.addProvider( - new FiltersInspectorViewProvider.default(openmct, supportedObjectTypesArray) - ); - }; +import FiltersInspectorViewProvider from './FiltersInspectorViewProvider'; + +export default function plugin(supportedObjectTypesArray) { + return function install(openmct) { + openmct.inspectorViews.addProvider( + new FiltersInspectorViewProvider(openmct, supportedObjectTypesArray) + ); }; -}); +} diff --git a/src/plugins/flexibleLayout/plugin.js b/src/plugins/flexibleLayout/plugin.js index e993cde6e56..a97c304e944 100644 --- a/src/plugins/flexibleLayout/plugin.js +++ b/src/plugins/flexibleLayout/plugin.js @@ -20,33 +20,31 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define(['./flexibleLayoutViewProvider', './utils/container', './toolbarProvider'], function ( - FlexibleLayoutViewProvider, - Container, - ToolBarProvider -) { - return function plugin() { - return function install(openmct) { - openmct.objectViews.addProvider(new FlexibleLayoutViewProvider.default(openmct)); +import FlexibleLayoutViewProvider from './flexibleLayoutViewProvider'; +import ToolBarProvider from './toolbarProvider'; +import Container from './utils/container'; - openmct.types.addType('flexible-layout', { - name: 'Flexible Layout', - creatable: true, - description: - 'A fluid, flexible layout canvas that can display multiple objects in rows or columns.', - cssClass: 'icon-flexible-layout', - initialize: function (domainObject) { - domainObject.configuration = { - containers: [new Container.default(50), new Container.default(50)], - rowsLayout: false - }; - domainObject.composition = []; - } - }); +export default function plugin() { + return function install(openmct) { + openmct.objectViews.addProvider(new FlexibleLayoutViewProvider(openmct)); - let toolbar = ToolBarProvider.default(openmct); + openmct.types.addType('flexible-layout', { + name: 'Flexible Layout', + creatable: true, + description: + 'A fluid, flexible layout canvas that can display multiple objects in rows or columns.', + cssClass: 'icon-flexible-layout', + initialize: function (domainObject) { + domainObject.configuration = { + containers: [new Container(50), new Container(50)], + rowsLayout: false + }; + domainObject.composition = []; + } + }); - openmct.toolbars.addProvider(toolbar); - }; + let toolbar = ToolBarProvider(openmct); + + openmct.toolbars.addProvider(toolbar); }; -}); +} diff --git a/src/plugins/imagery/lib/eventHelpers.js b/src/plugins/imagery/lib/eventHelpers.js index 367072b9f66..79aef224880 100644 --- a/src/plugins/imagery/lib/eventHelpers.js +++ b/src/plugins/imagery/lib/eventHelpers.js @@ -20,80 +20,78 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define([], function () { - const helperFunctions = { - listenTo: function (object, event, callback, context) { - if (!this._listeningTo) { - this._listeningTo = []; - } +const helperFunctions = { + listenTo: function (object, event, callback, context) { + if (!this._listeningTo) { + this._listeningTo = []; + } - const listener = { - object: object, - event: event, - callback: callback, - context: context, - _cb: context ? callback.bind(context) : callback - }; - if (object.$watch && event.indexOf('change:') === 0) { - const scopePath = event.replace('change:', ''); - listener.unlisten = object.$watch(scopePath, listener._cb, true); - } else if (object.$on) { - listener.unlisten = object.$on(event, listener._cb); - } else if (object.addEventListener) { - object.addEventListener(event, listener._cb); - } else { - object.on(event, listener._cb); - } + 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); - }, + this._listeningTo.push(listener); + }, - stopListening: function (object, event, callback, context) { - if (!this._listeningTo) { - this._listeningTo = []; - } + stopListening: function (object, event, callback, context) { + if (!this._listeningTo) { + this._listeningTo = []; + } - this._listeningTo - .filter(function (listener) { - if (object && object !== listener.object) { - return false; - } + this._listeningTo + .filter(function (listener) { + if (object && object !== listener.object) { + return false; + } - if (event && event !== listener.event) { - return false; - } + if (event && event !== listener.event) { + return false; + } - if (callback && callback !== listener.callback) { - return false; - } + if (callback && callback !== listener.callback) { + return false; + } - if (context && context !== listener.context) { - 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 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); - }, + 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; - } - }; + extend: function (object) { + object.listenTo = helperFunctions.listenTo; + object.stopListening = helperFunctions.stopListening; + } +}; - return helperFunctions; -}); +export default helperFunctions; diff --git a/src/plugins/latestDataClock/LADClock.js b/src/plugins/latestDataClock/LADClock.js index 131f7930e92..7c5097fbe3a 100644 --- a/src/plugins/latestDataClock/LADClock.js +++ b/src/plugins/latestDataClock/LADClock.js @@ -20,24 +20,24 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define(['../../../src/plugins/utcTimeSystem/LocalClock'], function (LocalClock) { +import LocalClock from '../../../src/plugins/utcTimeSystem/LocalClock'; + +class LADClock extends LocalClock { /** * A {@link Clock} that mocks a "latest available data" type tick source. * This is for testing purposes only, and behaves identically to a local clock. * It DOES NOT tick on receipt of data. * @constructor */ - function LADClock(period) { - LocalClock.call(this, period); + constructor(period) { + super(period); this.key = 'test-lad'; this.mode = 'lad'; this.cssClass = 'icon-suitcase'; this.name = 'Latest available data'; - this.description = 'Updates when when new data is available'; + this.description = 'Updates when new data is available'; } +} - LADClock.prototype = Object.create(LocalClock.prototype); - - return LADClock; -}); +export default LADClock; diff --git a/src/plugins/latestDataClock/plugin.js b/src/plugins/latestDataClock/plugin.js index a3ba32ccec0..fd3684c24bc 100644 --- a/src/plugins/latestDataClock/plugin.js +++ b/src/plugins/latestDataClock/plugin.js @@ -20,10 +20,10 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define(['./LADClock'], function (LADClock) { - return function () { - return function (openmct) { - openmct.time.addClock(new LADClock()); - }; +import LADClock from './LADClock'; + +export default function () { + return function (openmct) { + openmct.time.addClock(new LADClock()); }; -}); +} diff --git a/src/plugins/localTimeSystem/LocalTimeFormat.js b/src/plugins/localTimeSystem/LocalTimeFormat.js index 2311ebb913d..66d841ecc99 100644 --- a/src/plugins/localTimeSystem/LocalTimeFormat.js +++ b/src/plugins/localTimeSystem/LocalTimeFormat.js @@ -20,49 +20,47 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define(['moment'], function (moment) { - const DATE_FORMAT = 'YYYY-MM-DD h:mm:ss.SSS a'; +import moment from 'moment'; - const DATE_FORMATS = [DATE_FORMAT, 'YYYY-MM-DD h:mm:ss a', 'YYYY-MM-DD h:mm a', 'YYYY-MM-DD']; +const DATE_FORMAT = 'YYYY-MM-DD h:mm:ss.SSS a'; - /** - * @typedef Scale - * @property {number} min the minimum scale value, in ms - * @property {number} max the maximum scale value, in ms - */ +const DATE_FORMATS = [DATE_FORMAT, 'YYYY-MM-DD h:mm:ss a', 'YYYY-MM-DD h:mm a', 'YYYY-MM-DD']; - /** - * Formatter for UTC timestamps. Interprets numeric values as - * milliseconds since the start of 1970. - * - * @implements {Format} - * @constructor - * @memberof platform/commonUI/formats - */ - function LocalTimeFormat() { - this.key = 'local-format'; - } +/** + * @typedef Scale + * @property {number} min the minimum scale value, in ms + * @property {number} max the maximum scale value, in ms + */ - /** - * - * @param value - * @returns {string} the formatted date - */ - LocalTimeFormat.prototype.format = function (value, scale) { - return moment(value).format(DATE_FORMAT); - }; +/** + * Formatter for UTC timestamps. Interprets numeric values as + * milliseconds since the start of 1970. + * + * @implements {Format} + * @constructor + * @memberof platform/commonUI/formats + */ +export default function LocalTimeFormat() { + this.key = 'local-format'; +} - LocalTimeFormat.prototype.parse = function (text) { - if (typeof text === 'number') { - return text; - } +/** + * + * @param value + * @returns {string} the formatted date + */ +LocalTimeFormat.prototype.format = function (value, scale) { + return moment(value).format(DATE_FORMAT); +}; - return moment(text, DATE_FORMATS).valueOf(); - }; +LocalTimeFormat.prototype.parse = function (text) { + if (typeof text === 'number') { + return text; + } - LocalTimeFormat.prototype.validate = function (text) { - return moment(text, DATE_FORMATS).isValid(); - }; + return moment(text, DATE_FORMATS).valueOf(); +}; - return LocalTimeFormat; -}); +LocalTimeFormat.prototype.validate = function (text) { + return moment(text, DATE_FORMATS).isValid(); +}; diff --git a/src/plugins/localTimeSystem/LocalTimeSystem.js b/src/plugins/localTimeSystem/LocalTimeSystem.js index abd36bbcaf9..5e7ca160251 100644 --- a/src/plugins/localTimeSystem/LocalTimeSystem.js +++ b/src/plugins/localTimeSystem/LocalTimeSystem.js @@ -20,13 +20,13 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define([], function () { - /** - * This time system supports UTC dates and provides a ticking clock source. - * @implements TimeSystem - * @constructor - */ - function LocalTimeSystem() { +/** + * This time system supports UTC dates and provides a ticking clock source. + * @implements TimeSystem + * @constructor + */ +export default class LocalTimeSystem { + constructor() { /** * Some metadata, which will be used to identify the time system in * the UI @@ -41,6 +41,4 @@ define([], function () { this.isUTCBased = true; } - - return LocalTimeSystem; -}); +} diff --git a/src/plugins/localTimeSystem/plugin.js b/src/plugins/localTimeSystem/plugin.js index 161e7b41f6f..47599b621fc 100644 --- a/src/plugins/localTimeSystem/plugin.js +++ b/src/plugins/localTimeSystem/plugin.js @@ -20,11 +20,12 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define(['./LocalTimeSystem', './LocalTimeFormat'], function (LocalTimeSystem, LocalTimeFormat) { - return function () { - return function (openmct) { - openmct.time.addTimeSystem(new LocalTimeSystem()); - openmct.telemetry.addFormat(new LocalTimeFormat()); - }; +import LocalTimeFormat from './LocalTimeFormat'; +import LocalTimeSystem from './LocalTimeSystem'; + +export default function () { + return function (openmct) { + openmct.time.addTimeSystem(new LocalTimeSystem()); + openmct.telemetry.addFormat(new LocalTimeFormat()); }; -}); +} diff --git a/src/plugins/objectMigration/Migrations.js b/src/plugins/objectMigration/Migrations.js index 63e43d6ebb9..ccffb016c76 100644 --- a/src/plugins/objectMigration/Migrations.js +++ b/src/plugins/objectMigration/Migrations.js @@ -20,260 +20,260 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define(['uuid'], function ({ v4: uuid }) { - return function Migrations(openmct) { - function getColumnNameKeyMap(domainObject) { - let composition = openmct.composition.get(domainObject); - if (composition) { - return composition.load().then((composees) => { - return composees.reduce((nameKeyMap, composee) => { - let metadata = openmct.telemetry.getMetadata(composee); - if (metadata !== undefined) { - metadata.values().forEach((value) => { - nameKeyMap[value.name] = value.key; - }); - } +import { v4 as uuid } from 'uuid'; - return nameKeyMap; - }, {}); - }); - } else { - return Promise.resolve([]); - } - } +export default function Migrations(openmct) { + function getColumnNameKeyMap(domainObject) { + let composition = openmct.composition.get(domainObject); + if (composition) { + return composition.load().then((composees) => { + return composees.reduce((nameKeyMap, composee) => { + let metadata = openmct.telemetry.getMetadata(composee); + if (metadata !== undefined) { + metadata.values().forEach((value) => { + nameKeyMap[value.name] = value.key; + }); + } - function isTelemetry(domainObject) { - if ( - openmct.telemetry.isTelemetryObject(domainObject) && - domainObject.type !== 'summary-widget' && - domainObject.type !== 'example.imagery' - ) { - return true; - } else { - return false; - } + return nameKeyMap; + }, {}); + }); + } else { + return Promise.resolve([]); } + } - function migrateDisplayLayout(domainObject, childObjects) { - const DEFAULT_GRID_SIZE = [32, 32]; - let migratedObject = Object.assign({}, domainObject); - let panels = migratedObject.configuration.layout.panels; - let items = []; + function isTelemetry(domainObject) { + if ( + openmct.telemetry.isTelemetryObject(domainObject) && + domainObject.type !== 'summary-widget' && + domainObject.type !== 'example.imagery' + ) { + return true; + } else { + return false; + } + } - Object.keys(panels).forEach((key) => { - let panel = panels[key]; - let childDomainObject = childObjects[key]; - let identifier = undefined; + function migrateDisplayLayout(domainObject, childObjects) { + const DEFAULT_GRID_SIZE = [32, 32]; + let migratedObject = Object.assign({}, domainObject); + let panels = migratedObject.configuration.layout.panels; + let items = []; - if (isTelemetry(childDomainObject)) { - // If object is a telemetry point, convert it to a plot and - // replace the object in migratedObject composition with the plot. - identifier = { - key: uuid(), - namespace: migratedObject.identifier.namespace - }; - let plotObject = { - identifier: identifier, - location: childDomainObject.location, - name: childDomainObject.name, - type: 'telemetry.plot.overlay' - }; - let plotType = openmct.types.get('telemetry.plot.overlay'); - plotType.definition.initialize(plotObject); - plotObject.composition.push(childDomainObject.identifier); - openmct.objects.mutate(plotObject, 'persisted', Date.now()); + Object.keys(panels).forEach((key) => { + let panel = panels[key]; + let childDomainObject = childObjects[key]; + let identifier = undefined; - let keyString = openmct.objects.makeKeyString(childDomainObject.identifier); - let clonedComposition = Object.assign([], migratedObject.composition); - clonedComposition.forEach((objIdentifier, index) => { - if (openmct.objects.makeKeyString(objIdentifier) === keyString) { - migratedObject.composition[index] = plotObject.identifier; - } - }); - } + if (isTelemetry(childDomainObject)) { + // If object is a telemetry point, convert it to a plot and + // replace the object in migratedObject composition with the plot. + identifier = { + key: uuid(), + namespace: migratedObject.identifier.namespace + }; + let plotObject = { + identifier: identifier, + location: childDomainObject.location, + name: childDomainObject.name, + type: 'telemetry.plot.overlay' + }; + let plotType = openmct.types.get('telemetry.plot.overlay'); + plotType.definition.initialize(plotObject); + plotObject.composition.push(childDomainObject.identifier); + openmct.objects.mutate(plotObject, 'persisted', Date.now()); - items.push({ - width: panel.dimensions[0], - height: panel.dimensions[1], - x: panel.position[0], - y: panel.position[1], - identifier: identifier || childDomainObject.identifier, - id: uuid(), - type: 'subobject-view', - hasFrame: panel.hasFrame + let keyString = openmct.objects.makeKeyString(childDomainObject.identifier); + let clonedComposition = Object.assign([], migratedObject.composition); + clonedComposition.forEach((objIdentifier, index) => { + if (openmct.objects.makeKeyString(objIdentifier) === keyString) { + migratedObject.composition[index] = plotObject.identifier; + } }); + } + + items.push({ + width: panel.dimensions[0], + height: panel.dimensions[1], + x: panel.position[0], + y: panel.position[1], + identifier: identifier || childDomainObject.identifier, + id: uuid(), + type: 'subobject-view', + hasFrame: panel.hasFrame }); + }); - migratedObject.configuration.items = items; - migratedObject.configuration.layoutGrid = migratedObject.layoutGrid || DEFAULT_GRID_SIZE; - delete migratedObject.layoutGrid; - delete migratedObject.configuration.layout; + migratedObject.configuration.items = items; + migratedObject.configuration.layoutGrid = migratedObject.layoutGrid || DEFAULT_GRID_SIZE; + delete migratedObject.layoutGrid; + delete migratedObject.configuration.layout; - return migratedObject; - } + return migratedObject; + } - function migrateFixedPositionConfiguration(elements, telemetryObjects, gridSize) { - const DEFAULT_STROKE = 'transparent'; - const DEFAULT_SIZE = '13px'; - const DEFAULT_COLOR = ''; - const DEFAULT_FILL = ''; - let items = []; + function migrateFixedPositionConfiguration(elements, telemetryObjects, gridSize) { + const DEFAULT_STROKE = 'transparent'; + const DEFAULT_SIZE = '13px'; + const DEFAULT_COLOR = ''; + const DEFAULT_FILL = ''; + let items = []; - elements.forEach((element) => { - let item = { - x: element.x, - y: element.y, - width: element.width, - height: element.height, - id: uuid() - }; + elements.forEach((element) => { + let item = { + x: element.x, + y: element.y, + width: element.width, + height: element.height, + id: uuid() + }; - if (!element.useGrid) { - item.x = Math.round(item.x / gridSize[0]); - item.y = Math.round(item.y / gridSize[1]); - item.width = Math.round(item.width / gridSize[0]); - item.height = Math.round(item.height / gridSize[1]); - } + if (!element.useGrid) { + item.x = Math.round(item.x / gridSize[0]); + item.y = Math.round(item.y / gridSize[1]); + item.width = Math.round(item.width / gridSize[0]); + item.height = Math.round(item.height / gridSize[1]); + } - if (element.type === 'fixed.telemetry') { - item.type = 'telemetry-view'; - item.stroke = element.stroke || DEFAULT_STROKE; - item.fill = element.fill || DEFAULT_FILL; - item.color = element.color || DEFAULT_COLOR; - item.size = element.size || DEFAULT_SIZE; - item.identifier = telemetryObjects[element.id].identifier; - item.displayMode = element.titled ? 'all' : 'value'; - item.value = openmct.telemetry - .getMetadata(telemetryObjects[element.id]) - .getDefaultDisplayValue()?.key; - } else if (element.type === 'fixed.box') { - item.type = 'box-view'; - item.stroke = element.stroke || DEFAULT_STROKE; - item.fill = element.fill || DEFAULT_FILL; - } else if (element.type === 'fixed.line') { - item.type = 'line-view'; - item.x2 = element.x2; - item.y2 = element.y2; - item.stroke = element.stroke || DEFAULT_STROKE; - delete item.height; - delete item.width; - } else if (element.type === 'fixed.text') { - item.type = 'text-view'; - item.text = element.text; - item.stroke = element.stroke || DEFAULT_STROKE; - item.fill = element.fill || DEFAULT_FILL; - item.color = element.color || DEFAULT_COLOR; - item.size = element.size || DEFAULT_SIZE; - } else if (element.type === 'fixed.image') { - item.type = 'image-view'; - item.url = element.url; - item.stroke = element.stroke || DEFAULT_STROKE; - } + if (element.type === 'fixed.telemetry') { + item.type = 'telemetry-view'; + item.stroke = element.stroke || DEFAULT_STROKE; + item.fill = element.fill || DEFAULT_FILL; + item.color = element.color || DEFAULT_COLOR; + item.size = element.size || DEFAULT_SIZE; + item.identifier = telemetryObjects[element.id].identifier; + item.displayMode = element.titled ? 'all' : 'value'; + item.value = openmct.telemetry + .getMetadata(telemetryObjects[element.id]) + .getDefaultDisplayValue()?.key; + } else if (element.type === 'fixed.box') { + item.type = 'box-view'; + item.stroke = element.stroke || DEFAULT_STROKE; + item.fill = element.fill || DEFAULT_FILL; + } else if (element.type === 'fixed.line') { + item.type = 'line-view'; + item.x2 = element.x2; + item.y2 = element.y2; + item.stroke = element.stroke || DEFAULT_STROKE; + delete item.height; + delete item.width; + } else if (element.type === 'fixed.text') { + item.type = 'text-view'; + item.text = element.text; + item.stroke = element.stroke || DEFAULT_STROKE; + item.fill = element.fill || DEFAULT_FILL; + item.color = element.color || DEFAULT_COLOR; + item.size = element.size || DEFAULT_SIZE; + } else if (element.type === 'fixed.image') { + item.type = 'image-view'; + item.url = element.url; + item.stroke = element.stroke || DEFAULT_STROKE; + } - items.push(item); - }); + items.push(item); + }); - return items; - } + return items; + } - return [ - { - check(domainObject) { - return ( - domainObject.type === 'layout' && - domainObject.configuration && - domainObject.configuration.layout - ); - }, - migrate(domainObject) { - let childObjects = {}; - let promises = Object.keys(domainObject.configuration.layout.panels).map((key) => { - return openmct.objects.get(key).then((object) => { - childObjects[key] = object; - }); + return [ + { + check(domainObject) { + return ( + domainObject.type === 'layout' && + domainObject.configuration && + domainObject.configuration.layout + ); + }, + migrate(domainObject) { + let childObjects = {}; + let promises = Object.keys(domainObject.configuration.layout.panels).map((key) => { + return openmct.objects.get(key).then((object) => { + childObjects[key] = object; }); + }); - return Promise.all(promises).then(function () { - return migrateDisplayLayout(domainObject, childObjects); - }); - } + return Promise.all(promises).then(function () { + return migrateDisplayLayout(domainObject, childObjects); + }); + } + }, + { + check(domainObject) { + return ( + domainObject.type === 'telemetry.fixed' && + domainObject.configuration && + domainObject.configuration['fixed-display'] + ); }, - { - check(domainObject) { - return ( - domainObject.type === 'telemetry.fixed' && - domainObject.configuration && - domainObject.configuration['fixed-display'] - ); - }, - migrate(domainObject) { - const DEFAULT_GRID_SIZE = [64, 16]; - let newLayoutObject = { - identifier: domainObject.identifier, - location: domainObject.location, - name: domainObject.name, - type: 'layout' - }; - let gridSize = domainObject.layoutGrid || DEFAULT_GRID_SIZE; - let layoutType = openmct.types.get('layout'); - layoutType.definition.initialize(newLayoutObject); - newLayoutObject.composition = domainObject.composition; - newLayoutObject.configuration.layoutGrid = gridSize; + migrate(domainObject) { + const DEFAULT_GRID_SIZE = [64, 16]; + let newLayoutObject = { + identifier: domainObject.identifier, + location: domainObject.location, + name: domainObject.name, + type: 'layout' + }; + let gridSize = domainObject.layoutGrid || DEFAULT_GRID_SIZE; + let layoutType = openmct.types.get('layout'); + layoutType.definition.initialize(newLayoutObject); + newLayoutObject.composition = domainObject.composition; + newLayoutObject.configuration.layoutGrid = gridSize; - let elements = domainObject.configuration['fixed-display'].elements; - let telemetryObjects = {}; - let promises = elements.map((element) => { - if (element.id) { - return openmct.objects.get(element.id).then((object) => { - telemetryObjects[element.id] = object; - }); - } else { - return Promise.resolve(false); - } - }); + let elements = domainObject.configuration['fixed-display'].elements; + let telemetryObjects = {}; + let promises = elements.map((element) => { + if (element.id) { + return openmct.objects.get(element.id).then((object) => { + telemetryObjects[element.id] = object; + }); + } else { + return Promise.resolve(false); + } + }); - return Promise.all(promises).then(function () { - newLayoutObject.configuration.items = migrateFixedPositionConfiguration( - elements, - telemetryObjects, - gridSize - ); + return Promise.all(promises).then(function () { + newLayoutObject.configuration.items = migrateFixedPositionConfiguration( + elements, + telemetryObjects, + gridSize + ); - return newLayoutObject; - }); - } + return newLayoutObject; + }); + } + }, + { + check(domainObject) { + return ( + domainObject.type === 'table' && + domainObject.configuration && + domainObject.configuration.table + ); }, - { - check(domainObject) { - return ( - domainObject.type === 'table' && - domainObject.configuration && - domainObject.configuration.table - ); - }, - migrate(domainObject) { - let currentTableConfiguration = domainObject.configuration.table || {}; - let currentColumnConfiguration = currentTableConfiguration.columns || {}; + migrate(domainObject) { + let currentTableConfiguration = domainObject.configuration.table || {}; + let currentColumnConfiguration = currentTableConfiguration.columns || {}; - return getColumnNameKeyMap(domainObject).then((nameKeyMap) => { - let hiddenColumns = Object.keys(currentColumnConfiguration) - .filter((columnName) => { - return currentColumnConfiguration[columnName] === false; - }) - .reduce((hiddenColumnsMap, hiddenColumnName) => { - let key = nameKeyMap[hiddenColumnName]; - hiddenColumnsMap[key] = true; + return getColumnNameKeyMap(domainObject).then((nameKeyMap) => { + let hiddenColumns = Object.keys(currentColumnConfiguration) + .filter((columnName) => { + return currentColumnConfiguration[columnName] === false; + }) + .reduce((hiddenColumnsMap, hiddenColumnName) => { + let key = nameKeyMap[hiddenColumnName]; + hiddenColumnsMap[key] = true; - return hiddenColumnsMap; - }, {}); + return hiddenColumnsMap; + }, {}); - domainObject.configuration.hiddenColumns = hiddenColumns; - delete domainObject.configuration.table; + domainObject.configuration.hiddenColumns = hiddenColumns; + delete domainObject.configuration.table; - return domainObject; - }); - } + return domainObject; + }); } - ]; - }; -}); + } + ]; +} diff --git a/src/plugins/plugins.js b/src/plugins/plugins.js index af255090d93..d10032d770a 100644 --- a/src/plugins/plugins.js +++ b/src/plugins/plugins.js @@ -20,218 +20,151 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define([ - 'lodash', - './utcTimeSystem/plugin', - './remoteClock/plugin', - './localTimeSystem/plugin', - './ISOTimeFormat/plugin', - './myItems/plugin', - '../../example/generator/plugin', - '../../example/eventGenerator/plugin', - './autoflow/AutoflowTabularPlugin', - './timeConductor/plugin', - '../../example/imagery/plugin', - '../../example/faultManagement/exampleFaultSource', - './imagery/plugin', - './summaryWidget/plugin', - './URLIndicatorPlugin/URLIndicatorPlugin', - './telemetryMean/plugin', - './plot/plugin', - './charts/bar/plugin', - './charts/scatter/plugin', - './telemetryTable/plugin', - './staticRootPlugin/plugin', - './notebook/plugin', - './displayLayout/plugin', - './formActions/plugin', - './folderView/plugin', - './flexibleLayout/plugin', - './tabs/plugin', - './LADTable/plugin', - './filters/plugin', - './objectMigration/plugin', - './goToOriginalAction/plugin', - './openInNewTabAction/plugin', - './clearData/plugin', - './webPage/plugin', - './condition/plugin', - './conditionWidget/plugin', - './themes/espresso', - './themes/snow', - './URLTimeSettingsSynchronizer/plugin', - './notificationIndicator/plugin', - './newFolderAction/plugin', - './persistence/couch/plugin', - './defaultRootName/plugin', - './plan/plugin', - './viewDatumAction/plugin', - './viewLargeAction/plugin', - './interceptors/plugin', - './performanceIndicator/plugin', - './CouchDBSearchFolder/plugin', - './timeline/plugin', - './hyperlink/plugin', - './clock/plugin', - './DeviceClassifier/plugin', - './timer/plugin', - './userIndicator/plugin', - '../../example/exampleUser/plugin', - './localStorage/plugin', - './operatorStatus/plugin', - './gauge/GaugePlugin', - './timelist/plugin', - './faultManagement/FaultManagementPlugin', - '../../example/exampleTags/plugin', - './inspectorViews/plugin' -], function ( - _, - UTCTimeSystem, - RemoteClock, - LocalTimeSystem, - ISOTimeFormat, - MyItems, - GeneratorPlugin, - EventGeneratorPlugin, - AutoflowPlugin, - TimeConductorPlugin, - ExampleImagery, - ExampleFaultSource, - ImageryPlugin, - SummaryWidget, - URLIndicatorPlugin, - TelemetryMean, - PlotPlugin, - BarChartPlugin, - ScatterPlotPlugin, - TelemetryTablePlugin, - StaticRootPlugin, - Notebook, - DisplayLayoutPlugin, - FormActions, - FolderView, - FlexibleLayout, - Tabs, - LADTable, - Filters, - ObjectMigration, - GoToOriginalAction, - OpenInNewTabAction, - ClearData, - WebPagePlugin, - ConditionPlugin, - ConditionWidgetPlugin, - Espresso, - Snow, - URLTimeSettingsSynchronizer, - NotificationIndicator, - NewFolderAction, - CouchDBPlugin, - DefaultRootName, - PlanLayout, - ViewDatumAction, - ViewLargeAction, - ObjectInterceptors, - PerformanceIndicator, - CouchDBSearchFolder, - Timeline, - Hyperlink, - Clock, - DeviceClassifier, - Timer, - UserIndicator, - ExampleUser, - LocalStorage, - OperatorStatus, - GaugePlugin, - TimeList, - FaultManagementPlugin, - ExampleTags, - InspectorViews -) { - const plugins = {}; +import EventGeneratorPlugin from '../../example/eventGenerator/plugin'; +import ExampleTags from '../../example/exampleTags/plugin'; +import ExampleUser from '../../example/exampleUser/plugin'; +import ExampleFaultSource from '../../example/faultManagement/exampleFaultSource'; +import GeneratorPlugin from '../../example/generator/plugin'; +import ExampleImagery from '../../example/imagery/plugin'; +import AutoflowPlugin from './autoflow/AutoflowTabularPlugin'; +import BarChartPlugin from './charts/bar/plugin'; +import ScatterPlotPlugin from './charts/scatter/plugin'; +import ClearData from './clearData/plugin'; +import Clock from './clock/plugin'; +import ConditionPlugin from './condition/plugin'; +import ConditionWidgetPlugin from './conditionWidget/plugin'; +import CouchDBSearchFolder from './CouchDBSearchFolder/plugin'; +import DefaultRootName from './defaultRootName/plugin'; +import DeviceClassifier from './DeviceClassifier/plugin'; +import DisplayLayoutPlugin from './displayLayout/plugin'; +import FaultManagementPlugin from './faultManagement/FaultManagementPlugin'; +import Filters from './filters/plugin'; +import FlexibleLayout from './flexibleLayout/plugin'; +import FolderView from './folderView/plugin'; +import FormActions from './formActions/plugin'; +import GaugePlugin from './gauge/GaugePlugin'; +import GoToOriginalAction from './goToOriginalAction/plugin'; +import Hyperlink from './hyperlink/plugin'; +import ImageryPlugin from './imagery/plugin'; +import InspectorViews from './inspectorViews/plugin'; +import ObjectInterceptors from './interceptors/plugin'; +import ISOTimeFormat from './ISOTimeFormat/plugin'; +import LADTable from './LADTable/plugin'; +import LocalStorage from './localStorage/plugin'; +import LocalTimeSystem from './localTimeSystem/plugin'; +import MyItems from './myItems/plugin'; +import NewFolderAction from './newFolderAction/plugin'; +import * as Notebook from './notebook/plugin'; +import NotificationIndicator from './notificationIndicator/plugin'; +import ObjectMigration from './objectMigration/plugin'; +import OpenInNewTabAction from './openInNewTabAction/plugin'; +import OperatorStatus from './operatorStatus/plugin'; +import PerformanceIndicator from './performanceIndicator/plugin'; +import CouchDBPlugin from './persistence/couch/plugin'; +import PlanLayout from './plan/plugin'; +import PlotPlugin from './plot/plugin'; +import RemoteClock from './remoteClock/plugin'; +import StaticRootPlugin from './staticRootPlugin/plugin'; +import SummaryWidget from './summaryWidget/plugin'; +import Tabs from './tabs/plugin'; +import TelemetryMean from './telemetryMean/plugin'; +import TelemetryTablePlugin from './telemetryTable/plugin'; +import Espresso from './themes/espresso'; +import Snow from './themes/snow'; +import TimeConductorPlugin from './timeConductor/plugin'; +import Timeline from './timeline/plugin'; +import TimeList from './timelist/plugin'; +import Timer from './timer/plugin'; +import URLIndicatorPlugin from './URLIndicatorPlugin/URLIndicatorPlugin'; +import URLTimeSettingsSynchronizer from './URLTimeSettingsSynchronizer/plugin'; +import UserIndicator from './userIndicator/plugin'; +import UTCTimeSystem from './utcTimeSystem/plugin'; +import ViewDatumAction from './viewDatumAction/plugin'; +import ViewLargeAction from './viewLargeAction/plugin'; +import WebPagePlugin from './webPage/plugin'; - plugins.example = {}; - plugins.example.ExampleUser = ExampleUser.default; - plugins.example.ExampleImagery = ExampleImagery.default; - plugins.example.ExampleFaultSource = ExampleFaultSource.default; - plugins.example.EventGeneratorPlugin = EventGeneratorPlugin.default; - plugins.example.ExampleTags = ExampleTags.default; - plugins.example.Generator = () => GeneratorPlugin.default; +const plugins = {}; - plugins.UTCTimeSystem = UTCTimeSystem.default; - plugins.LocalTimeSystem = LocalTimeSystem; - plugins.RemoteClock = RemoteClock.default; +plugins.example = {}; +plugins.example.ExampleUser = ExampleUser; +plugins.example.ExampleImagery = ExampleImagery; +plugins.example.ExampleFaultSource = ExampleFaultSource; +plugins.example.EventGeneratorPlugin = EventGeneratorPlugin; +plugins.example.ExampleTags = ExampleTags; +plugins.example.Generator = () => GeneratorPlugin; - plugins.MyItems = MyItems.default; +plugins.UTCTimeSystem = UTCTimeSystem; +plugins.LocalTimeSystem = LocalTimeSystem; +plugins.RemoteClock = RemoteClock; - plugins.StaticRootPlugin = StaticRootPlugin.default; +plugins.MyItems = MyItems; - /** - * A tabular view showing the latest values of multiple telemetry points at - * once. Formatted so that labels and values are aligned. - * - * @param {Object} [options] Optional settings to apply to the autoflow - * tabular view. Currently supports one option, 'type'. - * @param {string} [options.type] The key of an object type to apply this view - * to exclusively. - */ - plugins.AutoflowView = AutoflowPlugin; +plugins.StaticRootPlugin = StaticRootPlugin; - plugins.Conductor = TimeConductorPlugin.default; +/** + * A tabular view showing the latest values of multiple telemetry points at + * once. Formatted so that labels and values are aligned. + * + * @param {Object} [options] Optional settings to apply to the autoflow + * tabular view. Currently supports one option, 'type'. + * @param {string} [options.type] The key of an object type to apply this view + * to exclusively. + */ +plugins.AutoflowView = AutoflowPlugin; + +plugins.Conductor = TimeConductorPlugin; - plugins.CouchDB = CouchDBPlugin.default; +plugins.CouchDB = CouchDBPlugin; - plugins.ImageryPlugin = ImageryPlugin; - plugins.Plot = PlotPlugin.default; - plugins.BarChart = BarChartPlugin.default; - plugins.ScatterPlot = ScatterPlotPlugin.default; - plugins.TelemetryTable = TelemetryTablePlugin; +plugins.ImageryPlugin = ImageryPlugin; +plugins.Plot = PlotPlugin; +plugins.BarChart = BarChartPlugin; +plugins.ScatterPlot = ScatterPlotPlugin; +plugins.TelemetryTable = TelemetryTablePlugin; - plugins.SummaryWidget = SummaryWidget; - plugins.TelemetryMean = TelemetryMean; - plugins.URLIndicator = URLIndicatorPlugin; - plugins.Notebook = Notebook.NotebookPlugin; - plugins.RestrictedNotebook = Notebook.RestrictedNotebookPlugin; - plugins.DisplayLayout = DisplayLayoutPlugin.default; - plugins.FaultManagement = FaultManagementPlugin.default; - plugins.FormActions = FormActions; - plugins.FolderView = FolderView.default; - plugins.Tabs = Tabs; - plugins.FlexibleLayout = FlexibleLayout; - plugins.LADTable = LADTable.default; - plugins.Filters = Filters; - plugins.ObjectMigration = ObjectMigration.default; - plugins.GoToOriginalAction = GoToOriginalAction.default; - plugins.OpenInNewTabAction = OpenInNewTabAction.default; - plugins.ClearData = ClearData.default; - plugins.WebPage = WebPagePlugin.default; - plugins.Espresso = Espresso.default; - plugins.Snow = Snow.default; - plugins.Condition = ConditionPlugin.default; - plugins.ConditionWidget = ConditionWidgetPlugin.default; - plugins.URLTimeSettingsSynchronizer = URLTimeSettingsSynchronizer.default; - plugins.NotificationIndicator = NotificationIndicator.default; - plugins.NewFolderAction = NewFolderAction.default; - plugins.ISOTimeFormat = ISOTimeFormat.default; - plugins.DefaultRootName = DefaultRootName.default; - plugins.PlanLayout = PlanLayout.default; - plugins.ViewDatumAction = ViewDatumAction.default; - plugins.ViewLargeAction = ViewLargeAction.default; - plugins.ObjectInterceptors = ObjectInterceptors.default; - plugins.PerformanceIndicator = PerformanceIndicator.default; - plugins.CouchDBSearchFolder = CouchDBSearchFolder.default; - plugins.Timeline = Timeline.default; - plugins.Hyperlink = Hyperlink.default; - plugins.Clock = Clock.default; - plugins.Timer = Timer.default; - plugins.DeviceClassifier = DeviceClassifier.default; - plugins.UserIndicator = UserIndicator.default; - plugins.LocalStorage = LocalStorage.default; - plugins.OperatorStatus = OperatorStatus.default; - plugins.Gauge = GaugePlugin.default; - plugins.Timelist = TimeList.default; - plugins.InspectorViews = InspectorViews.default; +plugins.SummaryWidget = SummaryWidget; +plugins.TelemetryMean = TelemetryMean; +plugins.URLIndicator = URLIndicatorPlugin; +plugins.Notebook = Notebook.NotebookPlugin; +plugins.RestrictedNotebook = Notebook.RestrictedNotebookPlugin; +plugins.DisplayLayout = DisplayLayoutPlugin; +plugins.FaultManagement = FaultManagementPlugin; +plugins.FormActions = FormActions; +plugins.FolderView = FolderView; +plugins.Tabs = Tabs; +plugins.FlexibleLayout = FlexibleLayout; +plugins.LADTable = LADTable; +plugins.Filters = Filters; +plugins.ObjectMigration = ObjectMigration; +plugins.GoToOriginalAction = GoToOriginalAction; +plugins.OpenInNewTabAction = OpenInNewTabAction; +plugins.ClearData = ClearData; +plugins.WebPage = WebPagePlugin; +plugins.Espresso = Espresso; +plugins.Snow = Snow; +plugins.Condition = ConditionPlugin; +plugins.ConditionWidget = ConditionWidgetPlugin; +plugins.URLTimeSettingsSynchronizer = URLTimeSettingsSynchronizer; +plugins.NotificationIndicator = NotificationIndicator; +plugins.NewFolderAction = NewFolderAction; +plugins.ISOTimeFormat = ISOTimeFormat; +plugins.DefaultRootName = DefaultRootName; +plugins.PlanLayout = PlanLayout; +plugins.ViewDatumAction = ViewDatumAction; +plugins.ViewLargeAction = ViewLargeAction; +plugins.ObjectInterceptors = ObjectInterceptors; +plugins.PerformanceIndicator = PerformanceIndicator; +plugins.CouchDBSearchFolder = CouchDBSearchFolder; +plugins.Timeline = Timeline; +plugins.Hyperlink = Hyperlink; +plugins.Clock = Clock; +plugins.Timer = Timer; +plugins.DeviceClassifier = DeviceClassifier; +plugins.UserIndicator = UserIndicator; +plugins.LocalStorage = LocalStorage; +plugins.OperatorStatus = OperatorStatus; +plugins.Gauge = GaugePlugin; +plugins.Timelist = TimeList; +plugins.InspectorViews = InspectorViews; - return plugins; -}); +export default plugins; diff --git a/src/plugins/summaryWidget/SummaryWidgetViewPolicy.js b/src/plugins/summaryWidget/SummaryWidgetViewPolicy.js index fcca9d21e0b..ab301d09c18 100644 --- a/src/plugins/summaryWidget/SummaryWidgetViewPolicy.js +++ b/src/plugins/summaryWidget/SummaryWidgetViewPolicy.js @@ -20,20 +20,16 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define([], function () { - /** - * Policy determining which views can apply to summary widget. Disables - * any view other than normal summary widget view. - */ - function SummaryWidgetViewPolicy() {} +/** + * Policy determining which views can apply to summary widget. Disables + * any view other than normal summary widget view. + */ +export default function SummaryWidgetViewPolicy() {} - SummaryWidgetViewPolicy.prototype.allow = function (view, domainObject) { - if (domainObject.getModel().type === 'summary-widget') { - return view.key === 'summary-widget-viewer'; - } +SummaryWidgetViewPolicy.prototype.allow = function (view, domainObject) { + if (domainObject.getModel().type === 'summary-widget') { + return view.key === 'summary-widget-viewer'; + } - return true; - }; - - return SummaryWidgetViewPolicy; -}); + return true; +}; diff --git a/src/plugins/summaryWidget/SummaryWidgetsCompositionPolicy.js b/src/plugins/summaryWidget/SummaryWidgetsCompositionPolicy.js index 30342cb9fc7..80c32c8a34c 100644 --- a/src/plugins/summaryWidget/SummaryWidgetsCompositionPolicy.js +++ b/src/plugins/summaryWidget/SummaryWidgetsCompositionPolicy.js @@ -20,20 +20,16 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define([], function () { - function SummaryWidgetsCompositionPolicy(openmct) { - this.openmct = openmct; - } - - SummaryWidgetsCompositionPolicy.prototype.allow = function (parent, child) { - const parentType = parent.type; +export default function SummaryWidgetsCompositionPolicy(openmct) { + this.openmct = openmct; +} - if (parentType === 'summary-widget' && !this.openmct.telemetry.isTelemetryObject(child)) { - return false; - } +SummaryWidgetsCompositionPolicy.prototype.allow = function (parent, child) { + const parentType = parent.type; - return true; - }; + if (parentType === 'summary-widget' && !this.openmct.telemetry.isTelemetryObject(child)) { + return false; + } - return SummaryWidgetsCompositionPolicy; -}); + return true; +}; diff --git a/src/plugins/summaryWidget/plugin.js b/src/plugins/summaryWidget/plugin.js index e14993ad572..016cfad918e 100755 --- a/src/plugins/summaryWidget/plugin.js +++ b/src/plugins/summaryWidget/plugin.js @@ -1,98 +1,87 @@ -define([ - './SummaryWidgetsCompositionPolicy', - './src/telemetry/SummaryWidgetMetadataProvider', - './src/telemetry/SummaryWidgetTelemetryProvider', - './src/views/SummaryWidgetViewProvider', - './SummaryWidgetViewPolicy' -], function ( - SummaryWidgetsCompositionPolicy, - SummaryWidgetMetadataProvider, - SummaryWidgetTelemetryProvider, - SummaryWidgetViewProvider, - SummaryWidgetViewPolicy -) { - function plugin() { - const widgetType = { - name: 'Summary Widget', - description: 'A compact status update for collections of telemetry-producing items', - cssClass: 'icon-summary-widget', - initialize: function (domainObject) { - domainObject.composition = []; - domainObject.configuration = { - ruleOrder: ['default'], - ruleConfigById: { - default: { - name: 'Default', - label: 'Unnamed Rule', - message: '', - id: 'default', - icon: ' ', - style: { - color: '#ffffff', - 'background-color': '#38761d', - 'border-color': 'rgba(0,0,0,0)' - }, - description: 'Default appearance for the widget', - conditions: [ - { - object: '', - key: '', - operation: '', - values: [] - } - ], - jsCondition: '', - trigger: 'any', - expanded: 'true' - } - }, - testDataConfig: [ - { - object: '', - key: '', - value: '' - } - ] - }; - domainObject.openNewTab = 'thisTab'; - domainObject.telemetry = {}; - }, - form: [ - { - key: 'url', - name: 'URL', - control: 'textfield', - required: false, - cssClass: 'l-input-lg' - }, - { - key: 'openNewTab', - name: 'Tab to Open Hyperlink', - control: 'select', - options: [ - { - value: 'thisTab', - name: 'Open in this tab' - }, - { - value: 'newTab', - name: 'Open in a new tab' - } - ], - cssClass: 'l-inline' - } - ] - }; +import SummaryWidgetMetadataProvider from './src/telemetry/SummaryWidgetMetadataProvider'; +import SummaryWidgetTelemetryProvider from './src/telemetry/SummaryWidgetTelemetryProvider'; +import SummaryWidgetViewProvider from './src/views/SummaryWidgetViewProvider'; +import SummaryWidgetsCompositionPolicy from './SummaryWidgetsCompositionPolicy'; - return function install(openmct) { - openmct.types.addType('summary-widget', widgetType); - let compositionPolicy = new SummaryWidgetsCompositionPolicy(openmct); - openmct.composition.addPolicy(compositionPolicy.allow.bind(compositionPolicy)); - openmct.telemetry.addProvider(new SummaryWidgetMetadataProvider(openmct)); - openmct.telemetry.addProvider(new SummaryWidgetTelemetryProvider(openmct)); - openmct.objectViews.addProvider(new SummaryWidgetViewProvider(openmct)); - }; - } +export default function plugin() { + const widgetType = { + name: 'Summary Widget', + description: 'A compact status update for collections of telemetry-producing items', + cssClass: 'icon-summary-widget', + initialize: function (domainObject) { + domainObject.composition = []; + domainObject.configuration = { + ruleOrder: ['default'], + ruleConfigById: { + default: { + name: 'Default', + label: 'Unnamed Rule', + message: '', + id: 'default', + icon: ' ', + style: { + color: '#ffffff', + 'background-color': '#38761d', + 'border-color': 'rgba(0,0,0,0)' + }, + description: 'Default appearance for the widget', + conditions: [ + { + object: '', + key: '', + operation: '', + values: [] + } + ], + jsCondition: '', + trigger: 'any', + expanded: 'true' + } + }, + testDataConfig: [ + { + object: '', + key: '', + value: '' + } + ] + }; + domainObject.openNewTab = 'thisTab'; + domainObject.telemetry = {}; + }, + form: [ + { + key: 'url', + name: 'URL', + control: 'textfield', + required: false, + cssClass: 'l-input-lg' + }, + { + key: 'openNewTab', + name: 'Tab to Open Hyperlink', + control: 'select', + options: [ + { + value: 'thisTab', + name: 'Open in this tab' + }, + { + value: 'newTab', + name: 'Open in a new tab' + } + ], + cssClass: 'l-inline' + } + ] + }; - return plugin; -}); + return function install(openmct) { + openmct.types.addType('summary-widget', widgetType); + let compositionPolicy = new SummaryWidgetsCompositionPolicy(openmct); + openmct.composition.addPolicy(compositionPolicy.allow.bind(compositionPolicy)); + openmct.telemetry.addProvider(new SummaryWidgetMetadataProvider(openmct)); + openmct.telemetry.addProvider(new SummaryWidgetTelemetryProvider(openmct)); + openmct.objectViews.addProvider(new SummaryWidgetViewProvider(openmct)); + }; +} diff --git a/src/plugins/summaryWidget/src/Condition.js b/src/plugins/summaryWidget/src/Condition.js index da200ee3f07..9805e7217cc 100644 --- a/src/plugins/summaryWidget/src/Condition.js +++ b/src/plugins/summaryWidget/src/Condition.js @@ -1,237 +1,226 @@ -define([ - '../res/conditionTemplate.html', - './input/ObjectSelect', - './input/KeySelect', - './input/OperationSelect', - './eventHelpers', - '../../../utils/template/templateHelpers', - 'EventEmitter' -], function ( - conditionTemplate, - ObjectSelect, - KeySelect, - OperationSelect, - eventHelpers, - templateHelpers, - EventEmitter -) { - /** - * Represents an individual condition for a summary widget rule. Manages the - * associated inputs and view. - * @param {Object} conditionConfig The configuration for this condition, consisting - * of object, key, operation, and values fields - * @param {number} index the index of this Condition object in it's parent Rule's data model, - * to be injected into callbacks for removes - * @param {ConditionManager} conditionManager A ConditionManager instance for populating - * selects with configuration data - */ - function Condition(conditionConfig, index, conditionManager) { - eventHelpers.extend(this); - this.config = conditionConfig; - this.index = index; - this.conditionManager = conditionManager; - - this.domElement = templateHelpers.convertTemplateToHTML(conditionTemplate)[0]; - - this.eventEmitter = new EventEmitter(); - this.supportedCallbacks = ['remove', 'duplicate', 'change']; - - this.deleteButton = this.domElement.querySelector('.t-delete'); - this.duplicateButton = this.domElement.querySelector('.t-duplicate'); - - this.selects = {}; - this.valueInputs = []; - - this.remove = this.remove.bind(this); - this.duplicate = this.duplicate.bind(this); - - const self = this; - - /** - * Event handler for a change in one of this conditions' custom selects - * @param {string} value The new value of this selects - * @param {string} property The property of this condition to modify - * @private - */ - function onSelectChange(value, property) { - if (property === 'operation') { - self.generateValueInputs(value); - } - - self.eventEmitter.emit('change', { - value: value, - property: property, - index: self.index - }); - } - - /** - * Event handler for this conditions value inputs - * @param {Event} event The oninput event that triggered this callback - * @private - */ - function onValueInput(event) { - const elem = event.target; - const value = isNaN(Number(elem.value)) ? elem.value : Number(elem.value); - const inputIndex = self.valueInputs.indexOf(elem); - - self.eventEmitter.emit('change', { - value: value, - property: 'values[' + inputIndex + ']', - index: self.index - }); - } - - this.listenTo(this.deleteButton, 'click', this.remove, this); - this.listenTo(this.duplicateButton, 'click', this.duplicate, this); - - this.selects.object = new ObjectSelect(this.config, this.conditionManager, [ - ['any', 'any telemetry'], - ['all', 'all telemetry'] - ]); - this.selects.key = new KeySelect(this.config, this.selects.object, this.conditionManager); - this.selects.operation = new OperationSelect( - this.config, - this.selects.key, - this.conditionManager, - function (value) { - onSelectChange(value, 'operation'); - } - ); - - this.selects.object.on('change', function (value) { - onSelectChange(value, 'object'); - }); - this.selects.key.on('change', function (value) { - onSelectChange(value, 'key'); - }); - - Object.values(this.selects).forEach(function (select) { - self.domElement.querySelector('.t-configuration').append(select.getDOM()); - }); - - this.listenTo(this.domElement.querySelector('.t-value-inputs'), 'input', onValueInput); - } - - Condition.prototype.getDOM = function (container) { - return this.domElement; - }; +import EventEmitter from 'EventEmitter'; + +import * as templateHelpers from '../../../utils/template/templateHelpers'; +import conditionTemplate from '../res/conditionTemplate.html'; +import eventHelpers from './eventHelpers'; +import KeySelect from './input/KeySelect'; +import ObjectSelect from './input/ObjectSelect'; +import OperationSelect from './input/OperationSelect'; + +/** + * Represents an individual condition for a summary widget rule. Manages the + * associated inputs and view. + * @param {Object} conditionConfig The configuration for this condition, consisting + * of object, key, operation, and values fields + * @param {number} index the index of this Condition object in it's parent Rule's data model, + * to be injected into callbacks for removes + * @param {ConditionManager} conditionManager A ConditionManager instance for populating + * selects with configuration data + */ +export default function Condition(conditionConfig, index, conditionManager) { + eventHelpers.extend(this); + this.config = conditionConfig; + this.index = index; + this.conditionManager = conditionManager; + + this.domElement = templateHelpers.convertTemplateToHTML(conditionTemplate)[0]; + + this.eventEmitter = new EventEmitter(); + this.supportedCallbacks = ['remove', 'duplicate', 'change']; + + this.deleteButton = this.domElement.querySelector('.t-delete'); + this.duplicateButton = this.domElement.querySelector('.t-duplicate'); + + this.selects = {}; + this.valueInputs = []; + + this.remove = this.remove.bind(this); + this.duplicate = this.duplicate.bind(this); + + const self = this; /** - * Register a callback with this condition: supported callbacks are remove, change, - * duplicate - * @param {string} event The key for the event to listen to - * @param {function} callback The function that this rule will invoke on this event - * @param {Object} context A reference to a scope to use as the context for - * context for the callback function + * Event handler for a change in one of this conditions' custom selects + * @param {string} value The new value of this selects + * @param {string} property The property of this condition to modify + * @private */ - Condition.prototype.on = function (event, callback, context) { - if (this.supportedCallbacks.includes(event)) { - this.eventEmitter.on(event, callback, context || this); + function onSelectChange(value, property) { + if (property === 'operation') { + self.generateValueInputs(value); } - }; - /** - * Hide the appropriate inputs when this is the only condition - */ - Condition.prototype.hideButtons = function () { - this.deleteButton.style.display = 'none'; - }; - - /** - * Remove this condition from the configuration. Invokes any registered - * remove callbacks - */ - Condition.prototype.remove = function () { - this.eventEmitter.emit('remove', this.index); - this.destroy(); - }; - - Condition.prototype.destroy = function () { - this.stopListening(); - Object.values(this.selects).forEach(function (select) { - select.destroy(); + self.eventEmitter.emit('change', { + value: value, + property: property, + index: self.index }); - }; + } /** - * Make a deep clone of this condition's configuration and invoke any duplicate - * callbacks with the cloned configuration and this rule's index + * Event handler for this conditions value inputs + * @param {Event} event The oninput event that triggered this callback + * @private */ - Condition.prototype.duplicate = function () { - const sourceCondition = JSON.parse(JSON.stringify(this.config)); - this.eventEmitter.emit('duplicate', { - sourceCondition: sourceCondition, - index: this.index + function onValueInput(event) { + const elem = event.target; + const value = isNaN(Number(elem.value)) ? elem.value : Number(elem.value); + const inputIndex = self.valueInputs.indexOf(elem); + + self.eventEmitter.emit('change', { + value: value, + property: 'values[' + inputIndex + ']', + index: self.index }); - }; + } - /** - * When an operation is selected, create the appropriate value inputs - * and add them to the view. If an operation is of type enum, create - * a drop-down menu instead. - * - * @param {string} operation The key of currently selected operation - */ - Condition.prototype.generateValueInputs = function (operation) { - const evaluator = this.conditionManager.getEvaluator(); - const inputArea = this.domElement.querySelector('.t-value-inputs'); - let inputCount; - let inputType; - let newInput; - let index = 0; - let emitChange = false; - - inputArea.innerHTML = ''; - this.valueInputs = []; - this.config.values = this.config.values || []; - - if (evaluator.getInputCount(operation)) { - inputCount = evaluator.getInputCount(operation); - inputType = evaluator.getInputType(operation); - - while (index < inputCount) { - if (inputType === 'select') { - const options = this.generateSelectOptions(); - - newInput = document.createElement('select'); - newInput.innerHTML = options; - - emitChange = true; - } else { - const defaultValue = inputType === 'number' ? 0 : ''; - const value = this.config.values[index] || defaultValue; - this.config.values[index] = value; - - newInput = document.createElement('input'); - newInput.type = `${inputType}`; - newInput.value = `${value}`; - } - - this.valueInputs.push(newInput); - inputArea.appendChild(newInput); - index += 1; + this.listenTo(this.deleteButton, 'click', this.remove, this); + this.listenTo(this.duplicateButton, 'click', this.duplicate, this); + + this.selects.object = new ObjectSelect(this.config, this.conditionManager, [ + ['any', 'any telemetry'], + ['all', 'all telemetry'] + ]); + this.selects.key = new KeySelect(this.config, this.selects.object, this.conditionManager); + this.selects.operation = new OperationSelect( + this.config, + this.selects.key, + this.conditionManager, + function (value) { + onSelectChange(value, 'operation'); + } + ); + + this.selects.object.on('change', function (value) { + onSelectChange(value, 'object'); + }); + this.selects.key.on('change', function (value) { + onSelectChange(value, 'key'); + }); + + Object.values(this.selects).forEach(function (select) { + self.domElement.querySelector('.t-configuration').append(select.getDOM()); + }); + + this.listenTo(this.domElement.querySelector('.t-value-inputs'), 'input', onValueInput); +} + +Condition.prototype.getDOM = function (container) { + return this.domElement; +}; + +/** + * Register a callback with this condition: supported callbacks are remove, change, + * duplicate + * @param {string} event The key for the event to listen to + * @param {function} callback The function that this rule will invoke on this event + * @param {Object} context A reference to a scope to use as the context for + * context for the callback function + */ +Condition.prototype.on = function (event, callback, context) { + if (this.supportedCallbacks.includes(event)) { + this.eventEmitter.on(event, callback, context || this); + } +}; + +/** + * Hide the appropriate inputs when this is the only condition + */ +Condition.prototype.hideButtons = function () { + this.deleteButton.style.display = 'none'; +}; + +/** + * Remove this condition from the configuration. Invokes any registered + * remove callbacks + */ +Condition.prototype.remove = function () { + this.eventEmitter.emit('remove', this.index); + this.destroy(); +}; + +Condition.prototype.destroy = function () { + this.stopListening(); + Object.values(this.selects).forEach(function (select) { + select.destroy(); + }); +}; + +/** + * Make a deep clone of this condition's configuration and invoke any duplicate + * callbacks with the cloned configuration and this rule's index + */ +Condition.prototype.duplicate = function () { + const sourceCondition = JSON.parse(JSON.stringify(this.config)); + this.eventEmitter.emit('duplicate', { + sourceCondition: sourceCondition, + index: this.index + }); +}; + +/** + * When an operation is selected, create the appropriate value inputs + * and add them to the view. If an operation is of type enum, create + * a drop-down menu instead. + * + * @param {string} operation The key of currently selected operation + */ +Condition.prototype.generateValueInputs = function (operation) { + const evaluator = this.conditionManager.getEvaluator(); + const inputArea = this.domElement.querySelector('.t-value-inputs'); + let inputCount; + let inputType; + let newInput; + let index = 0; + let emitChange = false; + + inputArea.innerHTML = ''; + this.valueInputs = []; + this.config.values = this.config.values || []; + + if (evaluator.getInputCount(operation)) { + inputCount = evaluator.getInputCount(operation); + inputType = evaluator.getInputType(operation); + + while (index < inputCount) { + if (inputType === 'select') { + const options = this.generateSelectOptions(); + + newInput = document.createElement('select'); + newInput.innerHTML = options; + + emitChange = true; + } else { + const defaultValue = inputType === 'number' ? 0 : ''; + const value = this.config.values[index] || defaultValue; + this.config.values[index] = value; + + newInput = document.createElement('input'); + newInput.type = `${inputType}`; + newInput.value = `${value}`; } - if (emitChange) { - this.eventEmitter.emit('change', { - value: Number(newInput[0].options[0].value), - property: 'values[0]', - index: this.index - }); - } + this.valueInputs.push(newInput); + inputArea.appendChild(newInput); + index += 1; } - }; - Condition.prototype.generateSelectOptions = function () { - let telemetryMetadata = this.conditionManager.getTelemetryMetadata(this.config.object); - let options = ''; - telemetryMetadata[this.config.key].enumerations.forEach((enumeration) => { - options += ''; - }); + if (emitChange) { + this.eventEmitter.emit('change', { + value: Number(newInput[0].options[0].value), + property: 'values[0]', + index: this.index + }); + } + } +}; - return options; - }; +Condition.prototype.generateSelectOptions = function () { + let telemetryMetadata = this.conditionManager.getTelemetryMetadata(this.config.object); + let options = ''; + telemetryMetadata[this.config.key].enumerations.forEach((enumeration) => { + options += ''; + }); - return Condition; -}); + return options; +}; diff --git a/src/plugins/summaryWidget/src/ConditionEvaluator.js b/src/plugins/summaryWidget/src/ConditionEvaluator.js index 1bef29b5c0e..9550fac32bb 100644 --- a/src/plugins/summaryWidget/src/ConditionEvaluator.js +++ b/src/plugins/summaryWidget/src/ConditionEvaluator.js @@ -1,486 +1,482 @@ -define([], function () { - /** - * Responsible for maintaining the possible operations for conditions - * in this widget, and evaluating the boolean value of conditions passed as - * input. - * @constructor - * @param {Object} subscriptionCache A cache consisting of the latest available - * data for any telemetry sources in the widget's - * composition. - * @param {Object} compositionObjs The current set of composition objects to - * evaluate for 'any' and 'all' conditions - */ - function ConditionEvaluator(subscriptionCache, compositionObjs) { - this.subscriptionCache = subscriptionCache; - this.compositionObjs = compositionObjs; +/** + * Responsible for maintaining the possible operations for conditions + * in this widget, and evaluating the boolean value of conditions passed as + * input. + * @constructor + * @param {Object} subscriptionCache A cache consisting of the latest available + * data for any telemetry sources in the widget's + * composition. + * @param {Object} compositionObjs The current set of composition objects to + * evaluate for 'any' and 'all' conditions + */ +function ConditionEvaluator(subscriptionCache, compositionObjs) { + this.subscriptionCache = subscriptionCache; + this.compositionObjs = compositionObjs; - this.testCache = {}; - this.useTestCache = false; + this.testCache = {}; + this.useTestCache = false; - /** - * Maps value types to HTML input field types. These - * type of inputs will be generated by conditions expecting this data type - */ - this.inputTypes = { - number: 'number', - string: 'text', - enum: 'select' - }; + /** + * Maps value types to HTML input field types. These + * type of inputs will be generated by conditions expecting this data type + */ + this.inputTypes = { + number: 'number', + string: 'text', + enum: 'select' + }; - /** - * Functions to validate that the input to an operation is of the type - * that it expects, in order to prevent unexpected behavior. Will be - * invoked before the corresponding operation is executed - */ - this.inputValidators = { - number: this.validateNumberInput, - string: this.validateStringInput, - enum: this.validateNumberInput - }; + /** + * Functions to validate that the input to an operation is of the type + * that it expects, in order to prevent unexpected behavior. Will be + * invoked before the corresponding operation is executed + */ + this.inputValidators = { + number: this.validateNumberInput, + string: this.validateStringInput, + enum: this.validateNumberInput + }; - /** - * A library of operations supported by this rule evaluator. Each operation - * consists of the following fields: - * operation: a function with boolean return type to be invoked when this - * operation is used. Will be called with an array of inputs - * where input [0] is the telemetry value and input [1..n] are - * any comparison values - * text: a human-readable description of this operation to populate selects - * appliesTo: an array of identifiers for types that operation may be used on - * inputCount: the number of inputs required to get any necessary comparison - * values for the operation - * getDescription: A function returning a human-readable shorthand description of - * this operation to populate the 'description' field in the rule header. - * Will be invoked with an array of a condition's comparison values. - */ - this.operations = { - equalTo: { - operation: function (input) { - return input[0] === input[1]; - }, - text: 'is equal to', - appliesTo: ['number'], - inputCount: 1, - getDescription: function (values) { - return ' == ' + values[0]; - } + /** + * A library of operations supported by this rule evaluator. Each operation + * consists of the following fields: + * operation: a function with boolean return type to be invoked when this + * operation is used. Will be called with an array of inputs + * where input [0] is the telemetry value and input [1..n] are + * any comparison values + * text: a human-readable description of this operation to populate selects + * appliesTo: an array of identifiers for types that operation may be used on + * inputCount: the number of inputs required to get any necessary comparison + * values for the operation + * getDescription: A function returning a human-readable shorthand description of + * this operation to populate the 'description' field in the rule header. + * Will be invoked with an array of a condition's comparison values. + */ + this.operations = { + equalTo: { + operation: function (input) { + return input[0] === input[1]; }, - notEqualTo: { - operation: function (input) { - return input[0] !== input[1]; - }, - text: 'is not equal to', - appliesTo: ['number'], - inputCount: 1, - getDescription: function (values) { - return ' != ' + values[0]; - } + text: 'is equal to', + appliesTo: ['number'], + inputCount: 1, + getDescription: function (values) { + return ' == ' + values[0]; + } + }, + notEqualTo: { + operation: function (input) { + return input[0] !== input[1]; }, - greaterThan: { - operation: function (input) { - return input[0] > input[1]; - }, - text: 'is greater than', - appliesTo: ['number'], - inputCount: 1, - getDescription: function (values) { - return ' > ' + values[0]; - } + text: 'is not equal to', + appliesTo: ['number'], + inputCount: 1, + getDescription: function (values) { + return ' != ' + values[0]; + } + }, + greaterThan: { + operation: function (input) { + return input[0] > input[1]; }, - lessThan: { - operation: function (input) { - return input[0] < input[1]; - }, - text: 'is less than', - appliesTo: ['number'], - inputCount: 1, - getDescription: function (values) { - return ' < ' + values[0]; - } + text: 'is greater than', + appliesTo: ['number'], + inputCount: 1, + getDescription: function (values) { + return ' > ' + values[0]; + } + }, + lessThan: { + operation: function (input) { + return input[0] < input[1]; }, - greaterThanOrEq: { - operation: function (input) { - return input[0] >= input[1]; - }, - text: 'is greater than or equal to', - appliesTo: ['number'], - inputCount: 1, - getDescription: function (values) { - return ' >= ' + values[0]; - } + text: 'is less than', + appliesTo: ['number'], + inputCount: 1, + getDescription: function (values) { + return ' < ' + values[0]; + } + }, + greaterThanOrEq: { + operation: function (input) { + return input[0] >= input[1]; }, - lessThanOrEq: { - operation: function (input) { - return input[0] <= input[1]; - }, - text: 'is less than or equal to', - appliesTo: ['number'], - inputCount: 1, - getDescription: function (values) { - return ' <= ' + values[0]; - } + text: 'is greater than or equal to', + appliesTo: ['number'], + inputCount: 1, + getDescription: function (values) { + return ' >= ' + values[0]; + } + }, + lessThanOrEq: { + operation: function (input) { + return input[0] <= input[1]; }, - between: { - operation: function (input) { - return input[0] > input[1] && input[0] < input[2]; - }, - text: 'is between', - appliesTo: ['number'], - inputCount: 2, - getDescription: function (values) { - return ' between ' + values[0] + ' and ' + values[1]; - } + text: 'is less than or equal to', + appliesTo: ['number'], + inputCount: 1, + getDescription: function (values) { + return ' <= ' + values[0]; + } + }, + between: { + operation: function (input) { + return input[0] > input[1] && input[0] < input[2]; }, - notBetween: { - operation: function (input) { - return input[0] < input[1] || input[0] > input[2]; - }, - text: 'is not between', - appliesTo: ['number'], - inputCount: 2, - getDescription: function (values) { - return ' not between ' + values[0] + ' and ' + values[1]; - } + text: 'is between', + appliesTo: ['number'], + inputCount: 2, + getDescription: function (values) { + return ' between ' + values[0] + ' and ' + values[1]; + } + }, + notBetween: { + operation: function (input) { + return input[0] < input[1] || input[0] > input[2]; }, - textContains: { - operation: function (input) { - return input[0] && input[1] && input[0].includes(input[1]); - }, - text: 'text contains', - appliesTo: ['string'], - inputCount: 1, - getDescription: function (values) { - return ' contains ' + values[0]; - } + text: 'is not between', + appliesTo: ['number'], + inputCount: 2, + getDescription: function (values) { + return ' not between ' + values[0] + ' and ' + values[1]; + } + }, + textContains: { + operation: function (input) { + return input[0] && input[1] && input[0].includes(input[1]); }, - textDoesNotContain: { - operation: function (input) { - return input[0] && input[1] && !input[0].includes(input[1]); - }, - text: 'text does not contain', - appliesTo: ['string'], - inputCount: 1, - getDescription: function (values) { - return ' does not contain ' + values[0]; - } + text: 'text contains', + appliesTo: ['string'], + inputCount: 1, + getDescription: function (values) { + return ' contains ' + values[0]; + } + }, + textDoesNotContain: { + operation: function (input) { + return input[0] && input[1] && !input[0].includes(input[1]); }, - textStartsWith: { - operation: function (input) { - return input[0].startsWith(input[1]); - }, - text: 'text starts with', - appliesTo: ['string'], - inputCount: 1, - getDescription: function (values) { - return ' starts with ' + values[0]; - } + text: 'text does not contain', + appliesTo: ['string'], + inputCount: 1, + getDescription: function (values) { + return ' does not contain ' + values[0]; + } + }, + textStartsWith: { + operation: function (input) { + return input[0].startsWith(input[1]); }, - textEndsWith: { - operation: function (input) { - return input[0].endsWith(input[1]); - }, - text: 'text ends with', - appliesTo: ['string'], - inputCount: 1, - getDescription: function (values) { - return ' ends with ' + values[0]; - } + text: 'text starts with', + appliesTo: ['string'], + inputCount: 1, + getDescription: function (values) { + return ' starts with ' + values[0]; + } + }, + textEndsWith: { + operation: function (input) { + return input[0].endsWith(input[1]); }, - textIsExactly: { - operation: function (input) { - return input[0] === input[1]; - }, - text: 'text is exactly', - appliesTo: ['string'], - inputCount: 1, - getDescription: function (values) { - return ' is exactly ' + values[0]; - } + text: 'text ends with', + appliesTo: ['string'], + inputCount: 1, + getDescription: function (values) { + return ' ends with ' + values[0]; + } + }, + textIsExactly: { + operation: function (input) { + return input[0] === input[1]; }, - isUndefined: { - operation: function (input) { - return typeof input[0] === 'undefined'; - }, - text: 'is undefined', - appliesTo: ['string', 'number', 'enum'], - inputCount: 0, - getDescription: function () { - return ' is undefined'; - } + text: 'text is exactly', + appliesTo: ['string'], + inputCount: 1, + getDescription: function (values) { + return ' is exactly ' + values[0]; + } + }, + isUndefined: { + operation: function (input) { + return typeof input[0] === 'undefined'; }, - isDefined: { - operation: function (input) { - return typeof input[0] !== 'undefined'; - }, - text: 'is defined', - appliesTo: ['string', 'number', 'enum'], - inputCount: 0, - getDescription: function () { - return ' is defined'; - } + text: 'is undefined', + appliesTo: ['string', 'number', 'enum'], + inputCount: 0, + getDescription: function () { + return ' is undefined'; + } + }, + isDefined: { + operation: function (input) { + return typeof input[0] !== 'undefined'; }, - enumValueIs: { - operation: function (input) { - return input[0] === input[1]; - }, - text: 'is', - appliesTo: ['enum'], - inputCount: 1, - getDescription: function (values) { - return ' == ' + values[0]; - } + text: 'is defined', + appliesTo: ['string', 'number', 'enum'], + inputCount: 0, + getDescription: function () { + return ' is defined'; + } + }, + enumValueIs: { + operation: function (input) { + return input[0] === input[1]; }, - enumValueIsNot: { - operation: function (input) { - return input[0] !== input[1]; - }, - text: 'is not', - appliesTo: ['enum'], - inputCount: 1, - getDescription: function (values) { - return ' != ' + values[0]; - } + text: 'is', + appliesTo: ['enum'], + inputCount: 1, + getDescription: function (values) { + return ' == ' + values[0]; } - }; - } + }, + enumValueIsNot: { + operation: function (input) { + return input[0] !== input[1]; + }, + text: 'is not', + appliesTo: ['enum'], + inputCount: 1, + getDescription: function (values) { + return ' != ' + values[0]; + } + } + }; +} - /** - * Evaluate the conditions passed in as an argument, and return the boolean - * value of these conditions. Available evaluation modes are 'any', which will - * return true if any of the conditions evaluates to true (i.e. logical OR); 'all', - * which returns true only if all conditions evalute to true (i.e. logical AND); - * or 'js', which returns the boolean value of a custom JavaScript conditional. - * @param {} conditions Either an array of objects with object, key, operation, - * and value fields, or a string representing a JavaScript - * condition. - * @param {string} mode The key of the mode to use when evaluating the conditions. - * @return {boolean} The boolean value of the conditions - */ - ConditionEvaluator.prototype.execute = function (conditions, mode) { - let active = false; - let conditionValue; - let conditionDefined = false; - const self = this; - let firstRuleEvaluated = false; - const compositionObjs = this.compositionObjs; +/** + * Evaluate the conditions passed in as an argument, and return the boolean + * value of these conditions. Available evaluation modes are 'any', which will + * return true if any of the conditions evaluates to true (i.e. logical OR); 'all', + * which returns true only if all conditions evalute to true (i.e. logical AND); + * or 'js', which returns the boolean value of a custom JavaScript conditional. + * @param {} conditions Either an array of objects with object, key, operation, + * and value fields, or a string representing a JavaScript + * condition. + * @param {string} mode The key of the mode to use when evaluating the conditions. + * @return {boolean} The boolean value of the conditions + */ +ConditionEvaluator.prototype.execute = function (conditions, mode) { + let active = false; + let conditionValue; + let conditionDefined = false; + const self = this; + let firstRuleEvaluated = false; + const compositionObjs = this.compositionObjs; - if (mode === 'js') { - active = this.executeJavaScriptCondition(conditions); - } else { - (conditions || []).forEach(function (condition) { - conditionDefined = false; - if (condition.object === 'any') { - conditionValue = false; - Object.keys(compositionObjs).forEach(function (objId) { - try { - conditionValue = - conditionValue || - self.executeCondition(objId, condition.key, condition.operation, condition.values); - conditionDefined = true; - } catch (e) { - //ignore a malformed condition - } - }); - } else if (condition.object === 'all') { - conditionValue = true; - Object.keys(compositionObjs).forEach(function (objId) { - try { - conditionValue = - conditionValue && - self.executeCondition(objId, condition.key, condition.operation, condition.values); - conditionDefined = true; - } catch (e) { - //ignore a malformed condition - } - }); - } else { + if (mode === 'js') { + active = this.executeJavaScriptCondition(conditions); + } else { + (conditions || []).forEach(function (condition) { + conditionDefined = false; + if (condition.object === 'any') { + conditionValue = false; + Object.keys(compositionObjs).forEach(function (objId) { try { - conditionValue = self.executeCondition( - condition.object, - condition.key, - condition.operation, - condition.values - ); + conditionValue = + conditionValue || + self.executeCondition(objId, condition.key, condition.operation, condition.values); conditionDefined = true; } catch (e) { - //ignore malformed condition + //ignore a malformed condition } + }); + } else if (condition.object === 'all') { + conditionValue = true; + Object.keys(compositionObjs).forEach(function (objId) { + try { + conditionValue = + conditionValue && + self.executeCondition(objId, condition.key, condition.operation, condition.values); + conditionDefined = true; + } catch (e) { + //ignore a malformed condition + } + }); + } else { + try { + conditionValue = self.executeCondition( + condition.object, + condition.key, + condition.operation, + condition.values + ); + conditionDefined = true; + } catch (e) { + //ignore malformed condition } + } - if (conditionDefined) { - active = mode === 'all' && !firstRuleEvaluated ? true : active; - firstRuleEvaluated = true; - if (mode === 'any') { - active = active || conditionValue; - } else if (mode === 'all') { - active = active && conditionValue; - } + if (conditionDefined) { + active = mode === 'all' && !firstRuleEvaluated ? true : active; + firstRuleEvaluated = true; + if (mode === 'any') { + active = active || conditionValue; + } else if (mode === 'all') { + active = active && conditionValue; } - }); - } + } + }); + } - return active; - }; + return active; +}; - /** - * Execute a condition defined as an object. - * @param {string} object The identifier of the telemetry object to retrieve data from - * @param {string} key The property of the telemetry object - * @param {string} operation The key of the operation in this ConditionEvaluator to executeCondition - * @param {string} values An array of comparison values to invoke the operation with - * @return {boolean} The value of this condition - */ - ConditionEvaluator.prototype.executeCondition = function (object, key, operation, values) { - const cache = this.useTestCache ? this.testCache : this.subscriptionCache; - let telemetryValue; - let op; - let input; - let validator; +/** + * Execute a condition defined as an object. + * @param {string} object The identifier of the telemetry object to retrieve data from + * @param {string} key The property of the telemetry object + * @param {string} operation The key of the operation in this ConditionEvaluator to executeCondition + * @param {string} values An array of comparison values to invoke the operation with + * @return {boolean} The value of this condition + */ +ConditionEvaluator.prototype.executeCondition = function (object, key, operation, values) { + const cache = this.useTestCache ? this.testCache : this.subscriptionCache; + let telemetryValue; + let op; + let input; + let validator; - if (cache[object] && typeof cache[object][key] !== 'undefined') { - let value = cache[object][key]; - telemetryValue = [isNaN(Number(value)) ? value : Number(value)]; - } + if (cache[object] && typeof cache[object][key] !== 'undefined') { + let value = cache[object][key]; + telemetryValue = [isNaN(Number(value)) ? value : Number(value)]; + } - op = this.operations[operation] && this.operations[operation].operation; - input = telemetryValue && telemetryValue.concat(values); - validator = op && this.inputValidators[this.operations[operation].appliesTo[0]]; + op = this.operations[operation] && this.operations[operation].operation; + input = telemetryValue && telemetryValue.concat(values); + validator = op && this.inputValidators[this.operations[operation].appliesTo[0]]; - if (op && input && validator) { - if (this.operations[operation].appliesTo.length > 1) { - return (this.validateNumberInput(input) || this.validateStringInput(input)) && op(input); - } else { - return validator(input) && op(input); - } + if (op && input && validator) { + if (this.operations[operation].appliesTo.length > 1) { + return (this.validateNumberInput(input) || this.validateStringInput(input)) && op(input); } else { - throw new Error('Malformed condition'); + return validator(input) && op(input); } - }; + } else { + throw new Error('Malformed condition'); + } +}; - /** - * A function that returns true only if each value in its input argument is - * of a numerical type - * @param {[]} input An array of values - * @returns {boolean} - */ - ConditionEvaluator.prototype.validateNumberInput = function (input) { - let valid = true; - input.forEach(function (value) { - valid = valid && typeof value === 'number'; - }); +/** + * A function that returns true only if each value in its input argument is + * of a numerical type + * @param {[]} input An array of values + * @returns {boolean} + */ +ConditionEvaluator.prototype.validateNumberInput = function (input) { + let valid = true; + input.forEach(function (value) { + valid = valid && typeof value === 'number'; + }); - return valid; - }; + return valid; +}; - /** - * A function that returns true only if each value in its input argument is - * a string - * @param {[]} input An array of values - * @returns {boolean} - */ - ConditionEvaluator.prototype.validateStringInput = function (input) { - let valid = true; - input.forEach(function (value) { - valid = valid && typeof value === 'string'; - }); +/** + * A function that returns true only if each value in its input argument is + * a string + * @param {[]} input An array of values + * @returns {boolean} + */ +ConditionEvaluator.prototype.validateStringInput = function (input) { + let valid = true; + input.forEach(function (value) { + valid = valid && typeof value === 'string'; + }); - return valid; - }; + return valid; +}; - /** - * Get the keys of operations supported by this evaluator - * @return {string[]} An array of the keys of supported operations - */ - ConditionEvaluator.prototype.getOperationKeys = function () { - return Object.keys(this.operations); - }; +/** + * Get the keys of operations supported by this evaluator + * @return {string[]} An array of the keys of supported operations + */ +ConditionEvaluator.prototype.getOperationKeys = function () { + return Object.keys(this.operations); +}; - /** - * Get the human-readable text corresponding to a given operation - * @param {string} key The key of the operation - * @return {string} The text description of the operation - */ - ConditionEvaluator.prototype.getOperationText = function (key) { - return this.operations[key].text; - }; +/** + * Get the human-readable text corresponding to a given operation + * @param {string} key The key of the operation + * @return {string} The text description of the operation + */ +ConditionEvaluator.prototype.getOperationText = function (key) { + return this.operations[key].text; +}; - /** - * Returns true only if the given operation applies to a given type - * @param {string} key The key of the operation - * @param {string} type The value type to query - * @returns {boolean} True if the condition applies, false otherwise - */ - ConditionEvaluator.prototype.operationAppliesTo = function (key, type) { - return this.operations[key].appliesTo.includes(type); - }; +/** + * Returns true only if the given operation applies to a given type + * @param {string} key The key of the operation + * @param {string} type The value type to query + * @returns {boolean} True if the condition applies, false otherwise + */ +ConditionEvaluator.prototype.operationAppliesTo = function (key, type) { + return this.operations[key].appliesTo.includes(type); +}; - /** - * Return the number of value inputs required by an operation - * @param {string} key The key of the operation to query - * @return {number} - */ - ConditionEvaluator.prototype.getInputCount = function (key) { - if (this.operations[key]) { - return this.operations[key].inputCount; - } - }; - - /** - * Return the human-readable shorthand description of the operation for a rule header - * @param {string} key The key of the operation to query - * @param {} values An array of values with which to invoke the getDescription function - * of the operation - * @return {string} A text description of this operation - */ - ConditionEvaluator.prototype.getOperationDescription = function (key, values) { - if (this.operations[key]) { - return this.operations[key].getDescription(values); - } - }; +/** + * Return the number of value inputs required by an operation + * @param {string} key The key of the operation to query + * @return {number} + */ +ConditionEvaluator.prototype.getInputCount = function (key) { + if (this.operations[key]) { + return this.operations[key].inputCount; + } +}; - /** - * Return the HTML input type associated with a given operation - * @param {string} key The key of the operation to query - * @return {string} The key for an HTML5 input type - */ - ConditionEvaluator.prototype.getInputType = function (key) { - let type; - if (this.operations[key]) { - type = this.operations[key].appliesTo[0]; - } +/** + * Return the human-readable shorthand description of the operation for a rule header + * @param {string} key The key of the operation to query + * @param {} values An array of values with which to invoke the getDescription function + * of the operation + * @return {string} A text description of this operation + */ +ConditionEvaluator.prototype.getOperationDescription = function (key, values) { + if (this.operations[key]) { + return this.operations[key].getDescription(values); + } +}; - if (this.inputTypes[type]) { - return this.inputTypes[type]; - } - }; +/** + * Return the HTML input type associated with a given operation + * @param {string} key The key of the operation to query + * @return {string} The key for an HTML5 input type + */ +ConditionEvaluator.prototype.getInputType = function (key) { + let type; + if (this.operations[key]) { + type = this.operations[key].appliesTo[0]; + } - /** - * Returns the HTML input type associated with a value type - * @param {string} dataType The JavaScript value type - * @return {string} The key for an HTML5 input type - */ - ConditionEvaluator.prototype.getInputTypeById = function (dataType) { - return this.inputTypes[dataType]; - }; + if (this.inputTypes[type]) { + return this.inputTypes[type]; + } +}; - /** - * Set the test data cache used by this rule evaluator - * @param {object} testCache A mock cache following the format of the real - * subscription cache - */ - ConditionEvaluator.prototype.setTestDataCache = function (testCache) { - this.testCache = testCache; - }; +/** + * Returns the HTML input type associated with a value type + * @param {string} dataType The JavaScript value type + * @return {string} The key for an HTML5 input type + */ +ConditionEvaluator.prototype.getInputTypeById = function (dataType) { + return this.inputTypes[dataType]; +}; - /** - * Have this RuleEvaluator pull data values from the provided test cache - * instead of its actual subscription cache when evaluating. If invoked with true, - * will use the test cache; otherwise, will use the subscription cache - * @param {boolean} useTestData Boolean flag - */ - ConditionEvaluator.prototype.useTestData = function (useTestCache) { - this.useTestCache = useTestCache; - }; +/** + * Set the test data cache used by this rule evaluator + * @param {object} testCache A mock cache following the format of the real + * subscription cache + */ +ConditionEvaluator.prototype.setTestDataCache = function (testCache) { + this.testCache = testCache; +}; - return ConditionEvaluator; -}); +/** + * Have this RuleEvaluator pull data values from the provided test cache + * instead of its actual subscription cache when evaluating. If invoked with true, + * will use the test cache; otherwise, will use the subscription cache + * @param {boolean} useTestData Boolean flag + */ +ConditionEvaluator.prototype.useTestData = function (useTestCache) { + this.useTestCache = useTestCache; +}; diff --git a/src/plugins/summaryWidget/src/ConditionManager.js b/src/plugins/summaryWidget/src/ConditionManager.js index 7ca6c7d33d4..70ea96663f1 100644 --- a/src/plugins/summaryWidget/src/ConditionManager.js +++ b/src/plugins/summaryWidget/src/ConditionManager.js @@ -1,386 +1,383 @@ -define(['./ConditionEvaluator', 'objectUtils', 'EventEmitter', 'lodash'], function ( - ConditionEvaluator, - objectUtils, - EventEmitter, - _ -) { - /** - * Provides a centralized content manager for conditions in the summary widget. - * Loads and caches composition and telemetry subscriptions, and maintains a - * {ConditionEvaluator} instance to handle evaluation - * @constructor - * @param {Object} domainObject the Summary Widget domain object - * @param {MCT} openmct an MCT instance - */ - function ConditionManager(domainObject, openmct) { - this.domainObject = domainObject; - this.openmct = openmct; - - this.composition = this.openmct.composition.get(this.domainObject); - this.compositionObjs = {}; - this.eventEmitter = new EventEmitter(); - this.supportedCallbacks = ['add', 'remove', 'load', 'metadata', 'receiveTelemetry']; - - this.keywordLabels = { - any: 'any Telemetry', - all: 'all Telemetry' - }; - - this.telemetryMetadataById = { - any: {}, - all: {} - }; - - this.telemetryTypesById = { - any: {}, - all: {} - }; - - this.subscriptions = {}; - this.subscriptionCache = {}; - this.loadComplete = false; - this.metadataLoadComplete = false; - this.evaluator = new ConditionEvaluator(this.subscriptionCache, this.compositionObjs); - - this.composition.on('add', this.onCompositionAdd, this); - this.composition.on('remove', this.onCompositionRemove, this); - this.composition.on('load', this.onCompositionLoad, this); - - this.composition.load(); - } - - /** - * Register a callback with this ConditionManager: supported callbacks are add - * remove, load, metadata, and receiveTelemetry - * @param {string} event The key for the event to listen to - * @param {function} callback The function that this rule will invoke on this event - * @param {Object} context A reference to a scope to use as the context for - * context for the callback function - */ - ConditionManager.prototype.on = function (event, callback, context) { - if (this.supportedCallbacks.includes(event)) { - this.eventEmitter.on(event, callback, context || this); - } else { - throw ( - event + ' is not a supported callback. Supported callbacks are ' + this.supportedCallbacks - ); - } - }; - - /** - * Given a set of rules, execute the conditions associated with each rule - * and return the id of the last rule whose conditions evaluate to true - * @param {string[]} ruleOrder An array of rule IDs indicating what order They - * should be evaluated in - * @param {Object} rules An object mapping rule IDs to rule configurations - * @return {string} The ID of the rule to display on the widget - */ - ConditionManager.prototype.executeRules = function (ruleOrder, rules) { - const self = this; - let activeId = ruleOrder[0]; - let rule; - let conditions; - - ruleOrder.forEach(function (ruleId) { - rule = rules[ruleId]; - conditions = rule.getProperty('conditions'); - if (self.evaluator.execute(conditions, rule.getProperty('trigger'))) { - activeId = ruleId; - } - }); - - return activeId; - }; - - /** - * Adds a field to the list of all available metadata fields in the widget - * @param {Object} metadatum An object representing a set of telemetry metadata - */ - ConditionManager.prototype.addGlobalMetadata = function (metadatum) { - this.telemetryMetadataById.any[metadatum.key] = metadatum; - this.telemetryMetadataById.all[metadatum.key] = metadatum; - }; - - /** - * Adds a field to the list of properties for globally available metadata - * @param {string} key The key for the property this type applies to - * @param {string} type The type that should be associated with this property - */ - ConditionManager.prototype.addGlobalPropertyType = function (key, type) { - this.telemetryTypesById.any[key] = type; - this.telemetryTypesById.all[key] = type; - }; - - /** - * Given a telemetry-producing domain object, associate each of it's telemetry - * fields with a type, parsing from historical data. - * @param {Object} object a domain object that can produce telemetry - * @return {Promise} A promise that resolves when a telemetry request - * has completed and types have been parsed - */ - ConditionManager.prototype.parsePropertyTypes = function (object) { - const objectId = objectUtils.makeKeyString(object.identifier); - - this.telemetryTypesById[objectId] = {}; - Object.values(this.telemetryMetadataById[objectId]).forEach(function (valueMetadata) { - let type; - if (valueMetadata.enumerations !== undefined) { - type = 'enum'; - } else if (Object.prototype.hasOwnProperty.call(valueMetadata.hints, 'range')) { - type = 'number'; - } else if (Object.prototype.hasOwnProperty.call(valueMetadata.hints, 'domain')) { - type = 'number'; - } else if (valueMetadata.key === 'name') { - type = 'string'; - } else { - type = 'string'; - } - - this.telemetryTypesById[objectId][valueMetadata.key] = type; - this.addGlobalPropertyType(valueMetadata.key, type); - }, this); - }; - - /** - * Parse types of telemetry fields from all composition objects; used internally - * to perform a block types load once initial composition load has completed - * @return {Promise} A promise that resolves when all metadata has been loaded - * and property types parsed - */ - ConditionManager.prototype.parseAllPropertyTypes = function () { - Object.values(this.compositionObjs).forEach(this.parsePropertyTypes, this); - this.metadataLoadComplete = true; - this.eventEmitter.emit('metadata'); +import EventEmitter from 'EventEmitter'; +import _ from 'lodash'; +import objectUtils from 'objectUtils'; + +import ConditionEvaluator from './ConditionEvaluator'; + +/** + * Provides a centralized content manager for conditions in the summary widget. + * Loads and caches composition and telemetry subscriptions, and maintains a + * {ConditionEvaluator} instance to handle evaluation + * @constructor + * @param {Object} domainObject the Summary Widget domain object + * @param {MCT} openmct an MCT instance + */ +export default function ConditionManager(domainObject, openmct) { + this.domainObject = domainObject; + this.openmct = openmct; + + this.composition = this.openmct.composition.get(this.domainObject); + this.compositionObjs = {}; + this.eventEmitter = new EventEmitter(); + this.supportedCallbacks = ['add', 'remove', 'load', 'metadata', 'receiveTelemetry']; + + this.keywordLabels = { + any: 'any Telemetry', + all: 'all Telemetry' }; - /** - * Invoked when a telemetry subscription yields new data. Updates the LAD - * cache and invokes any registered receiveTelemetry callbacks - * @param {string} objId The key associated with the telemetry source - * @param {datum} datum The new data from the telemetry source - * @private - */ - ConditionManager.prototype.handleSubscriptionCallback = function (objId, telemetryDatum) { - this.subscriptionCache[objId] = this.createNormalizedDatum(objId, telemetryDatum); - this.eventEmitter.emit('receiveTelemetry'); + this.telemetryMetadataById = { + any: {}, + all: {} }; - ConditionManager.prototype.createNormalizedDatum = function (objId, telemetryDatum) { - return Object.values(this.telemetryMetadataById[objId]).reduce((normalizedDatum, metadatum) => { - normalizedDatum[metadatum.key] = telemetryDatum[metadatum.source]; - - return normalizedDatum; - }, {}); + this.telemetryTypesById = { + any: {}, + all: {} }; - /** - * Event handler for an add event in this Summary Widget's composition. - * Sets up subscription handlers and parses its property types. - * @param {Object} obj The newly added domain object - * @private - */ - ConditionManager.prototype.onCompositionAdd = function (obj) { - let compositionKeys; - const telemetryAPI = this.openmct.telemetry; - const objId = objectUtils.makeKeyString(obj.identifier); - let telemetryMetadata; - const self = this; - - if (telemetryAPI.isTelemetryObject(obj)) { - self.compositionObjs[objId] = obj; - self.telemetryMetadataById[objId] = {}; - - // FIXME: this should just update based on listener. - compositionKeys = self.domainObject.composition.map(objectUtils.makeKeyString); - if (!compositionKeys.includes(objId)) { - self.domainObject.composition.push(obj.identifier); - } - - telemetryMetadata = telemetryAPI.getMetadata(obj).values(); - telemetryMetadata.forEach(function (metaDatum) { - self.telemetryMetadataById[objId][metaDatum.key] = metaDatum; - self.addGlobalMetadata(metaDatum); - }); - - self.subscriptionCache[objId] = {}; - self.subscriptions[objId] = telemetryAPI.subscribe( - obj, - function (datum) { - self.handleSubscriptionCallback(objId, datum); - }, - {} - ); - telemetryAPI - .request(obj, { - strategy: 'latest', - size: 1 - }) - .then(function (results) { - if (results && results.length) { - self.handleSubscriptionCallback(objId, results[results.length - 1]); - } - }); - - /** - * if this is the initial load, parsing property types will be postponed - * until all composition objects have been loaded - */ - if (self.loadComplete) { - self.parsePropertyTypes(obj); - } - - self.eventEmitter.emit('add', obj); - - const summaryWidget = document.querySelector('.w-summary-widget'); - if (summaryWidget) { - summaryWidget.classList.remove('s-status-no-data'); - } + this.subscriptions = {}; + this.subscriptionCache = {}; + this.loadComplete = false; + this.metadataLoadComplete = false; + this.evaluator = new ConditionEvaluator(this.subscriptionCache, this.compositionObjs); + + this.composition.on('add', this.onCompositionAdd, this); + this.composition.on('remove', this.onCompositionRemove, this); + this.composition.on('load', this.onCompositionLoad, this); + + this.composition.load(); +} + +/** + * Register a callback with this ConditionManager: supported callbacks are add + * remove, load, metadata, and receiveTelemetry + * @param {string} event The key for the event to listen to + * @param {function} callback The function that this rule will invoke on this event + * @param {Object} context A reference to a scope to use as the context for + * context for the callback function + */ +ConditionManager.prototype.on = function (event, callback, context) { + if (this.supportedCallbacks.includes(event)) { + this.eventEmitter.on(event, callback, context || this); + } else { + throw ( + event + ' is not a supported callback. Supported callbacks are ' + this.supportedCallbacks + ); + } +}; + +/** + * Given a set of rules, execute the conditions associated with each rule + * and return the id of the last rule whose conditions evaluate to true + * @param {string[]} ruleOrder An array of rule IDs indicating what order They + * should be evaluated in + * @param {Object} rules An object mapping rule IDs to rule configurations + * @return {string} The ID of the rule to display on the widget + */ +ConditionManager.prototype.executeRules = function (ruleOrder, rules) { + const self = this; + let activeId = ruleOrder[0]; + let rule; + let conditions; + + ruleOrder.forEach(function (ruleId) { + rule = rules[ruleId]; + conditions = rule.getProperty('conditions'); + if (self.evaluator.execute(conditions, rule.getProperty('trigger'))) { + activeId = ruleId; + } + }); + + return activeId; +}; + +/** + * Adds a field to the list of all available metadata fields in the widget + * @param {Object} metadatum An object representing a set of telemetry metadata + */ +ConditionManager.prototype.addGlobalMetadata = function (metadatum) { + this.telemetryMetadataById.any[metadatum.key] = metadatum; + this.telemetryMetadataById.all[metadatum.key] = metadatum; +}; + +/** + * Adds a field to the list of properties for globally available metadata + * @param {string} key The key for the property this type applies to + * @param {string} type The type that should be associated with this property + */ +ConditionManager.prototype.addGlobalPropertyType = function (key, type) { + this.telemetryTypesById.any[key] = type; + this.telemetryTypesById.all[key] = type; +}; + +/** + * Given a telemetry-producing domain object, associate each of it's telemetry + * fields with a type, parsing from historical data. + * @param {Object} object a domain object that can produce telemetry + * @return {Promise} A promise that resolves when a telemetry request + * has completed and types have been parsed + */ +ConditionManager.prototype.parsePropertyTypes = function (object) { + const objectId = objectUtils.makeKeyString(object.identifier); + + this.telemetryTypesById[objectId] = {}; + Object.values(this.telemetryMetadataById[objectId]).forEach(function (valueMetadata) { + let type; + if (valueMetadata.enumerations !== undefined) { + type = 'enum'; + } else if (Object.prototype.hasOwnProperty.call(valueMetadata.hints, 'range')) { + type = 'number'; + } else if (Object.prototype.hasOwnProperty.call(valueMetadata.hints, 'domain')) { + type = 'number'; + } else if (valueMetadata.key === 'name') { + type = 'string'; + } else { + type = 'string'; } - }; - /** - * Invoked on a remove event in this Summary Widget's composition. Removes - * the object from the local composition, and untracks it - * @param {object} identifier The identifier of the object to be removed - * @private - */ - ConditionManager.prototype.onCompositionRemove = function (identifier) { - const objectId = objectUtils.makeKeyString(identifier); - // FIXME: this should just update by listener. - _.remove(this.domainObject.composition, function (id) { - return id.key === identifier.key && id.namespace === identifier.namespace; - }); - delete this.compositionObjs[objectId]; - delete this.subscriptionCache[objectId]; - this.subscriptions[objectId](); //unsubscribe from telemetry source - delete this.subscriptions[objectId]; - this.eventEmitter.emit('remove', identifier); - - if (_.isEmpty(this.compositionObjs)) { - const summaryWidget = document.querySelector('.w-summary-widget'); - if (summaryWidget) { - summaryWidget.classList.add('s-status-no-data'); - } + this.telemetryTypesById[objectId][valueMetadata.key] = type; + this.addGlobalPropertyType(valueMetadata.key, type); + }, this); +}; + +/** + * Parse types of telemetry fields from all composition objects; used internally + * to perform a block types load once initial composition load has completed + * @return {Promise} A promise that resolves when all metadata has been loaded + * and property types parsed + */ +ConditionManager.prototype.parseAllPropertyTypes = function () { + Object.values(this.compositionObjs).forEach(this.parsePropertyTypes, this); + this.metadataLoadComplete = true; + this.eventEmitter.emit('metadata'); +}; + +/** + * Invoked when a telemetry subscription yields new data. Updates the LAD + * cache and invokes any registered receiveTelemetry callbacks + * @param {string} objId The key associated with the telemetry source + * @param {datum} datum The new data from the telemetry source + * @private + */ +ConditionManager.prototype.handleSubscriptionCallback = function (objId, telemetryDatum) { + this.subscriptionCache[objId] = this.createNormalizedDatum(objId, telemetryDatum); + this.eventEmitter.emit('receiveTelemetry'); +}; + +ConditionManager.prototype.createNormalizedDatum = function (objId, telemetryDatum) { + return Object.values(this.telemetryMetadataById[objId]).reduce((normalizedDatum, metadatum) => { + normalizedDatum[metadatum.key] = telemetryDatum[metadatum.source]; + + return normalizedDatum; + }, {}); +}; + +/** + * Event handler for an add event in this Summary Widget's composition. + * Sets up subscription handlers and parses its property types. + * @param {Object} obj The newly added domain object + * @private + */ +ConditionManager.prototype.onCompositionAdd = function (obj) { + let compositionKeys; + const telemetryAPI = this.openmct.telemetry; + const objId = objectUtils.makeKeyString(obj.identifier); + let telemetryMetadata; + const self = this; + + if (telemetryAPI.isTelemetryObject(obj)) { + self.compositionObjs[objId] = obj; + self.telemetryMetadataById[objId] = {}; + + // FIXME: this should just update based on listener. + compositionKeys = self.domainObject.composition.map(objectUtils.makeKeyString); + if (!compositionKeys.includes(objId)) { + self.domainObject.composition.push(obj.identifier); } - }; - /** - * Invoked when the Summary Widget's composition finishes its initial load. - * Invokes any registered load callbacks, does a block load of all metadata, - * and then invokes any registered metadata load callbacks. - * @private - */ - ConditionManager.prototype.onCompositionLoad = function () { - this.loadComplete = true; - this.eventEmitter.emit('load'); - this.parseAllPropertyTypes(); - }; + telemetryMetadata = telemetryAPI.getMetadata(obj).values(); + telemetryMetadata.forEach(function (metaDatum) { + self.telemetryMetadataById[objId][metaDatum.key] = metaDatum; + self.addGlobalMetadata(metaDatum); + }); - /** - * Returns the currently tracked telemetry sources - * @return {Object} An object mapping object keys to domain objects - */ - ConditionManager.prototype.getComposition = function () { - return this.compositionObjs; - }; + self.subscriptionCache[objId] = {}; + self.subscriptions[objId] = telemetryAPI.subscribe( + obj, + function (datum) { + self.handleSubscriptionCallback(objId, datum); + }, + {} + ); + telemetryAPI + .request(obj, { + strategy: 'latest', + size: 1 + }) + .then(function (results) { + if (results && results.length) { + self.handleSubscriptionCallback(objId, results[results.length - 1]); + } + }); - /** - * Get the human-readable name of a domain object from its key - * @param {string} id The key of the domain object - * @return {string} The human-readable name of the domain object - */ - ConditionManager.prototype.getObjectName = function (id) { - let name; - - if (this.keywordLabels[id]) { - name = this.keywordLabels[id]; - } else if (this.compositionObjs[id]) { - name = this.compositionObjs[id].name; + /** + * if this is the initial load, parsing property types will be postponed + * until all composition objects have been loaded + */ + if (self.loadComplete) { + self.parsePropertyTypes(obj); } - return name; - }; + self.eventEmitter.emit('add', obj); - /** - * Returns the property metadata associated with a given telemetry source - * @param {string} id The key associated with the domain object - * @return {Object} Returns an object with fields representing each telemetry field - */ - ConditionManager.prototype.getTelemetryMetadata = function (id) { - return this.telemetryMetadataById[id]; - }; - - /** - * Returns the type associated with a telemetry data field of a particular domain - * object - * @param {string} id The key associated with the domain object - * @param {string} property The telemetry field key to retrieve the type of - * @return {string} The type name - */ - ConditionManager.prototype.getTelemetryPropertyType = function (id, property) { - if (this.telemetryTypesById[id]) { - return this.telemetryTypesById[id][property]; + const summaryWidget = document.querySelector('.w-summary-widget'); + if (summaryWidget) { + summaryWidget.classList.remove('s-status-no-data'); } - }; - - /** - * Returns the human-readable name of a telemetry data field of a particular domain - * object - * @param {string} id The key associated with the domain object - * @param {string} property The telemetry field key to retrieve the type of - * @return {string} The telemetry field name - */ - ConditionManager.prototype.getTelemetryPropertyName = function (id, property) { - if (this.telemetryMetadataById[id] && this.telemetryMetadataById[id][property]) { - return this.telemetryMetadataById[id][property].name; + } +}; + +/** + * Invoked on a remove event in this Summary Widget's composition. Removes + * the object from the local composition, and untracks it + * @param {object} identifier The identifier of the object to be removed + * @private + */ +ConditionManager.prototype.onCompositionRemove = function (identifier) { + const objectId = objectUtils.makeKeyString(identifier); + // FIXME: this should just update by listener. + _.remove(this.domainObject.composition, function (id) { + return id.key === identifier.key && id.namespace === identifier.namespace; + }); + delete this.compositionObjs[objectId]; + delete this.subscriptionCache[objectId]; + this.subscriptions[objectId](); //unsubscribe from telemetry source + delete this.subscriptions[objectId]; + this.eventEmitter.emit('remove', identifier); + + if (_.isEmpty(this.compositionObjs)) { + const summaryWidget = document.querySelector('.w-summary-widget'); + if (summaryWidget) { + summaryWidget.classList.add('s-status-no-data'); } - }; - - /** - * Returns the {ConditionEvaluator} instance associated with this condition - * manager - * @return {ConditionEvaluator} - */ - ConditionManager.prototype.getEvaluator = function () { - return this.evaluator; - }; - - /** - * Returns true if the initial composition load has completed - * @return {boolean} - */ - ConditionManager.prototype.loadCompleted = function () { - return this.loadComplete; - }; - - /** - * Returns true if the initial block metadata load has completed - */ - ConditionManager.prototype.metadataLoadCompleted = function () { - return this.metadataLoadComplete; - }; - - /** - * Triggers the telemetryReceive callbacks registered to this ConditionManager, - * used by the {TestDataManager} to force a rule evaluation when test data is - * enabled - */ - ConditionManager.prototype.triggerTelemetryCallback = function () { - this.eventEmitter.emit('receiveTelemetry'); - }; - - /** - * Unsubscribe from all registered telemetry sources and unregister all event - * listeners registered with the Open MCT APIs - */ - ConditionManager.prototype.destroy = function () { - Object.values(this.subscriptions).forEach(function (unsubscribeFunction) { - unsubscribeFunction(); - }); - this.composition.off('add', this.onCompositionAdd, this); - this.composition.off('remove', this.onCompositionRemove, this); - this.composition.off('load', this.onCompositionLoad, this); - }; + } +}; + +/** + * Invoked when the Summary Widget's composition finishes its initial load. + * Invokes any registered load callbacks, does a block load of all metadata, + * and then invokes any registered metadata load callbacks. + * @private + */ +ConditionManager.prototype.onCompositionLoad = function () { + this.loadComplete = true; + this.eventEmitter.emit('load'); + this.parseAllPropertyTypes(); +}; + +/** + * Returns the currently tracked telemetry sources + * @return {Object} An object mapping object keys to domain objects + */ +ConditionManager.prototype.getComposition = function () { + return this.compositionObjs; +}; + +/** + * Get the human-readable name of a domain object from its key + * @param {string} id The key of the domain object + * @return {string} The human-readable name of the domain object + */ +ConditionManager.prototype.getObjectName = function (id) { + let name; + + if (this.keywordLabels[id]) { + name = this.keywordLabels[id]; + } else if (this.compositionObjs[id]) { + name = this.compositionObjs[id].name; + } - return ConditionManager; -}); + return name; +}; + +/** + * Returns the property metadata associated with a given telemetry source + * @param {string} id The key associated with the domain object + * @return {Object} Returns an object with fields representing each telemetry field + */ +ConditionManager.prototype.getTelemetryMetadata = function (id) { + return this.telemetryMetadataById[id]; +}; + +/** + * Returns the type associated with a telemetry data field of a particular domain + * object + * @param {string} id The key associated with the domain object + * @param {string} property The telemetry field key to retrieve the type of + * @return {string} The type name + */ +ConditionManager.prototype.getTelemetryPropertyType = function (id, property) { + if (this.telemetryTypesById[id]) { + return this.telemetryTypesById[id][property]; + } +}; + +/** + * Returns the human-readable name of a telemetry data field of a particular domain + * object + * @param {string} id The key associated with the domain object + * @param {string} property The telemetry field key to retrieve the type of + * @return {string} The telemetry field name + */ +ConditionManager.prototype.getTelemetryPropertyName = function (id, property) { + if (this.telemetryMetadataById[id] && this.telemetryMetadataById[id][property]) { + return this.telemetryMetadataById[id][property].name; + } +}; + +/** + * Returns the {ConditionEvaluator} instance associated with this condition + * manager + * @return {ConditionEvaluator} + */ +ConditionManager.prototype.getEvaluator = function () { + return this.evaluator; +}; + +/** + * Returns true if the initial composition load has completed + * @return {boolean} + */ +ConditionManager.prototype.loadCompleted = function () { + return this.loadComplete; +}; + +/** + * Returns true if the initial block metadata load has completed + */ +ConditionManager.prototype.metadataLoadCompleted = function () { + return this.metadataLoadComplete; +}; + +/** + * Triggers the telemetryReceive callbacks registered to this ConditionManager, + * used by the {TestDataManager} to force a rule evaluation when test data is + * enabled + */ +ConditionManager.prototype.triggerTelemetryCallback = function () { + this.eventEmitter.emit('receiveTelemetry'); +}; + +/** + * Unsubscribe from all registered telemetry sources and unregister all event + * listeners registered with the Open MCT APIs + */ +ConditionManager.prototype.destroy = function () { + Object.values(this.subscriptions).forEach(function (unsubscribeFunction) { + unsubscribeFunction(); + }); + this.composition.off('add', this.onCompositionAdd, this); + this.composition.off('remove', this.onCompositionRemove, this); + this.composition.off('load', this.onCompositionLoad, this); +}; diff --git a/src/plugins/summaryWidget/src/Rule.js b/src/plugins/summaryWidget/src/Rule.js index e4b53ad124c..5dc09aa6a2c 100644 --- a/src/plugins/summaryWidget/src/Rule.js +++ b/src/plugins/summaryWidget/src/Rule.js @@ -1,536 +1,529 @@ -define([ - '../res/ruleTemplate.html', - './Condition', - './input/ColorPalette', - './input/IconPalette', - './eventHelpers', - '../../../utils/template/templateHelpers', - 'EventEmitter', - 'lodash' -], function ( - ruleTemplate, - Condition, - ColorPalette, - IconPalette, - eventHelpers, - templateHelpers, - EventEmitter, - _ +import EventEmitter from 'EventEmitter'; +import _ from 'lodash'; + +import * as templateHelpers from '../../../utils/template/templateHelpers'; +import * as ruleTemplate from '../res/ruleTemplate.html'; +import Condition from './Condition'; +import eventHelpers from './eventHelpers'; +import ColorPalette from './input/ColorPalette'; +import IconPalette from './input/IconPalette'; + +/** + * An object representing a summary widget rule. Maintains a set of text + * and css properties for output, and a set of conditions for configuring + * when the rule will be applied to the summary widget. + * @constructor + * @param {Object} ruleConfig A JavaScript object representing the configuration of this rule + * @param {Object} domainObject The Summary Widget domain object which contains this rule + * @param {MCT} openmct An MCT instance + * @param {ConditionManager} conditionManager A ConditionManager instance + * @param {WidgetDnD} widgetDnD A WidgetDnD instance to handle dragging and dropping rules + * @param {element} container The DOM element which contains this summary widget + */ +export default function Rule( + ruleConfig, + domainObject, + openmct, + conditionManager, + widgetDnD, + container ) { - /** - * An object representing a summary widget rule. Maintains a set of text - * and css properties for output, and a set of conditions for configuring - * when the rule will be applied to the summary widget. - * @constructor - * @param {Object} ruleConfig A JavaScript object representing the configuration of this rule - * @param {Object} domainObject The Summary Widget domain object which contains this rule - * @param {MCT} openmct An MCT instance - * @param {ConditionManager} conditionManager A ConditionManager instance - * @param {WidgetDnD} widgetDnD A WidgetDnD instance to handle dragging and dropping rules - * @param {element} container The DOM element which contains this summary widget - */ - function Rule(ruleConfig, domainObject, openmct, conditionManager, widgetDnD, container) { - eventHelpers.extend(this); - const self = this; - const THUMB_ICON_CLASS = 'c-sw__icon js-sw__icon'; - - this.config = ruleConfig; - this.domainObject = domainObject; - this.openmct = openmct; - this.conditionManager = conditionManager; - this.widgetDnD = widgetDnD; - this.container = container; - - this.domElement = templateHelpers.convertTemplateToHTML(ruleTemplate)[0]; - this.eventEmitter = new EventEmitter(); - this.supportedCallbacks = ['remove', 'duplicate', 'change', 'conditionChange']; - this.conditions = []; - this.dragging = false; - - this.remove = this.remove.bind(this); - this.duplicate = this.duplicate.bind(this); - - this.thumbnail = this.domElement.querySelector('.t-widget-thumb'); - this.thumbnailIcon = this.domElement.querySelector('.js-sw__icon'); - this.thumbnailLabel = this.domElement.querySelector('.c-sw__label'); - this.title = this.domElement.querySelector('.rule-title'); - this.description = this.domElement.querySelector('.rule-description'); - this.trigger = this.domElement.querySelector('.t-trigger'); - this.toggleConfigButton = this.domElement.querySelector('.js-disclosure'); - this.configArea = this.domElement.querySelector('.widget-rule-content'); - this.grippy = this.domElement.querySelector('.t-grippy'); - this.conditionArea = this.domElement.querySelector('.t-widget-rule-config'); - this.jsConditionArea = this.domElement.querySelector('.t-rule-js-condition-input-holder'); - this.deleteButton = this.domElement.querySelector('.t-delete'); - this.duplicateButton = this.domElement.querySelector('.t-duplicate'); - this.addConditionButton = this.domElement.querySelector('.add-condition'); - - /** - * The text inputs for this rule: any input included in this object will - * have the appropriate event handlers registered to it, and it's corresponding - * field in the domain object will be updated with its value - */ - - this.textInputs = { - name: this.domElement.querySelector('.t-rule-name-input'), - label: this.domElement.querySelector('.t-rule-label-input'), - message: this.domElement.querySelector('.t-rule-message-input'), - jsCondition: this.domElement.querySelector('.t-rule-js-condition-input') - }; - - this.iconInput = new IconPalette('', container); - this.colorInputs = { - 'background-color': new ColorPalette('icon-paint-bucket', container), - 'border-color': new ColorPalette('icon-line-horz', container), - color: new ColorPalette('icon-font', container) - }; - - this.colorInputs.color.toggleNullOption(); - - /** - * An onchange event handler method for this rule's icon palettes - * @param {string} icon The css class name corresponding to this icon - * @private - */ - function onIconInput(icon) { - self.config.icon = icon; - self.updateDomainObject('icon', icon); - self.thumbnailIcon.className = `${THUMB_ICON_CLASS + ' ' + icon}`; - self.eventEmitter.emit('change'); - } - - /** - * An onchange event handler method for this rule's color palettes palettes - * @param {string} color The color selected in the palette - * @param {string} property The css property which this color corresponds to - * @private - */ - function onColorInput(color, property) { - self.config.style[property] = color; - self.thumbnail.style[property] = color; - self.eventEmitter.emit('change'); - } - - /** - * Parse input text from textbox to prevent HTML Injection - * @param {string} msg The text to be Parsed - * @private - */ - function encodeMsg(msg) { - const div = document.createElement('div'); - div.innerText = msg; - - return div.innerText; - } - - /** - * An onchange event handler method for this rule's trigger key - * @param {event} event The change event from this rule's select element - * @private - */ - function onTriggerInput(event) { - const elem = event.target; - self.config.trigger = encodeMsg(elem.value); - self.generateDescription(); - self.updateDomainObject(); - self.refreshConditions(); - self.eventEmitter.emit('conditionChange'); - } - - /** - * An onchange event handler method for this rule's text inputs - * @param {element} elem The input element that generated the event - * @param {string} inputKey The field of this rule's configuration to update - * @private - */ - function onTextInput(elem, inputKey) { - const text = encodeMsg(elem.value); - self.config[inputKey] = text; - self.updateDomainObject(); - if (inputKey === 'name') { - self.title.innerText = text; - } else if (inputKey === 'label') { - self.thumbnailLabel.innerText = text; - } - - self.eventEmitter.emit('change'); - } - - /** - * An onchange event handler for a mousedown event that initiates a drag gesture - * @param {event} event A mouseup event that was registered on this rule's grippy - * @private - */ - function onDragStart(event) { - document.querySelectorAll('.t-drag-indicator').forEach((indicator) => { - // eslint-disable-next-line no-invalid-this - const ruleHeader = self.domElement - .querySelectorAll('.widget-rule-header')[0] - .cloneNode(true); - indicator.innerHTML = ruleHeader; - }); - self.widgetDnD.setDragImage( - self.domElement.querySelectorAll('.widget-rule-header')[0].cloneNode(true) - ); - self.widgetDnD.dragStart(self.config.id); - self.domElement.style.display = 'none'; - } - - /** - * Show or hide this rule's configuration properties - * @private - */ - function toggleConfig() { - if (self.configArea.classList.contains('expanded')) { - self.configArea.classList.remove('expanded'); - } else { - self.configArea.classList.add('expanded'); - } - - if (self.toggleConfigButton.classList.contains('c-disclosure-triangle--expanded')) { - self.toggleConfigButton.classList.remove('c-disclosure-triangle--expanded'); - } else { - self.toggleConfigButton.classList.add('c-disclosure-triangle--expanded'); - } - - self.config.expanded = !self.config.expanded; - } - - const labelInput = this.domElement.querySelector('.t-rule-label-input'); - labelInput.parentNode.insertBefore(this.iconInput.getDOM(), labelInput); - this.iconInput.set(self.config.icon); - this.iconInput.on('change', function (value) { - onIconInput(value); - }); - - // Initialize thumbs when first loading - this.thumbnailIcon.className = `${THUMB_ICON_CLASS + ' ' + self.config.icon}`; - this.thumbnailLabel.innerText = self.config.label; - - Object.keys(this.colorInputs).forEach(function (inputKey) { - const input = self.colorInputs[inputKey]; - - input.set(self.config.style[inputKey]); - onColorInput(self.config.style[inputKey], inputKey); - - input.on('change', function (value) { - onColorInput(value, inputKey); - self.updateDomainObject(); - }); - - self.domElement.querySelector('.t-style-input').append(input.getDOM()); - }); - - Object.keys(this.textInputs).forEach(function (inputKey) { - if (self.textInputs[inputKey]) { - self.textInputs[inputKey].value = self.config[inputKey] || ''; - self.listenTo(self.textInputs[inputKey], 'input', function () { - // eslint-disable-next-line no-invalid-this - onTextInput(this, inputKey); - }); - } - }); - - this.listenTo(this.deleteButton, 'click', this.remove); - this.listenTo(this.duplicateButton, 'click', this.duplicate); - this.listenTo(this.addConditionButton, 'click', function () { - self.initCondition(); - }); - this.listenTo(this.toggleConfigButton, 'click', toggleConfig); - this.listenTo(this.trigger, 'change', onTriggerInput); - - this.title.innerHTML = self.config.name; - this.description.innerHTML = self.config.description; - this.trigger.value = self.config.trigger; - - this.listenTo(this.grippy, 'mousedown', onDragStart); - this.widgetDnD.on( - 'drop', - function () { - // eslint-disable-next-line no-invalid-this - this.domElement.show(); - document.querySelector('.t-drag-indicator').style.display = 'none'; - }, - this - ); - - if (!this.conditionManager.loadCompleted()) { - this.config.expanded = false; - } - - if (!this.config.expanded) { - this.configArea.classList.remove('expanded'); - this.toggleConfigButton.classList.remove('c-disclosure-triangle--expanded'); - } - - if (this.domainObject.configuration.ruleOrder.length === 2) { - this.domElement.querySelector('.t-grippy').style.display = 'none'; - } - - this.refreshConditions(); - - //if this is the default rule, hide elements that don't apply - if (this.config.id === 'default') { - this.domElement.querySelector('.t-delete').style.display = 'none'; - this.domElement.querySelector('.t-widget-rule-config').style.display = 'none'; - this.domElement.querySelector('.t-grippy').style.display = 'none'; - } - } + eventHelpers.extend(this); + const self = this; + const THUMB_ICON_CLASS = 'c-sw__icon js-sw__icon'; + + this.config = ruleConfig; + this.domainObject = domainObject; + this.openmct = openmct; + this.conditionManager = conditionManager; + this.widgetDnD = widgetDnD; + this.container = container; + + this.domElement = templateHelpers.convertTemplateToHTML(ruleTemplate)[0]; + this.eventEmitter = new EventEmitter(); + this.supportedCallbacks = ['remove', 'duplicate', 'change', 'conditionChange']; + this.conditions = []; + this.dragging = false; + + this.remove = this.remove.bind(this); + this.duplicate = this.duplicate.bind(this); + + this.thumbnail = this.domElement.querySelector('.t-widget-thumb'); + this.thumbnailIcon = this.domElement.querySelector('.js-sw__icon'); + this.thumbnailLabel = this.domElement.querySelector('.c-sw__label'); + this.title = this.domElement.querySelector('.rule-title'); + this.description = this.domElement.querySelector('.rule-description'); + this.trigger = this.domElement.querySelector('.t-trigger'); + this.toggleConfigButton = this.domElement.querySelector('.js-disclosure'); + this.configArea = this.domElement.querySelector('.widget-rule-content'); + this.grippy = this.domElement.querySelector('.t-grippy'); + this.conditionArea = this.domElement.querySelector('.t-widget-rule-config'); + this.jsConditionArea = this.domElement.querySelector('.t-rule-js-condition-input-holder'); + this.deleteButton = this.domElement.querySelector('.t-delete'); + this.duplicateButton = this.domElement.querySelector('.t-duplicate'); + this.addConditionButton = this.domElement.querySelector('.add-condition'); /** - * Return the DOM element representing this rule - * @return {Element} A DOM element + * The text inputs for this rule: any input included in this object will + * have the appropriate event handlers registered to it, and it's corresponding + * field in the domain object will be updated with its value */ - Rule.prototype.getDOM = function () { - return this.domElement; - }; - /** - * Unregister any event handlers registered with external sources - */ - Rule.prototype.destroy = function () { - Object.values(this.colorInputs).forEach(function (palette) { - palette.destroy(); - }); - this.iconInput.destroy(); - this.stopListening(); - this.conditions.forEach(function (condition) { - condition.destroy(); - }); + this.textInputs = { + name: this.domElement.querySelector('.t-rule-name-input'), + label: this.domElement.querySelector('.t-rule-label-input'), + message: this.domElement.querySelector('.t-rule-message-input'), + jsCondition: this.domElement.querySelector('.t-rule-js-condition-input') }; - /** - * Register a callback with this rule: supported callbacks are remove, change, - * conditionChange, and duplicate - * @param {string} event The key for the event to listen to - * @param {function} callback The function that this rule will invoke on this event - * @param {Object} context A reference to a scope to use as the context for - * context for the callback function - */ - Rule.prototype.on = function (event, callback, context) { - if (this.supportedCallbacks.includes(event)) { - this.eventEmitter.on(event, callback, context || this); - } + this.iconInput = new IconPalette('', container); + this.colorInputs = { + 'background-color': new ColorPalette('icon-paint-bucket', container), + 'border-color': new ColorPalette('icon-line-horz', container), + color: new ColorPalette('icon-font', container) }; + this.colorInputs.color.toggleNullOption(); + /** - * An event handler for when a condition's configuration is modified - * @param {} value - * @param {string} property The path in the configuration to updateDomainObject - * @param {number} index The index of the condition that initiated this change + * An onchange event handler method for this rule's icon palettes + * @param {string} icon The css class name corresponding to this icon + * @private */ - Rule.prototype.onConditionChange = function (event) { - _.set(this.config.conditions[event.index], event.property, event.value); - this.generateDescription(); - this.updateDomainObject(); - this.eventEmitter.emit('conditionChange'); - }; + function onIconInput(icon) { + self.config.icon = icon; + self.updateDomainObject('icon', icon); + self.thumbnailIcon.className = `${THUMB_ICON_CLASS + ' ' + icon}`; + self.eventEmitter.emit('change'); + } /** - * During a rule drag event, show the placeholder element after this rule + * An onchange event handler method for this rule's color palettes palettes + * @param {string} color The color selected in the palette + * @param {string} property The css property which this color corresponds to + * @private */ - Rule.prototype.showDragIndicator = function () { - document.querySelector('.t-drag-indicator').style.display = 'none'; - this.domElement.querySelector('.t-drag-indicator').style.display = ''; - }; + function onColorInput(color, property) { + self.config.style[property] = color; + self.thumbnail.style[property] = color; + self.eventEmitter.emit('change'); + } /** - * Mutate the domain object with this rule's local configuration + * Parse input text from textbox to prevent HTML Injection + * @param {string} msg The text to be Parsed + * @private */ - Rule.prototype.updateDomainObject = function () { - this.openmct.objects.mutate( - this.domainObject, - 'configuration.ruleConfigById.' + this.config.id, - this.config - ); - }; + function encodeMsg(msg) { + const div = document.createElement('div'); + div.innerText = msg; + + return div.innerText; + } /** - * Get a property of this rule by key - * @param {string} prop They property key of this rule to get - * @return {} The queried property + * An onchange event handler method for this rule's trigger key + * @param {event} event The change event from this rule's select element + * @private */ - Rule.prototype.getProperty = function (prop) { - return this.config[prop]; - }; + function onTriggerInput(event) { + const elem = event.target; + self.config.trigger = encodeMsg(elem.value); + self.generateDescription(); + self.updateDomainObject(); + self.refreshConditions(); + self.eventEmitter.emit('conditionChange'); + } /** - * Remove this rule from the domain object's configuration and invoke any - * registered remove callbacks + * An onchange event handler method for this rule's text inputs + * @param {element} elem The input element that generated the event + * @param {string} inputKey The field of this rule's configuration to update + * @private */ - Rule.prototype.remove = function () { - const ruleOrder = this.domainObject.configuration.ruleOrder; - const ruleConfigById = this.domainObject.configuration.ruleConfigById; - const self = this; - - ruleConfigById[self.config.id] = undefined; - _.remove(ruleOrder, function (ruleId) { - return ruleId === self.config.id; - }); + function onTextInput(elem, inputKey) { + const text = encodeMsg(elem.value); + self.config[inputKey] = text; + self.updateDomainObject(); + if (inputKey === 'name') { + self.title.innerText = text; + } else if (inputKey === 'label') { + self.thumbnailLabel.innerText = text; + } - this.openmct.objects.mutate(this.domainObject, 'configuration.ruleConfigById', ruleConfigById); - this.openmct.objects.mutate(this.domainObject, 'configuration.ruleOrder', ruleOrder); - this.destroy(); - this.eventEmitter.emit('remove'); - }; + self.eventEmitter.emit('change'); + } /** - * Makes a deep clone of this rule's configuration, and calls the duplicate event - * callback with the cloned configuration as an argument if one has been registered + * An onchange event handler for a mousedown event that initiates a drag gesture + * @param {event} event A mouseup event that was registered on this rule's grippy + * @private */ - Rule.prototype.duplicate = function () { - const sourceRule = JSON.parse(JSON.stringify(this.config)); - sourceRule.expanded = true; - this.eventEmitter.emit('duplicate', sourceRule); - }; + function onDragStart(event) { + document.querySelectorAll('.t-drag-indicator').forEach((indicator) => { + // eslint-disable-next-line no-invalid-this + const ruleHeader = self.domElement.querySelectorAll('.widget-rule-header')[0].cloneNode(true); + indicator.innerHTML = ruleHeader; + }); + self.widgetDnD.setDragImage( + self.domElement.querySelectorAll('.widget-rule-header')[0].cloneNode(true) + ); + self.widgetDnD.dragStart(self.config.id); + self.domElement.style.display = 'none'; + } /** - * Initialize a new condition. If called with the sourceConfig and sourceIndex arguments, - * will insert a new condition with the provided configuration after the sourceIndex - * index. Otherwise, initializes a new blank rule and inserts it at the end - * of the list. - * @param {Object} [config] The configuration to initialize this rule from, - * consisting of sourceCondition and index fields + * Show or hide this rule's configuration properties + * @private */ - Rule.prototype.initCondition = function (config) { - const ruleConfigById = this.domainObject.configuration.ruleConfigById; - let newConfig; - const sourceIndex = config && config.index; - const defaultConfig = { - object: '', - key: '', - operation: '', - values: [] - }; - - newConfig = config !== undefined ? config.sourceCondition : defaultConfig; - if (sourceIndex !== undefined) { - ruleConfigById[this.config.id].conditions.splice(sourceIndex + 1, 0, newConfig); + function toggleConfig() { + if (self.configArea.classList.contains('expanded')) { + self.configArea.classList.remove('expanded'); } else { - ruleConfigById[this.config.id].conditions.push(newConfig); + self.configArea.classList.add('expanded'); } - this.domainObject.configuration.ruleConfigById = ruleConfigById; - this.updateDomainObject(); - this.refreshConditions(); - this.generateDescription(); - }; + if (self.toggleConfigButton.classList.contains('c-disclosure-triangle--expanded')) { + self.toggleConfigButton.classList.remove('c-disclosure-triangle--expanded'); + } else { + self.toggleConfigButton.classList.add('c-disclosure-triangle--expanded'); + } - /** - * Build {Condition} objects from configuration and rebuild associated view - */ - Rule.prototype.refreshConditions = function () { - const self = this; - let $condition = null; - let loopCnt = 0; - const triggerContextStr = self.config.trigger === 'any' ? ' or ' : ' and '; + self.config.expanded = !self.config.expanded; + } - self.conditions = []; + const labelInput = this.domElement.querySelector('.t-rule-label-input'); + labelInput.parentNode.insertBefore(this.iconInput.getDOM(), labelInput); + this.iconInput.set(self.config.icon); + this.iconInput.on('change', function (value) { + onIconInput(value); + }); - this.domElement.querySelectorAll('.t-condition').forEach((condition) => { - condition.remove(); - }); + // Initialize thumbs when first loading + this.thumbnailIcon.className = `${THUMB_ICON_CLASS + ' ' + self.config.icon}`; + this.thumbnailLabel.innerText = self.config.label; - this.config.conditions.forEach(function (condition, index) { - const newCondition = new Condition(condition, index, self.conditionManager); - newCondition.on('remove', self.removeCondition, self); - newCondition.on('duplicate', self.initCondition, self); - newCondition.on('change', self.onConditionChange, self); - self.conditions.push(newCondition); - }); + Object.keys(this.colorInputs).forEach(function (inputKey) { + const input = self.colorInputs[inputKey]; - if (this.config.trigger === 'js') { - if (this.jsConditionArea) { - this.jsConditionArea.style.display = ''; - } + input.set(self.config.style[inputKey]); + onColorInput(self.config.style[inputKey], inputKey); - this.addConditionButton.style.display = 'none'; - } else { - if (this.jsConditionArea) { - this.jsConditionArea.style.display = 'none'; - } + input.on('change', function (value) { + onColorInput(value, inputKey); + self.updateDomainObject(); + }); - this.addConditionButton.style.display = ''; - self.conditions.forEach(function (condition) { - $condition = condition.getDOM(); - const lastOfType = self.conditionArea.querySelector('li:last-of-type'); - lastOfType.parentNode.insertBefore($condition, lastOfType); - if (loopCnt > 0) { - $condition.querySelector('.t-condition-context').innerHTML = triggerContextStr + ' when'; - } + self.domElement.querySelector('.t-style-input').append(input.getDOM()); + }); - loopCnt++; + Object.keys(this.textInputs).forEach(function (inputKey) { + if (self.textInputs[inputKey]) { + self.textInputs[inputKey].value = self.config[inputKey] || ''; + self.listenTo(self.textInputs[inputKey], 'input', function () { + // eslint-disable-next-line no-invalid-this + onTextInput(this, inputKey); }); } + }); + + this.listenTo(this.deleteButton, 'click', this.remove); + this.listenTo(this.duplicateButton, 'click', this.duplicate); + this.listenTo(this.addConditionButton, 'click', function () { + self.initCondition(); + }); + this.listenTo(this.toggleConfigButton, 'click', toggleConfig); + this.listenTo(this.trigger, 'change', onTriggerInput); + + this.title.innerHTML = self.config.name; + this.description.innerHTML = self.config.description; + this.trigger.value = self.config.trigger; + + this.listenTo(this.grippy, 'mousedown', onDragStart); + this.widgetDnD.on( + 'drop', + function () { + // eslint-disable-next-line no-invalid-this + this.domElement.show(); + document.querySelector('.t-drag-indicator').style.display = 'none'; + }, + this + ); + + if (!this.conditionManager.loadCompleted()) { + this.config.expanded = false; + } - if (self.conditions.length === 1) { - self.conditions[0].hideButtons(); - } - }; + if (!this.config.expanded) { + this.configArea.classList.remove('expanded'); + this.toggleConfigButton.classList.remove('c-disclosure-triangle--expanded'); + } - /** - * Remove a condition from this rule's configuration at the given index - * @param {number} removeIndex The index of the condition to remove - */ - Rule.prototype.removeCondition = function (removeIndex) { - const ruleConfigById = this.domainObject.configuration.ruleConfigById; - const conditions = ruleConfigById[this.config.id].conditions; + if (this.domainObject.configuration.ruleOrder.length === 2) { + this.domElement.querySelector('.t-grippy').style.display = 'none'; + } - _.remove(conditions, function (condition, index) { - return index === removeIndex; - }); + this.refreshConditions(); - this.domainObject.configuration.ruleConfigById[this.config.id] = this.config; - this.updateDomainObject(); - this.refreshConditions(); - this.generateDescription(); - this.eventEmitter.emit('conditionChange'); + //if this is the default rule, hide elements that don't apply + if (this.config.id === 'default') { + this.domElement.querySelector('.t-delete').style.display = 'none'; + this.domElement.querySelector('.t-widget-rule-config').style.display = 'none'; + this.domElement.querySelector('.t-grippy').style.display = 'none'; + } +} + +/** + * Return the DOM element representing this rule + * @return {Element} A DOM element + */ +Rule.prototype.getDOM = function () { + return this.domElement; +}; + +/** + * Unregister any event handlers registered with external sources + */ +Rule.prototype.destroy = function () { + Object.values(this.colorInputs).forEach(function (palette) { + palette.destroy(); + }); + this.iconInput.destroy(); + this.stopListening(); + this.conditions.forEach(function (condition) { + condition.destroy(); + }); +}; + +/** + * Register a callback with this rule: supported callbacks are remove, change, + * conditionChange, and duplicate + * @param {string} event The key for the event to listen to + * @param {function} callback The function that this rule will invoke on this event + * @param {Object} context A reference to a scope to use as the context for + * context for the callback function + */ +Rule.prototype.on = function (event, callback, context) { + if (this.supportedCallbacks.includes(event)) { + this.eventEmitter.on(event, callback, context || this); + } +}; + +/** + * An event handler for when a condition's configuration is modified + * @param {} value + * @param {string} property The path in the configuration to updateDomainObject + * @param {number} index The index of the condition that initiated this change + */ +Rule.prototype.onConditionChange = function (event) { + _.set(this.config.conditions[event.index], event.property, event.value); + this.generateDescription(); + this.updateDomainObject(); + this.eventEmitter.emit('conditionChange'); +}; + +/** + * During a rule drag event, show the placeholder element after this rule + */ +Rule.prototype.showDragIndicator = function () { + document.querySelector('.t-drag-indicator').style.display = 'none'; + this.domElement.querySelector('.t-drag-indicator').style.display = ''; +}; + +/** + * Mutate the domain object with this rule's local configuration + */ +Rule.prototype.updateDomainObject = function () { + this.openmct.objects.mutate( + this.domainObject, + 'configuration.ruleConfigById.' + this.config.id, + this.config + ); +}; + +/** + * Get a property of this rule by key + * @param {string} prop They property key of this rule to get + * @return {} The queried property + */ +Rule.prototype.getProperty = function (prop) { + return this.config[prop]; +}; + +/** + * Remove this rule from the domain object's configuration and invoke any + * registered remove callbacks + */ +Rule.prototype.remove = function () { + const ruleOrder = this.domainObject.configuration.ruleOrder; + const ruleConfigById = this.domainObject.configuration.ruleConfigById; + const self = this; + + ruleConfigById[self.config.id] = undefined; + _.remove(ruleOrder, function (ruleId) { + return ruleId === self.config.id; + }); + + this.openmct.objects.mutate(this.domainObject, 'configuration.ruleConfigById', ruleConfigById); + this.openmct.objects.mutate(this.domainObject, 'configuration.ruleOrder', ruleOrder); + this.destroy(); + this.eventEmitter.emit('remove'); +}; + +/** + * Makes a deep clone of this rule's configuration, and calls the duplicate event + * callback with the cloned configuration as an argument if one has been registered + */ +Rule.prototype.duplicate = function () { + const sourceRule = JSON.parse(JSON.stringify(this.config)); + sourceRule.expanded = true; + this.eventEmitter.emit('duplicate', sourceRule); +}; + +/** + * Initialize a new condition. If called with the sourceConfig and sourceIndex arguments, + * will insert a new condition with the provided configuration after the sourceIndex + * index. Otherwise, initializes a new blank rule and inserts it at the end + * of the list. + * @param {Object} [config] The configuration to initialize this rule from, + * consisting of sourceCondition and index fields + */ +Rule.prototype.initCondition = function (config) { + const ruleConfigById = this.domainObject.configuration.ruleConfigById; + let newConfig; + const sourceIndex = config && config.index; + const defaultConfig = { + object: '', + key: '', + operation: '', + values: [] }; - /** - * Build a human-readable description from this rule's conditions - */ - Rule.prototype.generateDescription = function () { - let description = ''; - const manager = this.conditionManager; - const evaluator = manager.getEvaluator(); - let name; - let property; - let operation; - const self = this; - - if (this.config.conditions && this.config.id !== 'default') { - if (self.config.trigger === 'js') { - description = 'when a custom JavaScript condition evaluates to true'; - } else { - this.config.conditions.forEach(function (condition, index) { - name = manager.getObjectName(condition.object); - property = manager.getTelemetryPropertyName(condition.object, condition.key); - operation = evaluator.getOperationDescription(condition.operation, condition.values); - if (name || property || operation) { - description += - 'when ' + - (name ? name + "'s " : '') + - (property ? property + ' ' : '') + - (operation ? operation + ' ' : '') + - (self.config.trigger === 'any' ? ' OR ' : ' AND '); - } - }); - } + newConfig = config !== undefined ? config.sourceCondition : defaultConfig; + if (sourceIndex !== undefined) { + ruleConfigById[this.config.id].conditions.splice(sourceIndex + 1, 0, newConfig); + } else { + ruleConfigById[this.config.id].conditions.push(newConfig); + } + + this.domainObject.configuration.ruleConfigById = ruleConfigById; + this.updateDomainObject(); + this.refreshConditions(); + this.generateDescription(); +}; + +/** + * Build {Condition} objects from configuration and rebuild associated view + */ +Rule.prototype.refreshConditions = function () { + const self = this; + let $condition = null; + let loopCnt = 0; + const triggerContextStr = self.config.trigger === 'any' ? ' or ' : ' and '; + + self.conditions = []; + + this.domElement.querySelectorAll('.t-condition').forEach((condition) => { + condition.remove(); + }); + + this.config.conditions.forEach(function (condition, index) { + const newCondition = new Condition(condition, index, self.conditionManager); + newCondition.on('remove', self.removeCondition, self); + newCondition.on('duplicate', self.initCondition, self); + newCondition.on('change', self.onConditionChange, self); + self.conditions.push(newCondition); + }); + + if (this.config.trigger === 'js') { + if (this.jsConditionArea) { + this.jsConditionArea.style.display = ''; } - if (description.endsWith('OR ')) { - description = description.substring(0, description.length - 3); + this.addConditionButton.style.display = 'none'; + } else { + if (this.jsConditionArea) { + this.jsConditionArea.style.display = 'none'; } - if (description.endsWith('AND ')) { - description = description.substring(0, description.length - 4); + this.addConditionButton.style.display = ''; + self.conditions.forEach(function (condition) { + $condition = condition.getDOM(); + const lastOfType = self.conditionArea.querySelector('li:last-of-type'); + lastOfType.parentNode.insertBefore($condition, lastOfType); + if (loopCnt > 0) { + $condition.querySelector('.t-condition-context').innerHTML = triggerContextStr + ' when'; + } + + loopCnt++; + }); + } + + if (self.conditions.length === 1) { + self.conditions[0].hideButtons(); + } +}; + +/** + * Remove a condition from this rule's configuration at the given index + * @param {number} removeIndex The index of the condition to remove + */ +Rule.prototype.removeCondition = function (removeIndex) { + const ruleConfigById = this.domainObject.configuration.ruleConfigById; + const conditions = ruleConfigById[this.config.id].conditions; + + _.remove(conditions, function (condition, index) { + return index === removeIndex; + }); + + this.domainObject.configuration.ruleConfigById[this.config.id] = this.config; + this.updateDomainObject(); + this.refreshConditions(); + this.generateDescription(); + this.eventEmitter.emit('conditionChange'); +}; + +/** + * Build a human-readable description from this rule's conditions + */ +Rule.prototype.generateDescription = function () { + let description = ''; + const manager = this.conditionManager; + const evaluator = manager.getEvaluator(); + let name; + let property; + let operation; + const self = this; + + if (this.config.conditions && this.config.id !== 'default') { + if (self.config.trigger === 'js') { + description = 'when a custom JavaScript condition evaluates to true'; + } else { + this.config.conditions.forEach(function (condition, index) { + name = manager.getObjectName(condition.object); + property = manager.getTelemetryPropertyName(condition.object, condition.key); + operation = evaluator.getOperationDescription(condition.operation, condition.values); + if (name || property || operation) { + description += + 'when ' + + (name ? name + "'s " : '') + + (property ? property + ' ' : '') + + (operation ? operation + ' ' : '') + + (self.config.trigger === 'any' ? ' OR ' : ' AND '); + } + }); } + } - description = description === '' ? this.config.description : description; - this.description.innerHTML = self.config.description; - this.config.description = description; - }; + if (description.endsWith('OR ')) { + description = description.substring(0, description.length - 3); + } + + if (description.endsWith('AND ')) { + description = description.substring(0, description.length - 4); + } - return Rule; -}); + description = description === '' ? this.config.description : description; + this.description.innerHTML = self.config.description; + this.config.description = description; +}; diff --git a/src/plugins/summaryWidget/src/SummaryWidget.js b/src/plugins/summaryWidget/src/SummaryWidget.js index 84c3b7c5f21..4229787d219 100644 --- a/src/plugins/summaryWidget/src/SummaryWidget.js +++ b/src/plugins/summaryWidget/src/SummaryWidget.js @@ -1,412 +1,392 @@ -define([ - '../res/widgetTemplate.html', - './Rule', - './ConditionManager', - './TestDataManager', - './WidgetDnD', - './eventHelpers', - '../../../utils/template/templateHelpers', - 'objectUtils', - 'lodash', - '@braintree/sanitize-url' -], function ( - widgetTemplate, - Rule, - ConditionManager, - TestDataManager, - WidgetDnD, - eventHelpers, - templateHelpers, - objectUtils, - _, - urlSanitizeLib -) { - //default css configuration for new rules - const DEFAULT_PROPS = { - color: '#cccccc', - 'background-color': '#666666', - 'border-color': 'rgba(0,0,0,0)' - }; +import * as urlSanitizeLib from '@braintree/sanitize-url'; + +import * as templateHelpers from '../../../utils/template/templateHelpers'; +import widgetTemplate from '../res/widgetTemplate.html'; +import ConditionManager from './ConditionManager'; +import eventHelpers from './eventHelpers'; +import Rule from './Rule'; +import TestDataManager from './TestDataManager'; +import WidgetDnD from './WidgetDnD'; + +//default css configuration for new rules +const DEFAULT_PROPS = { + color: '#cccccc', + 'background-color': '#666666', + 'border-color': 'rgba(0,0,0,0)' +}; + +/** + * A Summary Widget object, which allows a user to configure rules based + * on telemetry producing domain objects, and update a compact display + * accordingly. + * @constructor + * @param {Object} domainObject The domain Object represented by this Widget + * @param {MCT} openmct An MCT instance + */ +export default function SummaryWidget(domainObject, openmct) { + eventHelpers.extend(this); + + this.domainObject = domainObject; + this.openmct = openmct; + + this.domainObject.configuration = this.domainObject.configuration || {}; + this.domainObject.configuration.ruleConfigById = + this.domainObject.configuration.ruleConfigById || {}; + this.domainObject.configuration.ruleOrder = this.domainObject.configuration.ruleOrder || [ + 'default' + ]; + this.domainObject.configuration.testDataConfig = this.domainObject.configuration + .testDataConfig || [ + { + object: '', + key: '', + value: '' + } + ]; - /** - * A Summary Widget object, which allows a user to configure rules based - * on telemetry producing domain objects, and update a compact display - * accordingly. - * @constructor - * @param {Object} domainObject The domain Object represented by this Widget - * @param {MCT} openmct An MCT instance - */ - function SummaryWidget(domainObject, openmct) { - eventHelpers.extend(this); - - this.domainObject = domainObject; - this.openmct = openmct; - - this.domainObject.configuration = this.domainObject.configuration || {}; - this.domainObject.configuration.ruleConfigById = - this.domainObject.configuration.ruleConfigById || {}; - this.domainObject.configuration.ruleOrder = this.domainObject.configuration.ruleOrder || [ - 'default' - ]; - this.domainObject.configuration.testDataConfig = this.domainObject.configuration - .testDataConfig || [ - { - object: '', - key: '', - value: '' - } - ]; - - this.activeId = 'default'; - this.rulesById = {}; - this.domElement = templateHelpers.convertTemplateToHTML(widgetTemplate)[0]; - this.toggleRulesControl = this.domElement.querySelector('.t-view-control-rules'); - this.toggleTestDataControl = this.domElement.querySelector('.t-view-control-test-data'); - - this.widgetButton = this.domElement.querySelector(':scope > #widget'); - - this.editing = false; - this.container = ''; - this.editListenerUnsubscribe = () => {}; - - this.outerWrapper = this.domElement.querySelector('.widget-edit-holder'); - this.ruleArea = this.domElement.querySelector('#ruleArea'); - this.configAreaRules = this.domElement.querySelector('.widget-rules-wrapper'); - - this.testDataArea = this.domElement.querySelector('.widget-test-data'); - this.addRuleButton = this.domElement.querySelector('#addRule'); - - this.conditionManager = new ConditionManager(this.domainObject, this.openmct); - this.testDataManager = new TestDataManager( - this.domainObject, - this.conditionManager, - this.openmct - ); - - this.watchForChanges = this.watchForChanges.bind(this); - this.show = this.show.bind(this); - this.destroy = this.destroy.bind(this); - this.addRule = this.addRule.bind(this); - - this.addHyperlink(domainObject.url, domainObject.openNewTab); - this.watchForChanges(openmct, domainObject); - - const self = this; - - /** - * Toggles the configuration area for test data in the view - * @private - */ - function toggleTestData() { - if (self.outerWrapper.classList.contains('expanded-widget-test-data')) { - self.outerWrapper.classList.remove('expanded-widget-test-data'); - } else { - self.outerWrapper.classList.add('expanded-widget-test-data'); - } + this.activeId = 'default'; + this.rulesById = {}; + this.domElement = templateHelpers.convertTemplateToHTML(widgetTemplate)[0]; + this.toggleRulesControl = this.domElement.querySelector('.t-view-control-rules'); + this.toggleTestDataControl = this.domElement.querySelector('.t-view-control-test-data'); - if (self.toggleTestDataControl.classList.contains('c-disclosure-triangle--expanded')) { - self.toggleTestDataControl.classList.remove('c-disclosure-triangle--expanded'); - } else { - self.toggleTestDataControl.classList.add('c-disclosure-triangle--expanded'); - } - } + this.widgetButton = this.domElement.querySelector(':scope > #widget'); - this.listenTo(this.toggleTestDataControl, 'click', toggleTestData); + this.editing = false; + this.container = ''; + this.editListenerUnsubscribe = () => {}; - /** - * Toggles the configuration area for rules in the view - * @private - */ - function toggleRules() { - templateHelpers.toggleClass(self.outerWrapper, 'expanded-widget-rules'); - templateHelpers.toggleClass(self.toggleRulesControl, 'c-disclosure-triangle--expanded'); - } + this.outerWrapper = this.domElement.querySelector('.widget-edit-holder'); + this.ruleArea = this.domElement.querySelector('#ruleArea'); + this.configAreaRules = this.domElement.querySelector('.widget-rules-wrapper'); - this.listenTo(this.toggleRulesControl, 'click', toggleRules); - } + this.testDataArea = this.domElement.querySelector('.widget-test-data'); + this.addRuleButton = this.domElement.querySelector('#addRule'); + + this.conditionManager = new ConditionManager(this.domainObject, this.openmct); + this.testDataManager = new TestDataManager( + this.domainObject, + this.conditionManager, + this.openmct + ); + + this.watchForChanges = this.watchForChanges.bind(this); + this.show = this.show.bind(this); + this.destroy = this.destroy.bind(this); + this.addRule = this.addRule.bind(this); + + this.addHyperlink(domainObject.url, domainObject.openNewTab); + this.watchForChanges(openmct, domainObject); + + const self = this; /** - * adds or removes href to widget button and adds or removes openInNewTab - * @param {string} url String that denotes the url to be opened - * @param {string} openNewTab String that denotes wether to open link in new tab or not + * Toggles the configuration area for test data in the view + * @private */ - SummaryWidget.prototype.addHyperlink = function (url, openNewTab) { - if (url) { - this.widgetButton.href = urlSanitizeLib.sanitizeUrl(url); + function toggleTestData() { + if (self.outerWrapper.classList.contains('expanded-widget-test-data')) { + self.outerWrapper.classList.remove('expanded-widget-test-data'); } else { - this.widgetButton.removeAttribute('href'); + self.outerWrapper.classList.add('expanded-widget-test-data'); } - if (openNewTab === 'newTab') { - this.widgetButton.target = '_blank'; + if (self.toggleTestDataControl.classList.contains('c-disclosure-triangle--expanded')) { + self.toggleTestDataControl.classList.remove('c-disclosure-triangle--expanded'); } else { - this.widgetButton.removeAttribute('target'); + self.toggleTestDataControl.classList.add('c-disclosure-triangle--expanded'); } - }; + } - /** - * adds a listener to the object to watch for any changes made by user - * only executes if changes are observed - * @param {openmct} Object Instance of OpenMCT - * @param {domainObject} Object instance of this object - */ - SummaryWidget.prototype.watchForChanges = function (openmct, domainObject) { - this.watchForChangesUnsubscribe = openmct.objects.observe( - domainObject, - '*', - function (newDomainObject) { - if ( - newDomainObject.url !== this.domainObject.url || - newDomainObject.openNewTab !== this.domainObject.openNewTab - ) { - this.addHyperlink(newDomainObject.url, newDomainObject.openNewTab); - } - }.bind(this) - ); - }; + this.listenTo(this.toggleTestDataControl, 'click', toggleTestData); /** - * Builds the Summary Widget's DOM, performs other necessary setup, and attaches - * this Summary Widget's view to the supplied container. - * @param {element} container The DOM element that will contain this Summary - * Widget's view. + * Toggles the configuration area for rules in the view + * @private */ - SummaryWidget.prototype.show = function (container) { - const self = this; - this.container = container; - this.container.append(this.domElement); - this.domElement.querySelector('.widget-test-data').append(this.testDataManager.getDOM()); - this.widgetDnD = new WidgetDnD( - this.domElement, - this.domainObject.configuration.ruleOrder, - this.rulesById - ); - this.initRule('default', 'Default'); - this.domainObject.configuration.ruleOrder.forEach(function (ruleId) { - if (ruleId !== 'default') { - self.initRule(ruleId); - } - }); - this.refreshRules(); - this.updateWidget(); - - this.listenTo(this.addRuleButton, 'click', this.addRule); - this.conditionManager.on('receiveTelemetry', this.executeRules, this); - this.widgetDnD.on('drop', this.reorder, this); - }; + function toggleRules() { + templateHelpers.toggleClass(self.outerWrapper, 'expanded-widget-rules'); + templateHelpers.toggleClass(self.toggleRulesControl, 'c-disclosure-triangle--expanded'); + } - /** - * Unregister event listeners with the Open MCT APIs, unsubscribe from telemetry, - * and clean up event handlers - */ - SummaryWidget.prototype.destroy = function (container) { - this.editListenerUnsubscribe(); - this.conditionManager.destroy(); - this.testDataManager.destroy(); - this.widgetDnD.destroy(); - this.watchForChangesUnsubscribe(); - Object.values(this.rulesById).forEach(function (rule) { - rule.destroy(); - }); - - this.stopListening(); - }; + this.listenTo(this.toggleRulesControl, 'click', toggleRules); +} + +/** + * adds or removes href to widget button and adds or removes openInNewTab + * @param {string} url String that denotes the url to be opened + * @param {string} openNewTab String that denotes wether to open link in new tab or not + */ +SummaryWidget.prototype.addHyperlink = function (url, openNewTab) { + if (url) { + this.widgetButton.href = urlSanitizeLib.sanitizeUrl(url); + } else { + this.widgetButton.removeAttribute('href'); + } - /** - * Update the view from the current rule configuration and order - */ - SummaryWidget.prototype.refreshRules = function () { - const self = this; - const ruleOrder = self.domainObject.configuration.ruleOrder; - const rules = self.rulesById; - self.ruleArea.innerHTML = ''; - Object.values(ruleOrder).forEach(function (ruleId) { - self.ruleArea.append(rules[ruleId].getDOM()); - }); - - this.executeRules(); - this.addOrRemoveDragIndicator(); - }; - - SummaryWidget.prototype.addOrRemoveDragIndicator = function () { - const rules = this.domainObject.configuration.ruleOrder; - const rulesById = this.rulesById; - - rules.forEach(function (ruleKey, index, array) { - if (array.length > 2 && index > 0) { - rulesById[ruleKey].domElement.querySelector('.t-grippy').style.display = ''; - } else { - rulesById[ruleKey].domElement.querySelector('.t-grippy').style.display = 'none'; + if (openNewTab === 'newTab') { + this.widgetButton.target = '_blank'; + } else { + this.widgetButton.removeAttribute('target'); + } +}; + +/** + * adds a listener to the object to watch for any changes made by user + * only executes if changes are observed + * @param {openmct} Object Instance of OpenMCT + * @param {domainObject} Object instance of this object + */ +SummaryWidget.prototype.watchForChanges = function (openmct, domainObject) { + this.watchForChangesUnsubscribe = openmct.objects.observe( + domainObject, + '*', + function (newDomainObject) { + if ( + newDomainObject.url !== this.domainObject.url || + newDomainObject.openNewTab !== this.domainObject.openNewTab + ) { + this.addHyperlink(newDomainObject.url, newDomainObject.openNewTab); } - }); - }; - - /** - * Update the widget's appearance from the configuration of the active rule - */ - SummaryWidget.prototype.updateWidget = function () { - const WIDGET_ICON_CLASS = 'c-sw__icon js-sw__icon'; - const activeRule = this.rulesById[this.activeId]; - this.applyStyle(this.domElement.querySelector('#widget'), activeRule.getProperty('style')); - this.domElement.querySelector('#widget').title = activeRule.getProperty('message'); - this.domElement.querySelector('#widgetLabel').innerHTML = activeRule.getProperty('label'); - this.domElement.querySelector('#widgetIcon').classList = - WIDGET_ICON_CLASS + ' ' + activeRule.getProperty('icon'); - }; - - /** - * Get the active rule and update the Widget's appearance. - */ - SummaryWidget.prototype.executeRules = function () { - this.activeId = this.conditionManager.executeRules( - this.domainObject.configuration.ruleOrder, - this.rulesById - ); - this.updateWidget(); - }; - - /** - * Add a new rule to this widget - */ - SummaryWidget.prototype.addRule = function () { - let ruleCount = 0; - let ruleId; - const ruleOrder = this.domainObject.configuration.ruleOrder; - - while (Object.keys(this.rulesById).includes('rule' + ruleCount)) { - ruleCount++; + }.bind(this) + ); +}; + +/** + * Builds the Summary Widget's DOM, performs other necessary setup, and attaches + * this Summary Widget's view to the supplied container. + * @param {element} container The DOM element that will contain this Summary + * Widget's view. + */ +SummaryWidget.prototype.show = function (container) { + const self = this; + this.container = container; + this.container.append(this.domElement); + this.domElement.querySelector('.widget-test-data').append(this.testDataManager.getDOM()); + this.widgetDnD = new WidgetDnD( + this.domElement, + this.domainObject.configuration.ruleOrder, + this.rulesById + ); + this.initRule('default', 'Default'); + this.domainObject.configuration.ruleOrder.forEach(function (ruleId) { + if (ruleId !== 'default') { + self.initRule(ruleId); } + }); + this.refreshRules(); + this.updateWidget(); + + this.listenTo(this.addRuleButton, 'click', this.addRule); + this.conditionManager.on('receiveTelemetry', this.executeRules, this); + this.widgetDnD.on('drop', this.reorder, this); +}; + +/** + * Unregister event listeners with the Open MCT APIs, unsubscribe from telemetry, + * and clean up event handlers + */ +SummaryWidget.prototype.destroy = function (container) { + this.editListenerUnsubscribe(); + this.conditionManager.destroy(); + this.testDataManager.destroy(); + this.widgetDnD.destroy(); + this.watchForChangesUnsubscribe(); + Object.values(this.rulesById).forEach(function (rule) { + rule.destroy(); + }); + + this.stopListening(); +}; + +/** + * Update the view from the current rule configuration and order + */ +SummaryWidget.prototype.refreshRules = function () { + const self = this; + const ruleOrder = self.domainObject.configuration.ruleOrder; + const rules = self.rulesById; + self.ruleArea.innerHTML = ''; + Object.values(ruleOrder).forEach(function (ruleId) { + self.ruleArea.append(rules[ruleId].getDOM()); + }); + + this.executeRules(); + this.addOrRemoveDragIndicator(); +}; + +SummaryWidget.prototype.addOrRemoveDragIndicator = function () { + const rules = this.domainObject.configuration.ruleOrder; + const rulesById = this.rulesById; + + rules.forEach(function (ruleKey, index, array) { + if (array.length > 2 && index > 0) { + rulesById[ruleKey].domElement.querySelector('.t-grippy').style.display = ''; + } else { + rulesById[ruleKey].domElement.querySelector('.t-grippy').style.display = 'none'; + } + }); +}; + +/** + * Update the widget's appearance from the configuration of the active rule + */ +SummaryWidget.prototype.updateWidget = function () { + const WIDGET_ICON_CLASS = 'c-sw__icon js-sw__icon'; + const activeRule = this.rulesById[this.activeId]; + this.applyStyle(this.domElement.querySelector('#widget'), activeRule.getProperty('style')); + this.domElement.querySelector('#widget').title = activeRule.getProperty('message'); + this.domElement.querySelector('#widgetLabel').innerHTML = activeRule.getProperty('label'); + this.domElement.querySelector('#widgetIcon').classList = + WIDGET_ICON_CLASS + ' ' + activeRule.getProperty('icon'); +}; + +/** + * Get the active rule and update the Widget's appearance. + */ +SummaryWidget.prototype.executeRules = function () { + this.activeId = this.conditionManager.executeRules( + this.domainObject.configuration.ruleOrder, + this.rulesById + ); + this.updateWidget(); +}; + +/** + * Add a new rule to this widget + */ +SummaryWidget.prototype.addRule = function () { + let ruleCount = 0; + let ruleId; + const ruleOrder = this.domainObject.configuration.ruleOrder; + + while (Object.keys(this.rulesById).includes('rule' + ruleCount)) { + ruleCount++; + } - ruleId = 'rule' + ruleCount; - ruleOrder.push(ruleId); - this.domainObject.configuration.ruleOrder = ruleOrder; - - this.initRule(ruleId, 'Rule'); - this.updateDomainObject(); - this.refreshRules(); - }; + ruleId = 'rule' + ruleCount; + ruleOrder.push(ruleId); + this.domainObject.configuration.ruleOrder = ruleOrder; + + this.initRule(ruleId, 'Rule'); + this.updateDomainObject(); + this.refreshRules(); +}; + +/** + * Duplicate an existing widget rule from its configuration and splice it in + * after the rule it duplicates + * @param {Object} sourceConfig The configuration properties of the rule to be + * instantiated + */ +SummaryWidget.prototype.duplicateRule = function (sourceConfig) { + let ruleCount = 0; + let ruleId; + const sourceRuleId = sourceConfig.id; + const ruleOrder = this.domainObject.configuration.ruleOrder; + const ruleIds = Object.keys(this.rulesById); + + while (ruleIds.includes('rule' + ruleCount)) { + ruleCount = ++ruleCount; + } - /** - * Duplicate an existing widget rule from its configuration and splice it in - * after the rule it duplicates - * @param {Object} sourceConfig The configuration properties of the rule to be - * instantiated - */ - SummaryWidget.prototype.duplicateRule = function (sourceConfig) { - let ruleCount = 0; - let ruleId; - const sourceRuleId = sourceConfig.id; - const ruleOrder = this.domainObject.configuration.ruleOrder; - const ruleIds = Object.keys(this.rulesById); - - while (ruleIds.includes('rule' + ruleCount)) { - ruleCount = ++ruleCount; - } + ruleId = 'rule' + ruleCount; + sourceConfig.id = ruleId; + sourceConfig.name += ' Copy'; + ruleOrder.splice(ruleOrder.indexOf(sourceRuleId) + 1, 0, ruleId); + this.domainObject.configuration.ruleOrder = ruleOrder; + this.domainObject.configuration.ruleConfigById[ruleId] = sourceConfig; + this.initRule(ruleId, sourceConfig.name); + this.updateDomainObject(); + this.refreshRules(); +}; + +/** + * Initialize a new rule from a default configuration, or build a {Rule} object + * from it if already exists + * @param {string} ruleId An key to be used to identify this ruleId, or the key + of the rule to be instantiated + * @param {string} ruleName The initial human-readable name of this rule + */ +SummaryWidget.prototype.initRule = function (ruleId, ruleName) { + let ruleConfig; + const styleObj = {}; + + Object.assign(styleObj, DEFAULT_PROPS); + if (!this.domainObject.configuration.ruleConfigById[ruleId]) { + this.domainObject.configuration.ruleConfigById[ruleId] = { + name: ruleName || 'Rule', + label: 'Unnamed Rule', + message: '', + id: ruleId, + icon: ' ', + style: styleObj, + description: ruleId === 'default' ? 'Default appearance for the widget' : 'A new rule', + conditions: [ + { + object: '', + key: '', + operation: '', + values: [] + } + ], + jsCondition: '', + trigger: 'any', + expanded: 'true' + }; + } - ruleId = 'rule' + ruleCount; - sourceConfig.id = ruleId; - sourceConfig.name += ' Copy'; - ruleOrder.splice(ruleOrder.indexOf(sourceRuleId) + 1, 0, ruleId); + ruleConfig = this.domainObject.configuration.ruleConfigById[ruleId]; + this.rulesById[ruleId] = new Rule( + ruleConfig, + this.domainObject, + this.openmct, + this.conditionManager, + this.widgetDnD, + this.container + ); + this.rulesById[ruleId].on('remove', this.refreshRules, this); + this.rulesById[ruleId].on('duplicate', this.duplicateRule, this); + this.rulesById[ruleId].on('change', this.updateWidget, this); + this.rulesById[ruleId].on('conditionChange', this.executeRules, this); +}; + +/** + * Given two ruleIds, move the source rule after the target rule and update + * the view. + * @param {Object} event An event object representing this drop with draggingId + * and dropTarget fields + */ +SummaryWidget.prototype.reorder = function (event) { + const ruleOrder = this.domainObject.configuration.ruleOrder; + const sourceIndex = ruleOrder.indexOf(event.draggingId); + let targetIndex; + + if (event.draggingId !== event.dropTarget) { + ruleOrder.splice(sourceIndex, 1); + targetIndex = ruleOrder.indexOf(event.dropTarget); + ruleOrder.splice(targetIndex + 1, 0, event.draggingId); this.domainObject.configuration.ruleOrder = ruleOrder; - this.domainObject.configuration.ruleConfigById[ruleId] = sourceConfig; - this.initRule(ruleId, sourceConfig.name); this.updateDomainObject(); - this.refreshRules(); - }; - - /** - * Initialize a new rule from a default configuration, or build a {Rule} object - * from it if already exists - * @param {string} ruleId An key to be used to identify this ruleId, or the key - of the rule to be instantiated - * @param {string} ruleName The initial human-readable name of this rule - */ - SummaryWidget.prototype.initRule = function (ruleId, ruleName) { - let ruleConfig; - const styleObj = {}; - - Object.assign(styleObj, DEFAULT_PROPS); - if (!this.domainObject.configuration.ruleConfigById[ruleId]) { - this.domainObject.configuration.ruleConfigById[ruleId] = { - name: ruleName || 'Rule', - label: 'Unnamed Rule', - message: '', - id: ruleId, - icon: ' ', - style: styleObj, - description: ruleId === 'default' ? 'Default appearance for the widget' : 'A new rule', - conditions: [ - { - object: '', - key: '', - operation: '', - values: [] - } - ], - jsCondition: '', - trigger: 'any', - expanded: 'true' - }; - } - - ruleConfig = this.domainObject.configuration.ruleConfigById[ruleId]; - this.rulesById[ruleId] = new Rule( - ruleConfig, - this.domainObject, - this.openmct, - this.conditionManager, - this.widgetDnD, - this.container - ); - this.rulesById[ruleId].on('remove', this.refreshRules, this); - this.rulesById[ruleId].on('duplicate', this.duplicateRule, this); - this.rulesById[ruleId].on('change', this.updateWidget, this); - this.rulesById[ruleId].on('conditionChange', this.executeRules, this); - }; - - /** - * Given two ruleIds, move the source rule after the target rule and update - * the view. - * @param {Object} event An event object representing this drop with draggingId - * and dropTarget fields - */ - SummaryWidget.prototype.reorder = function (event) { - const ruleOrder = this.domainObject.configuration.ruleOrder; - const sourceIndex = ruleOrder.indexOf(event.draggingId); - let targetIndex; - - if (event.draggingId !== event.dropTarget) { - ruleOrder.splice(sourceIndex, 1); - targetIndex = ruleOrder.indexOf(event.dropTarget); - ruleOrder.splice(targetIndex + 1, 0, event.draggingId); - this.domainObject.configuration.ruleOrder = ruleOrder; - this.updateDomainObject(); - } - - this.refreshRules(); - }; - - /** - * Apply a list of css properties to an element - * @param {element} elem The DOM element to which the rules will be applied - * @param {object} style an object representing the style - */ - SummaryWidget.prototype.applyStyle = function (elem, style) { - Object.keys(style).forEach(function (propId) { - elem.style[propId] = style[propId]; - }); - }; + } - /** - * Mutate this domain object's configuration with the current local configuration - */ - SummaryWidget.prototype.updateDomainObject = function () { - this.openmct.objects.mutate( - this.domainObject, - 'configuration', - this.domainObject.configuration - ); - }; - - return SummaryWidget; -}); + this.refreshRules(); +}; + +/** + * Apply a list of css properties to an element + * @param {element} elem The DOM element to which the rules will be applied + * @param {object} style an object representing the style + */ +SummaryWidget.prototype.applyStyle = function (elem, style) { + Object.keys(style).forEach(function (propId) { + elem.style[propId] = style[propId]; + }); +}; + +/** + * Mutate this domain object's configuration with the current local configuration + */ +SummaryWidget.prototype.updateDomainObject = function () { + this.openmct.objects.mutate(this.domainObject, 'configuration', this.domainObject.configuration); +}; diff --git a/src/plugins/summaryWidget/src/TestDataItem.js b/src/plugins/summaryWidget/src/TestDataItem.js index f6702a36d60..246e2318020 100644 --- a/src/plugins/summaryWidget/src/TestDataItem.js +++ b/src/plugins/summaryWidget/src/TestDataItem.js @@ -1,193 +1,190 @@ -define([ - '../res/testDataItemTemplate.html', - './input/ObjectSelect', - './input/KeySelect', - './eventHelpers', - '../../../utils/template/templateHelpers', - 'EventEmitter' -], function (itemTemplate, ObjectSelect, KeySelect, eventHelpers, templateHelpers, EventEmitter) { +import EventEmitter from 'EventEmitter'; + +import * as templateHelpers from '../../../utils/template/templateHelpers'; +import * as itemTemplate from '../res/testDataItemTemplate.html'; +import eventHelpers from './eventHelpers'; +import KeySelect from './input/KeySelect'; +import ObjectSelect from './input/ObjectSelect'; + +/** + * An object representing a single mock telemetry value + * @param {object} itemConfig the configuration for this item, consisting of + * object, key, and value fields + * @param {number} index the index of this TestDataItem object in the data + * model of its parent {TestDataManager} o be injected into callbacks + * for removes + * @param {ConditionManager} conditionManager a conditionManager instance + * for populating selects with configuration data + * @constructor + */ +export default function TestDataItem(itemConfig, index, conditionManager) { + eventHelpers.extend(this); + this.config = itemConfig; + this.index = index; + this.conditionManager = conditionManager; + + this.domElement = templateHelpers.convertTemplateToHTML(itemTemplate)[0]; + this.eventEmitter = new EventEmitter(); + this.supportedCallbacks = ['remove', 'duplicate', 'change']; + + this.deleteButton = this.domElement.querySelector('.t-delete'); + this.duplicateButton = this.domElement.querySelector('.t-duplicate'); + + this.selects = {}; + this.valueInputs = []; + + this.remove = this.remove.bind(this); + this.duplicate = this.duplicate.bind(this); + + const self = this; + /** - * An object representing a single mock telemetry value - * @param {object} itemConfig the configuration for this item, consisting of - * object, key, and value fields - * @param {number} index the index of this TestDataItem object in the data - * model of its parent {TestDataManager} o be injected into callbacks - * for removes - * @param {ConditionManager} conditionManager a conditionManager instance - * for populating selects with configuration data - * @constructor + * A change event handler for this item's select inputs, which also invokes + * change callbacks registered with this item + * @param {string} value The new value of this select item + * @param {string} property The property of this item to modify + * @private */ - function TestDataItem(itemConfig, index, conditionManager) { - eventHelpers.extend(this); - this.config = itemConfig; - this.index = index; - this.conditionManager = conditionManager; - - this.domElement = templateHelpers.convertTemplateToHTML(itemTemplate)[0]; - this.eventEmitter = new EventEmitter(); - this.supportedCallbacks = ['remove', 'duplicate', 'change']; - - this.deleteButton = this.domElement.querySelector('.t-delete'); - this.duplicateButton = this.domElement.querySelector('.t-duplicate'); - - this.selects = {}; - this.valueInputs = []; - - this.remove = this.remove.bind(this); - this.duplicate = this.duplicate.bind(this); - - const self = this; - - /** - * A change event handler for this item's select inputs, which also invokes - * change callbacks registered with this item - * @param {string} value The new value of this select item - * @param {string} property The property of this item to modify - * @private - */ - function onSelectChange(value, property) { - if (property === 'key') { - self.generateValueInput(value); - } - - self.eventEmitter.emit('change', { - value: value, - property: property, - index: self.index - }); - } - - /** - * An input event handler for this item's value field. Invokes any change - * callbacks associated with this item - * @param {Event} event The input event that initiated this callback - * @private - */ - function onValueInput(event) { - const elem = event.target; - const value = isNaN(elem.valueAsNumber) ? elem.value : elem.valueAsNumber; - - if (elem.tagName.toUpperCase() === 'INPUT') { - self.eventEmitter.emit('change', { - value: value, - property: 'value', - index: self.index - }); - } + function onSelectChange(value, property) { + if (property === 'key') { + self.generateValueInput(value); } - this.listenTo(this.deleteButton, 'click', this.remove); - this.listenTo(this.duplicateButton, 'click', this.duplicate); - - this.selects.object = new ObjectSelect(this.config, this.conditionManager); - this.selects.key = new KeySelect( - this.config, - this.selects.object, - this.conditionManager, - function (value) { - onSelectChange(value, 'key'); - } - ); - - this.selects.object.on('change', function (value) { - onSelectChange(value, 'object'); - }); - - Object.values(this.selects).forEach(function (select) { - self.domElement.querySelector('.t-configuration').append(select.getDOM()); + self.eventEmitter.emit('change', { + value: value, + property: property, + index: self.index }); - this.listenTo(this.domElement, 'input', onValueInput); } /** - * Gets the DOM associated with this element's view - * @return {Element} + * An input event handler for this item's value field. Invokes any change + * callbacks associated with this item + * @param {Event} event The input event that initiated this callback + * @private */ - TestDataItem.prototype.getDOM = function (container) { - return this.domElement; - }; + function onValueInput(event) { + const elem = event.target; + const value = isNaN(elem.valueAsNumber) ? elem.value : elem.valueAsNumber; - /** - * Register a callback with this item: supported callbacks are remove, change, - * and duplicate - * @param {string} event The key for the event to listen to - * @param {function} callback The function that this rule will invoke on this event - * @param {Object} context A reference to a scope to use as the context for - * context for the callback function - */ - TestDataItem.prototype.on = function (event, callback, context) { - if (this.supportedCallbacks.includes(event)) { - this.eventEmitter.on(event, callback, context || this); + if (elem.tagName.toUpperCase() === 'INPUT') { + self.eventEmitter.emit('change', { + value: value, + property: 'value', + index: self.index + }); } - }; - - /** - * Implement "off" to complete event emitter interface. - */ - TestDataItem.prototype.off = function (event, callback, context) { - this.eventEmitter.off(event, callback, context); - }; - - /** - * Hide the appropriate inputs when this is the only item - */ - TestDataItem.prototype.hideButtons = function () { - this.deleteButton.style.display = 'none'; - }; - - /** - * Remove this item from the configuration. Invokes any registered - * remove callbacks - */ - TestDataItem.prototype.remove = function () { - const self = this; - this.eventEmitter.emit('remove', self.index); - this.stopListening(); - - Object.values(this.selects).forEach(function (select) { - select.destroy(); - }); - }; - - /** - * Makes a deep clone of this item's configuration, and invokes any registered - * duplicate callbacks with the cloned configuration as an argument - */ - TestDataItem.prototype.duplicate = function () { - const sourceItem = JSON.parse(JSON.stringify(this.config)); - const self = this; + } - this.eventEmitter.emit('duplicate', { - sourceItem: sourceItem, - index: self.index - }); - }; + this.listenTo(this.deleteButton, 'click', this.remove); + this.listenTo(this.duplicateButton, 'click', this.duplicate); - /** - * When a telemetry property key is selected, create the appropriate value input - * and add it to the view - * @param {string} key The key of currently selected telemetry property - */ - TestDataItem.prototype.generateValueInput = function (key) { - const evaluator = this.conditionManager.getEvaluator(); - const inputArea = this.domElement.querySelector('.t-value-inputs'); - const dataType = this.conditionManager.getTelemetryPropertyType(this.config.object, key); - const inputType = evaluator.getInputTypeById(dataType); - - inputArea.innerHTML = ''; - if (inputType) { - if (!this.config.value) { - this.config.value = inputType === 'number' ? 0 : ''; - } - - const newInput = document.createElement('input'); - newInput.type = `${inputType}`; - newInput.value = `${this.config.value}`; - - this.valueInput = newInput; - inputArea.append(this.valueInput); + this.selects.object = new ObjectSelect(this.config, this.conditionManager); + this.selects.key = new KeySelect( + this.config, + this.selects.object, + this.conditionManager, + function (value) { + onSelectChange(value, 'key'); + } + ); + + this.selects.object.on('change', function (value) { + onSelectChange(value, 'object'); + }); + + Object.values(this.selects).forEach(function (select) { + self.domElement.querySelector('.t-configuration').append(select.getDOM()); + }); + this.listenTo(this.domElement, 'input', onValueInput); +} + +/** + * Gets the DOM associated with this element's view + * @return {Element} + */ +TestDataItem.prototype.getDOM = function (container) { + return this.domElement; +}; + +/** + * Register a callback with this item: supported callbacks are remove, change, + * and duplicate + * @param {string} event The key for the event to listen to + * @param {function} callback The function that this rule will invoke on this event + * @param {Object} context A reference to a scope to use as the context for + * context for the callback function + */ +TestDataItem.prototype.on = function (event, callback, context) { + if (this.supportedCallbacks.includes(event)) { + this.eventEmitter.on(event, callback, context || this); + } +}; + +/** + * Implement "off" to complete event emitter interface. + */ +TestDataItem.prototype.off = function (event, callback, context) { + this.eventEmitter.off(event, callback, context); +}; + +/** + * Hide the appropriate inputs when this is the only item + */ +TestDataItem.prototype.hideButtons = function () { + this.deleteButton.style.display = 'none'; +}; + +/** + * Remove this item from the configuration. Invokes any registered + * remove callbacks + */ +TestDataItem.prototype.remove = function () { + const self = this; + this.eventEmitter.emit('remove', self.index); + this.stopListening(); + + Object.values(this.selects).forEach(function (select) { + select.destroy(); + }); +}; + +/** + * Makes a deep clone of this item's configuration, and invokes any registered + * duplicate callbacks with the cloned configuration as an argument + */ +TestDataItem.prototype.duplicate = function () { + const sourceItem = JSON.parse(JSON.stringify(this.config)); + const self = this; + + this.eventEmitter.emit('duplicate', { + sourceItem: sourceItem, + index: self.index + }); +}; + +/** + * When a telemetry property key is selected, create the appropriate value input + * and add it to the view + * @param {string} key The key of currently selected telemetry property + */ +TestDataItem.prototype.generateValueInput = function (key) { + const evaluator = this.conditionManager.getEvaluator(); + const inputArea = this.domElement.querySelector('.t-value-inputs'); + const dataType = this.conditionManager.getTelemetryPropertyType(this.config.object, key); + const inputType = evaluator.getInputTypeById(dataType); + + inputArea.innerHTML = ''; + if (inputType) { + if (!this.config.value) { + this.config.value = inputType === 'number' ? 0 : ''; } - }; - return TestDataItem; -}); + const newInput = document.createElement('input'); + newInput.type = `${inputType}`; + newInput.value = `${this.config.value}`; + + this.valueInput = newInput; + inputArea.append(this.valueInput); + } +}; diff --git a/src/plugins/summaryWidget/src/TestDataManager.js b/src/plugins/summaryWidget/src/TestDataManager.js index 45fa9750f6d..be468c0339b 100644 --- a/src/plugins/summaryWidget/src/TestDataManager.js +++ b/src/plugins/summaryWidget/src/TestDataManager.js @@ -1,201 +1,198 @@ -define([ - './eventHelpers', - '../res/testDataTemplate.html', - './TestDataItem', - '../../../utils/template/templateHelpers', - 'lodash' -], function (eventHelpers, testDataTemplate, TestDataItem, templateHelpers, _) { - /** - * Controls the input and usage of test data in the summary widget. - * @constructor - * @param {Object} domainObject The summary widget domain object - * @param {ConditionManager} conditionManager A conditionManager instance - * @param {MCT} openmct and MCT instance - */ - function TestDataManager(domainObject, conditionManager, openmct) { - eventHelpers.extend(this); - const self = this; - - this.domainObject = domainObject; - this.manager = conditionManager; - this.openmct = openmct; - - this.evaluator = this.manager.getEvaluator(); - this.domElement = templateHelpers.convertTemplateToHTML(testDataTemplate)[0]; - this.config = this.domainObject.configuration.testDataConfig; - this.testCache = {}; - - this.itemArea = this.domElement.querySelector('.t-test-data-config'); - this.addItemButton = this.domElement.querySelector('.add-test-condition'); - this.testDataInput = this.domElement.querySelector('.t-test-data-checkbox'); - - /** - * Toggles whether the associated {ConditionEvaluator} uses the actual - * subscription cache or the test data cache - * @param {Event} event The change event that triggered this callback - * @private - */ - function toggleTestData(event) { - const elem = event.target; - self.evaluator.useTestData(elem.checked); - self.updateTestCache(); - } - - this.listenTo(this.addItemButton, 'click', function () { - self.initItem(); - }); - this.listenTo(this.testDataInput, 'change', toggleTestData); - - this.evaluator.setTestDataCache(this.testCache); - this.evaluator.useTestData(false); - - this.refreshItems(); - } - - /** - * Get the DOM element representing this test data manager in the view - */ - TestDataManager.prototype.getDOM = function () { - return this.domElement; - }; +import _ from 'lodash'; + +import * as templateHelpers from '../../../utils/template/templateHelpers'; +import testDataTemplate from '../res/testDataTemplate.html'; +import eventHelpers from './eventHelpers'; +import TestDataItem from './TestDataItem'; + +/** + * Controls the input and usage of test data in the summary widget. + * @constructor + * @param {Object} domainObject The summary widget domain object + * @param {ConditionManager} conditionManager A conditionManager instance + * @param {MCT} openmct and MCT instance + */ +export default function TestDataManager(domainObject, conditionManager, openmct) { + eventHelpers.extend(this); + const self = this; + + this.domainObject = domainObject; + this.manager = conditionManager; + this.openmct = openmct; + + this.evaluator = this.manager.getEvaluator(); + this.domElement = templateHelpers.convertTemplateToHTML(testDataTemplate)[0]; + this.config = this.domainObject.configuration.testDataConfig; + this.testCache = {}; + + this.itemArea = this.domElement.querySelector('.t-test-data-config'); + this.addItemButton = this.domElement.querySelector('.add-test-condition'); + this.testDataInput = this.domElement.querySelector('.t-test-data-checkbox'); /** - * Initialize a new test data item, either from a source configuration, or with - * the default empty configuration - * @param {Object} [config] An object with sourceItem and index fields to instantiate - * this rule from, optional + * Toggles whether the associated {ConditionEvaluator} uses the actual + * subscription cache or the test data cache + * @param {Event} event The change event that triggered this callback + * @private */ - TestDataManager.prototype.initItem = function (config) { - const sourceIndex = config && config.index; - const defaultItem = { - object: '', - key: '', - value: '' - }; - let newItem; - - newItem = config !== undefined ? config.sourceItem : defaultItem; - if (sourceIndex !== undefined) { - this.config.splice(sourceIndex + 1, 0, newItem); - } else { - this.config.push(newItem); - } + function toggleTestData(event) { + const elem = event.target; + self.evaluator.useTestData(elem.checked); + self.updateTestCache(); + } - this.updateDomainObject(); - this.refreshItems(); + this.listenTo(this.addItemButton, 'click', function () { + self.initItem(); + }); + this.listenTo(this.testDataInput, 'change', toggleTestData); + + this.evaluator.setTestDataCache(this.testCache); + this.evaluator.useTestData(false); + + this.refreshItems(); +} + +/** + * Get the DOM element representing this test data manager in the view + */ +TestDataManager.prototype.getDOM = function () { + return this.domElement; +}; + +/** + * Initialize a new test data item, either from a source configuration, or with + * the default empty configuration + * @param {Object} [config] An object with sourceItem and index fields to instantiate + * this rule from, optional + */ +TestDataManager.prototype.initItem = function (config) { + const sourceIndex = config && config.index; + const defaultItem = { + object: '', + key: '', + value: '' }; + let newItem; - /** - * Remove an item from this TestDataManager at the given index - * @param {number} removeIndex The index of the item to remove - */ - TestDataManager.prototype.removeItem = function (removeIndex) { - _.remove(this.config, function (item, index) { - return index === removeIndex; - }); - this.updateDomainObject(); - this.refreshItems(); - }; + newItem = config !== undefined ? config.sourceItem : defaultItem; + if (sourceIndex !== undefined) { + this.config.splice(sourceIndex + 1, 0, newItem); + } else { + this.config.push(newItem); + } - /** - * Change event handler for the test data items which compose this - * test data generator - * @param {Object} event An object representing this event, with value, property, - * and index fields - */ - TestDataManager.prototype.onItemChange = function (event) { - this.config[event.index][event.property] = event.value; - this.updateDomainObject(); - this.updateTestCache(); - }; + this.updateDomainObject(); + this.refreshItems(); +}; + +/** + * Remove an item from this TestDataManager at the given index + * @param {number} removeIndex The index of the item to remove + */ +TestDataManager.prototype.removeItem = function (removeIndex) { + _.remove(this.config, function (item, index) { + return index === removeIndex; + }); + this.updateDomainObject(); + this.refreshItems(); +}; + +/** + * Change event handler for the test data items which compose this + * test data generator + * @param {Object} event An object representing this event, with value, property, + * and index fields + */ +TestDataManager.prototype.onItemChange = function (event) { + this.config[event.index][event.property] = event.value; + this.updateDomainObject(); + this.updateTestCache(); +}; + +/** + * Builds the test cache from the current item configuration, and passes + * the new test cache to the associated {ConditionEvaluator} instance + */ +TestDataManager.prototype.updateTestCache = function () { + this.generateTestCache(); + this.evaluator.setTestDataCache(this.testCache); + this.manager.triggerTelemetryCallback(); +}; + +/** + * Instantiate {TestDataItem} objects from the current configuration, and + * update the view accordingly + */ +TestDataManager.prototype.refreshItems = function () { + const self = this; + if (this.items) { + this.items.forEach(function (item) { + this.stopListening(item); + }, this); + } - /** - * Builds the test cache from the current item configuration, and passes - * the new test cache to the associated {ConditionEvaluator} instance - */ - TestDataManager.prototype.updateTestCache = function () { - this.generateTestCache(); - this.evaluator.setTestDataCache(this.testCache); - this.manager.triggerTelemetryCallback(); - }; + self.items = []; - /** - * Instantiate {TestDataItem} objects from the current configuration, and - * update the view accordingly - */ - TestDataManager.prototype.refreshItems = function () { - const self = this; - if (this.items) { - this.items.forEach(function (item) { - this.stopListening(item); - }, this); - } + this.domElement.querySelectorAll('.t-test-data-item').forEach((item) => { + item.remove(); + }); - self.items = []; + this.config.forEach(function (item, index) { + const newItem = new TestDataItem(item, index, self.manager); + self.listenTo(newItem, 'remove', self.removeItem, self); + self.listenTo(newItem, 'duplicate', self.initItem, self); + self.listenTo(newItem, 'change', self.onItemChange, self); + self.items.push(newItem); + }); - this.domElement.querySelectorAll('.t-test-data-item').forEach((item) => { - item.remove(); - }); + self.items.forEach(function (item) { + self.itemArea.prepend(item.getDOM()); + }); - this.config.forEach(function (item, index) { - const newItem = new TestDataItem(item, index, self.manager); - self.listenTo(newItem, 'remove', self.removeItem, self); - self.listenTo(newItem, 'duplicate', self.initItem, self); - self.listenTo(newItem, 'change', self.onItemChange, self); - self.items.push(newItem); - }); + if (self.items.length === 1) { + self.items[0].hideButtons(); + } - self.items.forEach(function (item) { - self.itemArea.prepend(item.getDOM()); + this.updateTestCache(); +}; + +/** + * Builds a test data cache in the format of a telemetry subscription cache + * as expected by a {ConditionEvaluator} + */ +TestDataManager.prototype.generateTestCache = function () { + let testCache = this.testCache; + const manager = this.manager; + const compositionObjs = manager.getComposition(); + let metadata; + + testCache = {}; + Object.keys(compositionObjs).forEach(function (id) { + testCache[id] = {}; + metadata = manager.getTelemetryMetadata(id); + Object.keys(metadata).forEach(function (key) { + testCache[id][key] = ''; }); - - if (self.items.length === 1) { - self.items[0].hideButtons(); + }); + this.config.forEach(function (item) { + if (testCache[item.object]) { + testCache[item.object][item.key] = item.value; } - - this.updateTestCache(); - }; - - /** - * Builds a test data cache in the format of a telemetry subscription cache - * as expected by a {ConditionEvaluator} - */ - TestDataManager.prototype.generateTestCache = function () { - let testCache = this.testCache; - const manager = this.manager; - const compositionObjs = manager.getComposition(); - let metadata; - - testCache = {}; - Object.keys(compositionObjs).forEach(function (id) { - testCache[id] = {}; - metadata = manager.getTelemetryMetadata(id); - Object.keys(metadata).forEach(function (key) { - testCache[id][key] = ''; - }); - }); - this.config.forEach(function (item) { - if (testCache[item.object]) { - testCache[item.object][item.key] = item.value; - } - }); - - this.testCache = testCache; - }; - - /** - * Update the domain object configuration associated with this test data manager - */ - TestDataManager.prototype.updateDomainObject = function () { - this.openmct.objects.mutate(this.domainObject, 'configuration.testDataConfig', this.config); - }; - - TestDataManager.prototype.destroy = function () { - this.stopListening(); - this.items.forEach(function (item) { - item.remove(); - }); - }; - - return TestDataManager; -}); + }); + + this.testCache = testCache; +}; + +/** + * Update the domain object configuration associated with this test data manager + */ +TestDataManager.prototype.updateDomainObject = function () { + this.openmct.objects.mutate(this.domainObject, 'configuration.testDataConfig', this.config); +}; + +TestDataManager.prototype.destroy = function () { + this.stopListening(); + this.items.forEach(function (item) { + item.remove(); + }); +}; diff --git a/src/plugins/summaryWidget/src/WidgetDnD.js b/src/plugins/summaryWidget/src/WidgetDnD.js index 05b4175734c..5fc94b3d050 100644 --- a/src/plugins/summaryWidget/src/WidgetDnD.js +++ b/src/plugins/summaryWidget/src/WidgetDnD.js @@ -1,165 +1,162 @@ -define([ - '../res/ruleImageTemplate.html', - 'EventEmitter', - '../../../utils/template/templateHelpers' -], function (ruleImageTemplate, EventEmitter, templateHelpers) { - /** - * Manages the Sortable List interface for reordering rules by drag and drop - * @param {Element} container The DOM element that contains this Summary Widget's view - * @param {string[]} ruleOrder An array of rule IDs representing the current rule order - * @param {Object} rulesById An object mapping rule IDs to rule configurations - */ - function WidgetDnD(container, ruleOrder, rulesById) { - this.container = container; - this.ruleOrder = ruleOrder; - this.rulesById = rulesById; - - this.imageContainer = templateHelpers.convertTemplateToHTML(ruleImageTemplate)[0]; - this.image = this.imageContainer.querySelector('.t-drag-rule-image'); - this.draggingId = ''; - this.draggingRulePrevious = ''; - this.eventEmitter = new EventEmitter(); - this.supportedCallbacks = ['drop']; +import EventEmitter from 'EventEmitter'; + +import * as templateHelpers from '../../../utils/template/templateHelpers'; +import ruleImageTemplate from '../res/ruleImageTemplate.html'; + +/** + * Manages the Sortable List interface for reordering rules by drag and drop + * @param {Element} container The DOM element that contains this Summary Widget's view + * @param {string[]} ruleOrder An array of rule IDs representing the current rule order + * @param {Object} rulesById An object mapping rule IDs to rule configurations + */ +export default function WidgetDnD(container, ruleOrder, rulesById) { + this.container = container; + this.ruleOrder = ruleOrder; + this.rulesById = rulesById; + + this.imageContainer = templateHelpers.convertTemplateToHTML(ruleImageTemplate)[0]; + this.image = this.imageContainer.querySelector('.t-drag-rule-image'); + this.draggingId = ''; + this.draggingRulePrevious = ''; + this.eventEmitter = new EventEmitter(); + this.supportedCallbacks = ['drop']; + + this.drag = this.drag.bind(this); + this.drop = this.drop.bind(this); - this.drag = this.drag.bind(this); - this.drop = this.drop.bind(this); + this.container.addEventListener('mousemove', this.drag); + document.addEventListener('mouseup', this.drop); + this.container.parentNode.insertBefore(this.imageContainer, this.container); + this.imageContainer.style.display = 'none'; +} - this.container.addEventListener('mousemove', this.drag); - document.addEventListener('mouseup', this.drop); - this.container.parentNode.insertBefore(this.imageContainer, this.container); - this.imageContainer.style.display = 'none'; +/** + * Remove event listeners registered to elements external to the widget + */ +WidgetDnD.prototype.destroy = function () { + this.container.removeEventListener('mousemove', this.drag); + document.removeEventListener('mouseup', this.drop); +}; + +/** + * Register a callback with this WidgetDnD: supported callback is drop + * @param {string} event The key for the event to listen to + * @param {function} callback The function that this rule will invoke on this event + * @param {Object} context A reference to a scope to use as the context for + * context for the callback function + */ +WidgetDnD.prototype.on = function (event, callback, context) { + if (this.supportedCallbacks.includes(event)) { + this.eventEmitter.on(event, callback, context || this); } +}; - /** - * Remove event listeners registered to elements external to the widget - */ - WidgetDnD.prototype.destroy = function () { - this.container.removeEventListener('mousemove', this.drag); - document.removeEventListener('mouseup', this.drop); - }; - - /** - * Register a callback with this WidgetDnD: supported callback is drop - * @param {string} event The key for the event to listen to - * @param {function} callback The function that this rule will invoke on this event - * @param {Object} context A reference to a scope to use as the context for - * context for the callback function - */ - WidgetDnD.prototype.on = function (event, callback, context) { - if (this.supportedCallbacks.includes(event)) { - this.eventEmitter.on(event, callback, context || this); - } - }; - - /** - * Sets the image for the dragged element to the given DOM element - * @param {Element} image The HTML element to set as the drap image - */ - WidgetDnD.prototype.setDragImage = function (image) { - this.image.html(image); - }; - - /** - * Calculate where this rule has been dragged relative to the other rules - * @param {Event} event The mousemove or mouseup event that triggered this - event handler - * @return {string} The ID of the rule whose drag indicator should be displayed - */ - WidgetDnD.prototype.getDropLocation = function (event) { - const ruleOrder = this.ruleOrder; - const rulesById = this.rulesById; - const draggingId = this.draggingId; - let offset; - let y; - let height; - const dropY = event.pageY; - let target = ''; - - ruleOrder.forEach(function (ruleId, index) { - const ruleDOM = rulesById[ruleId].getDOM(); - offset = window.innerWidth - (ruleDOM.offsetLeft + ruleDOM.offsetWidth); - y = offset.top; - height = offset.height; - if (index === 0) { - if (dropY < y + (7 * height) / 3) { - target = ruleId; - } - } else if (index === ruleOrder.length - 1 && ruleId !== draggingId) { - if (y + height / 3 < dropY) { - target = ruleId; - } - } else { - if (y + height / 3 < dropY && dropY < y + (7 * height) / 3) { - target = ruleId; - } +/** + * Sets the image for the dragged element to the given DOM element + * @param {Element} image The HTML element to set as the drap image + */ +WidgetDnD.prototype.setDragImage = function (image) { + this.image.html(image); +}; + +/** + * Calculate where this rule has been dragged relative to the other rules + * @param {Event} event The mousemove or mouseup event that triggered this + event handler + * @return {string} The ID of the rule whose drag indicator should be displayed + */ +WidgetDnD.prototype.getDropLocation = function (event) { + const ruleOrder = this.ruleOrder; + const rulesById = this.rulesById; + const draggingId = this.draggingId; + let offset; + let y; + let height; + const dropY = event.pageY; + let target = ''; + + ruleOrder.forEach(function (ruleId, index) { + const ruleDOM = rulesById[ruleId].getDOM(); + offset = window.innerWidth - (ruleDOM.offsetLeft + ruleDOM.offsetWidth); + y = offset.top; + height = offset.height; + if (index === 0) { + if (dropY < y + (7 * height) / 3) { + target = ruleId; } - }); + } else if (index === ruleOrder.length - 1 && ruleId !== draggingId) { + if (y + height / 3 < dropY) { + target = ruleId; + } + } else { + if (y + height / 3 < dropY && dropY < y + (7 * height) / 3) { + target = ruleId; + } + } + }); + + return target; +}; - return target; - }; - - /** - * Called by a {Rule} instance that initiates a drag gesture - * @param {string} ruleId The identifier of the rule which is being dragged - */ - WidgetDnD.prototype.dragStart = function (ruleId) { - const ruleOrder = this.ruleOrder; - this.draggingId = ruleId; - this.draggingRulePrevious = ruleOrder[ruleOrder.indexOf(ruleId) - 1]; - this.rulesById[this.draggingRulePrevious].showDragIndicator(); - this.imageContainer.show(); +/** + * Called by a {Rule} instance that initiates a drag gesture + * @param {string} ruleId The identifier of the rule which is being dragged + */ +WidgetDnD.prototype.dragStart = function (ruleId) { + const ruleOrder = this.ruleOrder; + this.draggingId = ruleId; + this.draggingRulePrevious = ruleOrder[ruleOrder.indexOf(ruleId) - 1]; + this.rulesById[this.draggingRulePrevious].showDragIndicator(); + this.imageContainer.show(); + this.imageContainer.offset({ + top: event.pageY - this.image.height() / 2, + left: event.pageX - this.image.querySelector('.t-grippy').style.width + }); +}; + +/** + * An event handler for a mousemove event, once a rule has begun a drag gesture + * @param {Event} event The mousemove event that triggered this callback + */ +WidgetDnD.prototype.drag = function (event) { + let dragTarget; + if (this.draggingId && this.draggingId !== '') { + event.preventDefault(); + dragTarget = this.getDropLocation(event); this.imageContainer.offset({ top: event.pageY - this.image.height() / 2, left: event.pageX - this.image.querySelector('.t-grippy').style.width }); - }; - - /** - * An event handler for a mousemove event, once a rule has begun a drag gesture - * @param {Event} event The mousemove event that triggered this callback - */ - WidgetDnD.prototype.drag = function (event) { - let dragTarget; - if (this.draggingId && this.draggingId !== '') { - event.preventDefault(); - dragTarget = this.getDropLocation(event); - this.imageContainer.offset({ - top: event.pageY - this.image.height() / 2, - left: event.pageX - this.image.querySelector('.t-grippy').style.width - }); - if (this.rulesById[dragTarget]) { - this.rulesById[dragTarget].showDragIndicator(); - } else { - this.rulesById[this.draggingRulePrevious].showDragIndicator(); - } + if (this.rulesById[dragTarget]) { + this.rulesById[dragTarget].showDragIndicator(); + } else { + this.rulesById[this.draggingRulePrevious].showDragIndicator(); } - }; - - /** - * Handles the mouseup event that corresponds to the user dropping the rule - * in its final location. Invokes any registered drop callbacks with the dragged - * rule's ID and the ID of the target rule that the dragged rule should be - * inserted after - * @param {Event} event The mouseup event that triggered this callback - */ - WidgetDnD.prototype.drop = function (event) { - let dropTarget = this.getDropLocation(event); - const draggingId = this.draggingId; - - if (this.draggingId && this.draggingId !== '') { - if (!this.rulesById[dropTarget]) { - dropTarget = this.draggingId; - } + } +}; + +/** + * Handles the mouseup event that corresponds to the user dropping the rule + * in its final location. Invokes any registered drop callbacks with the dragged + * rule's ID and the ID of the target rule that the dragged rule should be + * inserted after + * @param {Event} event The mouseup event that triggered this callback + */ +WidgetDnD.prototype.drop = function (event) { + let dropTarget = this.getDropLocation(event); + const draggingId = this.draggingId; - this.eventEmitter.emit('drop', { - draggingId: draggingId, - dropTarget: dropTarget - }); - this.draggingId = ''; - this.draggingRulePrevious = ''; - this.imageContainer.hide(); + if (this.draggingId && this.draggingId !== '') { + if (!this.rulesById[dropTarget]) { + dropTarget = this.draggingId; } - }; - return WidgetDnD; -}); + this.eventEmitter.emit('drop', { + draggingId: draggingId, + dropTarget: dropTarget + }); + this.draggingId = ''; + this.draggingRulePrevious = ''; + this.imageContainer.hide(); + } +}; diff --git a/src/plugins/summaryWidget/src/eventHelpers.js b/src/plugins/summaryWidget/src/eventHelpers.js index 367072b9f66..79aef224880 100644 --- a/src/plugins/summaryWidget/src/eventHelpers.js +++ b/src/plugins/summaryWidget/src/eventHelpers.js @@ -20,80 +20,78 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define([], function () { - const helperFunctions = { - listenTo: function (object, event, callback, context) { - if (!this._listeningTo) { - this._listeningTo = []; - } +const helperFunctions = { + listenTo: function (object, event, callback, context) { + if (!this._listeningTo) { + this._listeningTo = []; + } - const listener = { - object: object, - event: event, - callback: callback, - context: context, - _cb: context ? callback.bind(context) : callback - }; - if (object.$watch && event.indexOf('change:') === 0) { - const scopePath = event.replace('change:', ''); - listener.unlisten = object.$watch(scopePath, listener._cb, true); - } else if (object.$on) { - listener.unlisten = object.$on(event, listener._cb); - } else if (object.addEventListener) { - object.addEventListener(event, listener._cb); - } else { - object.on(event, listener._cb); - } + 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); - }, + this._listeningTo.push(listener); + }, - stopListening: function (object, event, callback, context) { - if (!this._listeningTo) { - this._listeningTo = []; - } + stopListening: function (object, event, callback, context) { + if (!this._listeningTo) { + this._listeningTo = []; + } - this._listeningTo - .filter(function (listener) { - if (object && object !== listener.object) { - return false; - } + this._listeningTo + .filter(function (listener) { + if (object && object !== listener.object) { + return false; + } - if (event && event !== listener.event) { - return false; - } + if (event && event !== listener.event) { + return false; + } - if (callback && callback !== listener.callback) { - return false; - } + if (callback && callback !== listener.callback) { + return false; + } - if (context && context !== listener.context) { - 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 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); - }, + 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; - } - }; + extend: function (object) { + object.listenTo = helperFunctions.listenTo; + object.stopListening = helperFunctions.stopListening; + } +}; - return helperFunctions; -}); +export default helperFunctions; diff --git a/src/plugins/summaryWidget/src/input/ColorPalette.js b/src/plugins/summaryWidget/src/input/ColorPalette.js index 82ed0b39814..44046b4001b 100644 --- a/src/plugins/summaryWidget/src/input/ColorPalette.js +++ b/src/plugins/summaryWidget/src/input/ColorPalette.js @@ -1,128 +1,126 @@ -define(['./Palette'], function (Palette) { - //The colors that will be used to instantiate this palette if none are provided - const DEFAULT_COLORS = [ - '#000000', - '#434343', - '#666666', - '#999999', - '#b7b7b7', - '#cccccc', - '#d9d9d9', - '#efefef', - '#f3f3f3', - '#ffffff', - '#980000', - '#ff0000', - '#ff9900', - '#ffff00', - '#00ff00', - '#00ffff', - '#4a86e8', - '#0000ff', - '#9900ff', - '#ff00ff', - '#e6b8af', - '#f4cccc', - '#fce5cd', - '#fff2cc', - '#d9ead3', - '#d0e0e3', - '#c9daf8', - '#cfe2f3', - '#d9d2e9', - '#ead1dc', - '#dd7e6b', - '#dd7e6b', - '#f9cb9c', - '#ffe599', - '#b6d7a8', - '#a2c4c9', - '#a4c2f4', - '#9fc5e8', - '#b4a7d6', - '#d5a6bd', - '#cc4125', - '#e06666', - '#f6b26b', - '#ffd966', - '#93c47d', - '#76a5af', - '#6d9eeb', - '#6fa8dc', - '#8e7cc3', - '#c27ba0', - '#a61c00', - '#cc0000', - '#e69138', - '#f1c232', - '#6aa84f', - '#45818e', - '#3c78d8', - '#3d85c6', - '#674ea7', - '#a64d79', - '#85200c', - '#990000', - '#b45f06', - '#bf9000', - '#38761d', - '#134f5c', - '#1155cc', - '#0b5394', - '#351c75', - '#741b47', - '#5b0f00', - '#660000', - '#783f04', - '#7f6000', - '#274e13', - '#0c343d', - '#1c4587', - '#073763', - '#20124d', - '#4c1130' - ]; +import Palette from './Palette'; - /** - * Instantiates a new Open MCT Color Palette input - * @constructor - * @param {string} cssClass The class name of the icon which should be applied - * to this palette - * @param {Element} container The view that contains this palette - * @param {string[]} colors (optional) A list of colors that should be used to instantiate this palette - */ - function ColorPalette(cssClass, container, colors) { - this.colors = colors || DEFAULT_COLORS; - this.palette = new Palette(cssClass, container, this.colors); - - this.palette.setNullOption('rgba(0,0,0,0)'); +// The colors that will be used to instantiate this palette if none are provided +const DEFAULT_COLORS = [ + '#000000', + '#434343', + '#666666', + '#999999', + '#b7b7b7', + '#cccccc', + '#d9d9d9', + '#efefef', + '#f3f3f3', + '#ffffff', + '#980000', + '#ff0000', + '#ff9900', + '#ffff00', + '#00ff00', + '#00ffff', + '#4a86e8', + '#0000ff', + '#9900ff', + '#ff00ff', + '#e6b8af', + '#f4cccc', + '#fce5cd', + '#fff2cc', + '#d9ead3', + '#d0e0e3', + '#c9daf8', + '#cfe2f3', + '#d9d2e9', + '#ead1dc', + '#dd7e6b', + '#dd7e6b', + '#f9cb9c', + '#ffe599', + '#b6d7a8', + '#a2c4c9', + '#a4c2f4', + '#9fc5e8', + '#b4a7d6', + '#d5a6bd', + '#cc4125', + '#e06666', + '#f6b26b', + '#ffd966', + '#93c47d', + '#76a5af', + '#6d9eeb', + '#6fa8dc', + '#8e7cc3', + '#c27ba0', + '#a61c00', + '#cc0000', + '#e69138', + '#f1c232', + '#6aa84f', + '#45818e', + '#3c78d8', + '#3d85c6', + '#674ea7', + '#a64d79', + '#85200c', + '#990000', + '#b45f06', + '#bf9000', + '#38761d', + '#134f5c', + '#1155cc', + '#0b5394', + '#351c75', + '#741b47', + '#5b0f00', + '#660000', + '#783f04', + '#7f6000', + '#274e13', + '#0c343d', + '#1c4587', + '#073763', + '#20124d', + '#4c1130' +]; - const domElement = this.palette.getDOM(); - const self = this; +/** + * Instantiates a new Open MCT Color Palette input + * @constructor + * @param {string} cssClass The class name of the icon which should be applied + * to this palette + * @param {Element} container The view that contains this palette + * @param {string[]} colors (optional) A list of colors that should be used to instantiate this palette + */ +export default function ColorPalette(cssClass, container, colors) { + this.colors = colors || DEFAULT_COLORS; + this.palette = new Palette(cssClass, container, this.colors); - domElement.querySelector('.c-button--menu').classList.add('c-button--swatched'); - domElement.querySelector('.t-swatch').classList.add('color-swatch'); - domElement.querySelector('.c-palette').classList.add('c-palette--color'); + this.palette.setNullOption('rgba(0,0,0,0)'); - domElement.querySelectorAll('.c-palette__item').forEach((item) => { - // eslint-disable-next-line no-invalid-this - item.style.backgroundColor = item.dataset.item; - }); + const domElement = this.palette.getDOM(); + const self = this; - /** - * Update this palette's current selection indicator with the style - * of the currently selected item - * @private - */ - function updateSwatch() { - const color = self.palette.getCurrent(); - domElement.querySelector('.color-swatch').style.backgroundColor = color; - } + domElement.querySelector('.c-button--menu').classList.add('c-button--swatched'); + domElement.querySelector('.t-swatch').classList.add('color-swatch'); + domElement.querySelector('.c-palette').classList.add('c-palette--color'); - this.palette.on('change', updateSwatch); + domElement.querySelectorAll('.c-palette__item').forEach((item) => { + // eslint-disable-next-line no-invalid-this + item.style.backgroundColor = item.dataset.item; + }); - return this.palette; + /** + * Update this palette's current selection indicator with the style + * of the currently selected item + * @private + */ + function updateSwatch() { + const color = self.palette.getCurrent(); + domElement.querySelector('.color-swatch').style.backgroundColor = color; } - return ColorPalette; -}); + this.palette.on('change', updateSwatch); + + return this.palette; +} diff --git a/src/plugins/summaryWidget/src/input/IconPalette.js b/src/plugins/summaryWidget/src/input/IconPalette.js index 4c2cf5e6ff7..a698767e14b 100644 --- a/src/plugins/summaryWidget/src/input/IconPalette.js +++ b/src/plugins/summaryWidget/src/input/IconPalette.js @@ -1,77 +1,75 @@ -define(['./Palette'], function (Palette) { - //The icons that will be used to instantiate this palette if none are provided - const DEFAULT_ICONS = [ - 'icon-alert-rect', - 'icon-alert-triangle', - 'icon-arrow-down', - 'icon-arrow-left', - 'icon-arrow-right', - 'icon-arrow-double-up', - 'icon-arrow-tall-up', - 'icon-arrow-tall-down', - 'icon-arrow-double-down', - 'icon-arrow-up', - 'icon-asterisk', - 'icon-bell', - 'icon-check', - 'icon-eye-open', - 'icon-gear', - 'icon-hourglass', - 'icon-info', - 'icon-link', - 'icon-lock', - 'icon-people', - 'icon-person', - 'icon-plus', - 'icon-trash', - 'icon-x' - ]; +import Palette from './Palette'; - /** - * Instantiates a new Open MCT Icon Palette input - * @constructor - * @param {string} cssClass The class name of the icon which should be applied - * to this palette - * @param {Element} container The view that contains this palette - * @param {string[]} icons (optional) A list of icons that should be used to instantiate this palette - */ - function IconPalette(cssClass, container, icons) { - this.icons = icons || DEFAULT_ICONS; - this.palette = new Palette(cssClass, container, this.icons); +//The icons that will be used to instantiate this palette if none are provided +const DEFAULT_ICONS = [ + 'icon-alert-rect', + 'icon-alert-triangle', + 'icon-arrow-down', + 'icon-arrow-left', + 'icon-arrow-right', + 'icon-arrow-double-up', + 'icon-arrow-tall-up', + 'icon-arrow-tall-down', + 'icon-arrow-double-down', + 'icon-arrow-up', + 'icon-asterisk', + 'icon-bell', + 'icon-check', + 'icon-eye-open', + 'icon-gear', + 'icon-hourglass', + 'icon-info', + 'icon-link', + 'icon-lock', + 'icon-people', + 'icon-person', + 'icon-plus', + 'icon-trash', + 'icon-x' +]; - this.palette.setNullOption(''); - this.oldIcon = this.palette.current || ''; +/** + * Instantiates a new Open MCT Icon Palette input + * @constructor + * @param {string} cssClass The class name of the icon which should be applied + * to this palette + * @param {Element} container The view that contains this palette + * @param {string[]} icons (optional) A list of icons that should be used to instantiate this palette + */ +export default function IconPalette(cssClass, container, icons) { + this.icons = icons || DEFAULT_ICONS; + this.palette = new Palette(cssClass, container, this.icons); - const domElement = this.palette.getDOM(); - const self = this; + this.palette.setNullOption(''); + this.oldIcon = this.palette.current || ''; - domElement.querySelector('.c-button--menu').classList.add('c-button--swatched'); - domElement.querySelector('.t-swatch').classList.add('icon-swatch'); - domElement.querySelector('.c-palette').classList.add('c-palette--icon'); + const domElement = this.palette.getDOM(); + const self = this; - domElement.querySelectorAll('.c-palette-item').forEach((item) => { - // eslint-disable-next-line no-invalid-this - item.classList.add(item.dataset.item); - }); + domElement.querySelector('.c-button--menu').classList.add('c-button--swatched'); + domElement.querySelector('.t-swatch').classList.add('icon-swatch'); + domElement.querySelector('.c-palette').classList.add('c-palette--icon'); - /** - * Update this palette's current selection indicator with the style - * of the currently selected item - * @private - */ - function updateSwatch() { - if (self.oldIcon) { - domElement.querySelector('.icon-swatch').classList.remove(self.oldIcon); - } + domElement.querySelectorAll('.c-palette-item').forEach((item) => { + // eslint-disable-next-line no-invalid-this + item.classList.add(item.dataset.item); + }); - domElement.querySelector('.icon-swatch').classList.add(self.palette.getCurrent()); - self.oldIcon = self.palette.getCurrent(); + /** + * Update this palette's current selection indicator with the style + * of the currently selected item + * @private + */ + function updateSwatch() { + if (self.oldIcon) { + domElement.querySelector('.icon-swatch').classList.remove(self.oldIcon); } - this.palette.on('change', updateSwatch); - - return this.palette; + domElement.querySelector('.icon-swatch').classList.add(self.palette.getCurrent()); + self.oldIcon = self.palette.getCurrent(); } - return IconPalette; -}); + this.palette.on('change', updateSwatch); + + return this.palette; +} diff --git a/src/plugins/summaryWidget/src/input/KeySelect.js b/src/plugins/summaryWidget/src/input/KeySelect.js index ae16b210a8d..04601313751 100644 --- a/src/plugins/summaryWidget/src/input/KeySelect.js +++ b/src/plugins/summaryWidget/src/input/KeySelect.js @@ -1,95 +1,93 @@ -define(['./Select'], function (Select) { - /** - * Create a {Select} element whose composition is dynamically updated with - * the telemetry fields of a particular domain object - * @constructor - * @param {Object} config The current state of this select. Must have object - * and key fields - * @param {ObjectSelect} objectSelect The linked ObjectSelect instance to which - * this KeySelect should listen to for change - * events - * @param {ConditionManager} manager A ConditionManager instance from which - * to receive telemetry metadata - * @param {function} changeCallback A change event callback to register with this - * select on initialization - */ - const NULLVALUE = '- Select Field -'; +import Select from './Select'; - function KeySelect(config, objectSelect, manager, changeCallback) { - const self = this; +/** + * Create a {Select} element whose composition is dynamically updated with + * the telemetry fields of a particular domain object + * @constructor + * @param {Object} config The current state of this select. Must have object + * and key fields + * @param {ObjectSelect} objectSelect The linked ObjectSelect instance to which + * this KeySelect should listen to for change + * events + * @param {ConditionManager} manager A ConditionManager instance from which + * to receive telemetry metadata + * @param {function} changeCallback A change event callback to register with this + * select on initialization + */ +const NULLVALUE = '- Select Field -'; - this.config = config; - this.objectSelect = objectSelect; - this.manager = manager; +export default function KeySelect(config, objectSelect, manager, changeCallback) { + const self = this; - this.select = new Select(); - this.select.hide(); - this.select.addOption('', NULLVALUE); - if (changeCallback) { - this.select.on('change', changeCallback); - } + this.config = config; + this.objectSelect = objectSelect; + this.manager = manager; - /** - * Change event handler for the {ObjectSelect} to which this KeySelect instance - * is linked. Loads the new object's metadata and updates its select element's - * composition. - * @param {Object} key The key identifying the newly selected domain object - * @private - */ - function onObjectChange(key) { - const selected = self.manager.metadataLoadCompleted() - ? self.select.getSelected() - : self.config.key; - self.telemetryMetadata = self.manager.getTelemetryMetadata(key) || {}; - self.generateOptions(); - self.select.setSelected(selected); - } - - /** - * Event handler for the initial metadata load event from the associated - * ConditionManager. Retrieves metadata from the manager and populates - * the select element. - * @private - */ - function onMetadataLoad() { - if (self.manager.getTelemetryMetadata(self.config.object)) { - self.telemetryMetadata = self.manager.getTelemetryMetadata(self.config.object); - self.generateOptions(); - } + this.select = new Select(); + this.select.hide(); + this.select.addOption('', NULLVALUE); + if (changeCallback) { + this.select.on('change', changeCallback); + } - self.select.setSelected(self.config.key); - } + /** + * Change event handler for the {ObjectSelect} to which this KeySelect instance + * is linked. Loads the new object's metadata and updates its select element's + * composition. + * @param {Object} key The key identifying the newly selected domain object + * @private + */ + function onObjectChange(key) { + const selected = self.manager.metadataLoadCompleted() + ? self.select.getSelected() + : self.config.key; + self.telemetryMetadata = self.manager.getTelemetryMetadata(key) || {}; + self.generateOptions(); + self.select.setSelected(selected); + } - if (self.manager.metadataLoadCompleted()) { - onMetadataLoad(); + /** + * Event handler for the initial metadata load event from the associated + * ConditionManager. Retrieves metadata from the manager and populates + * the select element. + * @private + */ + function onMetadataLoad() { + if (self.manager.getTelemetryMetadata(self.config.object)) { + self.telemetryMetadata = self.manager.getTelemetryMetadata(self.config.object); + self.generateOptions(); } - this.objectSelect.on('change', onObjectChange, this); - this.manager.on('metadata', onMetadataLoad); + self.select.setSelected(self.config.key); + } - return this.select; + if (self.manager.metadataLoadCompleted()) { + onMetadataLoad(); } - /** - * Populate this select with options based on its current composition - */ - KeySelect.prototype.generateOptions = function () { - const items = Object.entries(this.telemetryMetadata).map(function (metaDatum) { - return [metaDatum[0], metaDatum[1].name]; - }); - items.splice(0, 0, ['', NULLVALUE]); - this.select.setOptions(items); + this.objectSelect.on('change', onObjectChange, this); + this.manager.on('metadata', onMetadataLoad); - if (this.select.options.length < 2) { - this.select.hide(); - } else if (this.select.options.length > 1) { - this.select.show(); - } - }; + return this.select; +} + +/** + * Populate this select with options based on its current composition + */ +KeySelect.prototype.generateOptions = function () { + const items = Object.entries(this.telemetryMetadata).map(function (metaDatum) { + return [metaDatum[0], metaDatum[1].name]; + }); + items.splice(0, 0, ['', NULLVALUE]); + this.select.setOptions(items); - KeySelect.prototype.destroy = function () { - this.objectSelect.destroy(); - }; + if (this.select.options.length < 2) { + this.select.hide(); + } else if (this.select.options.length > 1) { + this.select.show(); + } +}; - return KeySelect; -}); +KeySelect.prototype.destroy = function () { + this.objectSelect.destroy(); +}; diff --git a/src/plugins/summaryWidget/src/input/ObjectSelect.js b/src/plugins/summaryWidget/src/input/ObjectSelect.js index f3bac8b3772..282bb0a9946 100644 --- a/src/plugins/summaryWidget/src/input/ObjectSelect.js +++ b/src/plugins/summaryWidget/src/input/ObjectSelect.js @@ -1,86 +1,86 @@ -define(['./Select', 'objectUtils'], function (Select, objectUtils) { - /** - * Create a {Select} element whose composition is dynamically updated with - * the current composition of the Summary Widget - * @constructor - * @param {Object} config The current state of this select. Must have an - * object field - * @param {ConditionManager} manager A ConditionManager instance from which - * to receive the current composition status - * @param {string[][]} baseOptions A set of [value, label] keyword pairs to - * display regardless of the composition state - */ - function ObjectSelect(config, manager, baseOptions) { - const self = this; - - this.config = config; - this.manager = manager; +import objectUtils from 'objectUtils'; - this.select = new Select(); - this.baseOptions = [['', '- Select Telemetry -']]; - if (baseOptions) { - this.baseOptions = this.baseOptions.concat(baseOptions); - } +import Select from './Select'; - this.baseOptions.forEach(function (option) { - self.select.addOption(option[0], option[1]); - }); - - this.compositionObjs = this.manager.getComposition(); - self.generateOptions(); +/** + * Create a {Select} element whose composition is dynamically updated with + * the current composition of the Summary Widget + * @constructor + * @param {Object} config The current state of this select. Must have an + * object field + * @param {ConditionManager} manager A ConditionManager instance from which + * to receive the current composition status + * @param {string[][]} baseOptions A set of [value, label] keyword pairs to + * display regardless of the composition state + */ +export default function ObjectSelect(config, manager, baseOptions) { + const self = this; - /** - * Add a new composition object to this select when a composition added - * is detected on the Summary Widget - * @param {Object} obj The newly added domain object - * @private - */ - function onCompositionAdd(obj) { - self.select.addOption(objectUtils.makeKeyString(obj.identifier), obj.name); - } + this.config = config; + this.manager = manager; - /** - * Refresh the composition of this select when a domain object is removed - * from the Summary Widget's composition - * @private - */ - function onCompositionRemove() { - const selected = self.select.getSelected(); - self.generateOptions(); - self.select.setSelected(selected); - } + this.select = new Select(); + this.baseOptions = [['', '- Select Telemetry -']]; + if (baseOptions) { + this.baseOptions = this.baseOptions.concat(baseOptions); + } - /** - * Defer setting the selected state on initial load until load is complete - * @private - */ - function onCompositionLoad() { - self.select.setSelected(self.config.object); - } + this.baseOptions.forEach(function (option) { + self.select.addOption(option[0], option[1]); + }); - this.manager.on('add', onCompositionAdd); - this.manager.on('remove', onCompositionRemove); - this.manager.on('load', onCompositionLoad); + this.compositionObjs = this.manager.getComposition(); + self.generateOptions(); - if (this.manager.loadCompleted()) { - onCompositionLoad(); - } + /** + * Add a new composition object to this select when a composition added + * is detected on the Summary Widget + * @param {Object} obj The newly added domain object + * @private + */ + function onCompositionAdd(obj) { + self.select.addOption(objectUtils.makeKeyString(obj.identifier), obj.name); + } - return this.select; + /** + * Refresh the composition of this select when a domain object is removed + * from the Summary Widget's composition + * @private + */ + function onCompositionRemove() { + const selected = self.select.getSelected(); + self.generateOptions(); + self.select.setSelected(selected); } /** - * Populate this select with options based on its current composition + * Defer setting the selected state on initial load until load is complete + * @private */ - ObjectSelect.prototype.generateOptions = function () { - const items = Object.values(this.compositionObjs).map(function (obj) { - return [objectUtils.makeKeyString(obj.identifier), obj.name]; - }); - this.baseOptions.forEach(function (option, index) { - items.splice(index, 0, option); - }); - this.select.setOptions(items); - }; + function onCompositionLoad() { + self.select.setSelected(self.config.object); + } + + this.manager.on('add', onCompositionAdd); + this.manager.on('remove', onCompositionRemove); + this.manager.on('load', onCompositionLoad); + + if (this.manager.loadCompleted()) { + onCompositionLoad(); + } + + return this.select; +} - return ObjectSelect; -}); +/** + * Populate this select with options based on its current composition + */ +ObjectSelect.prototype.generateOptions = function () { + const items = Object.values(this.compositionObjs).map(function (obj) { + return [objectUtils.makeKeyString(obj.identifier), obj.name]; + }); + this.baseOptions.forEach(function (option, index) { + items.splice(index, 0, option); + }); + this.select.setOptions(items); +}; diff --git a/src/plugins/summaryWidget/src/input/OperationSelect.js b/src/plugins/summaryWidget/src/input/OperationSelect.js index 0279927760f..8bf48c8f688 100644 --- a/src/plugins/summaryWidget/src/input/OperationSelect.js +++ b/src/plugins/summaryWidget/src/input/OperationSelect.js @@ -1,120 +1,119 @@ -define(['./Select', '../eventHelpers'], function (Select, eventHelpers) { +import eventHelpers from '../eventHelpers'; +import Select from './Select'; + +/** + * Create a {Select} element whose composition is dynamically updated with + * the operations applying to a particular telemetry property + * @constructor + * @param {Object} config The current state of this select. Must have object, + * key, and operation fields + * @param {KeySelect} keySelect The linked Key Select instance to which + * this OperationSelect should listen to for change + * events + * @param {ConditionManager} manager A ConditionManager instance from which + * to receive telemetry metadata + * @param {function} changeCallback A change event callback to register with this + * select on initialization + */ +const NULLVALUE = '- Select Comparison -'; + +export default function OperationSelect(config, keySelect, manager, changeCallback) { + eventHelpers.extend(this); + const self = this; + + this.config = config; + this.keySelect = keySelect; + this.manager = manager; + + this.operationKeys = []; + this.evaluator = this.manager.getEvaluator(); + this.loadComplete = false; + + this.select = new Select(); + this.select.hide(); + this.select.addOption('', NULLVALUE); + if (changeCallback) { + this.listenTo(this.select, 'change', changeCallback); + } + /** - * Create a {Select} element whose composition is dynamically updated with - * the operations applying to a particular telemetry property - * @constructor - * @param {Object} config The current state of this select. Must have object, - * key, and operation fields - * @param {KeySelect} keySelect The linked Key Select instance to which - * this OperationSelect should listen to for change - * events - * @param {ConditionManager} manager A ConditionManager instance from which - * to receive telemetry metadata - * @param {function} changeCallback A change event callback to register with this - * select on initialization + * Change event handler for the {KeySelect} to which this OperationSelect instance + * is linked. Loads the operations applicable to the given telemetry property and updates + * its select element's composition + * @param {Object} key The key identifying the newly selected property + * @private */ - const NULLVALUE = '- Select Comparison -'; - - function OperationSelect(config, keySelect, manager, changeCallback) { - eventHelpers.extend(this); - const self = this; - - this.config = config; - this.keySelect = keySelect; - this.manager = manager; - - this.operationKeys = []; - this.evaluator = this.manager.getEvaluator(); - this.loadComplete = false; - - this.select = new Select(); - this.select.hide(); - this.select.addOption('', NULLVALUE); - if (changeCallback) { - this.listenTo(this.select, 'change', changeCallback); - } - - /** - * Change event handler for the {KeySelect} to which this OperationSelect instance - * is linked. Loads the operations applicable to the given telemetry property and updates - * its select element's composition - * @param {Object} key The key identifying the newly selected property - * @private - */ - function onKeyChange(key) { - const selected = self.config.operation; - if (self.manager.metadataLoadCompleted()) { - self.loadOptions(key); - self.generateOptions(); - self.select.setSelected(selected); - } - } - - /** - * Event handler for the initial metadata load event from the associated - * ConditionManager. Retrieves telemetry property types and updates the - * select - * @private - */ - function onMetadataLoad() { - if (self.manager.getTelemetryPropertyType(self.config.object, self.config.key)) { - self.loadOptions(self.config.key); - self.generateOptions(); - } - - self.select.setSelected(self.config.operation); - } - - this.keySelect.on('change', onKeyChange); - this.manager.on('metadata', onMetadataLoad); - - if (this.manager.metadataLoadCompleted()) { - onMetadataLoad(); + function onKeyChange(key) { + const selected = self.config.operation; + if (self.manager.metadataLoadCompleted()) { + self.loadOptions(key); + self.generateOptions(); + self.select.setSelected(selected); } - - return this.select; } /** - * Populate this select with options based on its current composition + * Event handler for the initial metadata load event from the associated + * ConditionManager. Retrieves telemetry property types and updates the + * select + * @private */ - OperationSelect.prototype.generateOptions = function () { - const self = this; - const items = this.operationKeys.map(function (operation) { - return [operation, self.evaluator.getOperationText(operation)]; - }); - items.splice(0, 0, ['', NULLVALUE]); - this.select.setOptions(items); - - if (this.select.options.length < 2) { - this.select.hide(); - } else { - this.select.show(); + function onMetadataLoad() { + if (self.manager.getTelemetryPropertyType(self.config.object, self.config.key)) { + self.loadOptions(self.config.key); + self.generateOptions(); } - }; - /** - * Retrieve the data type associated with a given telemetry property and - * the applicable operations from the {ConditionEvaluator} - * @param {string} key The telemetry property to load operations for - */ - OperationSelect.prototype.loadOptions = function (key) { - const self = this; - const operations = self.evaluator.getOperationKeys(); - let type; + self.select.setSelected(self.config.operation); + } - type = self.manager.getTelemetryPropertyType(self.config.object, key); + this.keySelect.on('change', onKeyChange); + this.manager.on('metadata', onMetadataLoad); - if (type !== undefined) { - self.operationKeys = operations.filter(function (operation) { - return self.evaluator.operationAppliesTo(operation, type); - }); - } - }; + if (this.manager.metadataLoadCompleted()) { + onMetadataLoad(); + } - OperationSelect.prototype.destroy = function () { - this.stopListening(); - }; + return this.select; +} + +/** + * Populate this select with options based on its current composition + */ +OperationSelect.prototype.generateOptions = function () { + const self = this; + const items = this.operationKeys.map(function (operation) { + return [operation, self.evaluator.getOperationText(operation)]; + }); + items.splice(0, 0, ['', NULLVALUE]); + this.select.setOptions(items); + + if (this.select.options.length < 2) { + this.select.hide(); + } else { + this.select.show(); + } +}; + +/** + * Retrieve the data type associated with a given telemetry property and + * the applicable operations from the {ConditionEvaluator} + * @param {string} key The telemetry property to load operations for + */ +OperationSelect.prototype.loadOptions = function (key) { + const self = this; + const operations = self.evaluator.getOperationKeys(); + let type; + + type = self.manager.getTelemetryPropertyType(self.config.object, key); + + if (type !== undefined) { + self.operationKeys = operations.filter(function (operation) { + return self.evaluator.operationAppliesTo(operation, type); + }); + } +}; - return OperationSelect; -}); +OperationSelect.prototype.destroy = function () { + this.stopListening(); +}; diff --git a/src/plugins/summaryWidget/src/input/Palette.js b/src/plugins/summaryWidget/src/input/Palette.js index 57a172858e7..cfb4adc3ab0 100644 --- a/src/plugins/summaryWidget/src/input/Palette.js +++ b/src/plugins/summaryWidget/src/input/Palette.js @@ -1,183 +1,180 @@ -define([ - '../eventHelpers', - '../../res/input/paletteTemplate.html', - '../../../../utils/template/templateHelpers', - 'EventEmitter' -], function (eventHelpers, paletteTemplate, templateHelpers, EventEmitter) { - /** - * Instantiates a new Open MCT Color Palette input - * @constructor - * @param {string} cssClass The class name of the icon which should be applied - * to this palette - * @param {Element} container The view that contains this palette - * @param {string[]} items A list of data items that will be associated with each - * palette item in the view; how this data is represented is - * up to the descendent class - */ - function Palette(cssClass, container, items) { - eventHelpers.extend(this); - - const self = this; - - this.cssClass = cssClass; - this.items = items; - this.container = container; - - this.domElement = templateHelpers.convertTemplateToHTML(paletteTemplate)[0]; - - this.itemElements = { - nullOption: this.domElement.querySelector('.c-palette__item-none .c-palette__item') - }; - this.eventEmitter = new EventEmitter(); - this.supportedCallbacks = ['change']; - this.value = this.items[0]; - this.nullOption = ' '; - this.button = this.domElement.querySelector('.js-button'); - this.menu = this.domElement.querySelector('.c-menu'); - - this.hideMenu = this.hideMenu.bind(this); - - if (this.cssClass) { - self.button.classList.add(this.cssClass); - } - - self.setNullOption(this.nullOption); - - self.items.forEach(function (item) { - const itemElement = `
`; - const temp = document.createElement('div'); - temp.innerHTML = itemElement; - self.itemElements[item] = temp.firstChild; - self.domElement.querySelector('.c-palette__items').appendChild(temp.firstChild); - }); - - self.domElement.querySelector('.c-menu').style.display = 'none'; +import EventEmitter from 'EventEmitter'; + +import * as templateHelpers from '../../../../utils/template/templateHelpers'; +import paletteTemplate from '../../res/input/paletteTemplate.html'; +import eventHelpers from '../eventHelpers'; + +/** + * Instantiates a new Open MCT Color Palette input + * @constructor + * @param {string} cssClass The class name of the icon which should be applied + * to this palette + * @param {Element} container The view that contains this palette + * @param {string[]} items A list of data items that will be associated with each + * palette item in the view; how this data is represented is + * up to the descendent class + */ +export default function Palette(cssClass, container, items) { + eventHelpers.extend(this); + + const self = this; + + this.cssClass = cssClass; + this.items = items; + this.container = container; + + this.domElement = templateHelpers.convertTemplateToHTML(paletteTemplate)[0]; + + this.itemElements = { + nullOption: this.domElement.querySelector('.c-palette__item-none .c-palette__item') + }; + this.eventEmitter = new EventEmitter(); + this.supportedCallbacks = ['change']; + this.value = this.items[0]; + this.nullOption = ' '; + this.button = this.domElement.querySelector('.js-button'); + this.menu = this.domElement.querySelector('.c-menu'); - this.listenTo(window.document, 'click', this.hideMenu); - this.listenTo(self.domElement.querySelector('.js-button'), 'click', function (event) { - event.stopPropagation(); - self.container.querySelector('.c-menu').style.display = 'none'; - self.domElement.querySelector('.c-menu').style.display = ''; - }); - - /** - * Event handler for selection of an individual palette item. Sets the - * currently selected element to be the one associated with that item's data - * @param {Event} event the click event that initiated this callback - * @private - */ - function handleItemClick(event) { - const elem = event.currentTarget; - const item = elem.dataset.item; - self.set(item); - self.domElement.querySelector('.c-menu').style.display = 'none'; - } + this.hideMenu = this.hideMenu.bind(this); - self.domElement.querySelectorAll('.c-palette__item').forEach((item) => { - this.listenTo(item, 'click', handleItemClick); - }); + if (this.cssClass) { + self.button.classList.add(this.cssClass); } - /** - * Get the DOM element representing this palette in the view - */ - Palette.prototype.getDOM = function () { - return this.domElement; - }; + self.setNullOption(this.nullOption); - /** - * Clean up any event listeners registered to DOM elements external to the widget - */ - Palette.prototype.destroy = function () { - this.stopListening(); - }; - - Palette.prototype.hideMenu = function () { - this.domElement.querySelector('.c-menu').style.display = 'none'; - }; + self.items.forEach(function (item) { + const itemElement = `
`; + const temp = document.createElement('div'); + temp.innerHTML = itemElement; + self.itemElements[item] = temp.firstChild; + self.domElement.querySelector('.c-palette__items').appendChild(temp.firstChild); + }); - /** - * Register a callback with this palette: supported callback is change - * @param {string} event The key for the event to listen to - * @param {function} callback The function that this rule will invoke on this event - * @param {Object} context A reference to a scope to use as the context for - * context for the callback function - */ - Palette.prototype.on = function (event, callback, context) { - if (this.supportedCallbacks.includes(event)) { - this.eventEmitter.on(event, callback, context || this); - } else { - throw new Error('Unsupported event type: ' + event); - } - }; + self.domElement.querySelector('.c-menu').style.display = 'none'; - /** - * Get the currently selected value of this palette - * @return {string} The selected value - */ - Palette.prototype.getCurrent = function () { - return this.value; - }; + this.listenTo(window.document, 'click', this.hideMenu); + this.listenTo(self.domElement.querySelector('.js-button'), 'click', function (event) { + event.stopPropagation(); + self.container.querySelector('.c-menu').style.display = 'none'; + self.domElement.querySelector('.c-menu').style.display = ''; + }); /** - * Set the selected value of this palette; if the item doesn't exist in the - * palette's data model, the selected value will not change. Invokes any - * change callbacks associated with this palette. - * @param {string} item The key of the item to set as selected + * Event handler for selection of an individual palette item. Sets the + * currently selected element to be the one associated with that item's data + * @param {Event} event the click event that initiated this callback + * @private */ - Palette.prototype.set = function (item) { - const self = this; - if (this.items.includes(item) || item === this.nullOption) { - this.value = item; - if (item === this.nullOption) { - this.updateSelected('nullOption'); - } else { - this.updateSelected(item); - } - } - - this.eventEmitter.emit('change', self.value); - }; + function handleItemClick(event) { + const elem = event.currentTarget; + const item = elem.dataset.item; + self.set(item); + self.domElement.querySelector('.c-menu').style.display = 'none'; + } - /** - * Update the view associated with the currently selected item - */ - Palette.prototype.updateSelected = function (item) { - this.domElement.querySelectorAll('.c-palette__item').forEach((paletteItem) => { - if (paletteItem.classList.contains('is-selected')) { - paletteItem.classList.remove('is-selected'); - } - }); - this.itemElements[item].classList.add('is-selected'); - if (item === 'nullOption') { - this.domElement.querySelector('.t-swatch').classList.add('no-selection'); + self.domElement.querySelectorAll('.c-palette__item').forEach((item) => { + this.listenTo(item, 'click', handleItemClick); + }); +} + +/** + * Get the DOM element representing this palette in the view + */ +Palette.prototype.getDOM = function () { + return this.domElement; +}; + +/** + * Clean up any event listeners registered to DOM elements external to the widget + */ +Palette.prototype.destroy = function () { + this.stopListening(); +}; + +Palette.prototype.hideMenu = function () { + this.domElement.querySelector('.c-menu').style.display = 'none'; +}; + +/** + * Register a callback with this palette: supported callback is change + * @param {string} event The key for the event to listen to + * @param {function} callback The function that this rule will invoke on this event + * @param {Object} context A reference to a scope to use as the context for + * context for the callback function + */ +Palette.prototype.on = function (event, callback, context) { + if (this.supportedCallbacks.includes(event)) { + this.eventEmitter.on(event, callback, context || this); + } else { + throw new Error('Unsupported event type: ' + event); + } +}; + +/** + * Get the currently selected value of this palette + * @return {string} The selected value + */ +Palette.prototype.getCurrent = function () { + return this.value; +}; + +/** + * Set the selected value of this palette; if the item doesn't exist in the + * palette's data model, the selected value will not change. Invokes any + * change callbacks associated with this palette. + * @param {string} item The key of the item to set as selected + */ +Palette.prototype.set = function (item) { + const self = this; + if (this.items.includes(item) || item === this.nullOption) { + this.value = item; + if (item === this.nullOption) { + this.updateSelected('nullOption'); } else { - this.domElement.querySelector('.t-swatch').classList.remove('no-selection'); + this.updateSelected(item); } - }; + } - /** - * set the property to be used for the 'no selection' item. If not set, this - * defaults to a single space - * @param {string} item The key to use as the 'no selection' item - */ - Palette.prototype.setNullOption = function (item) { - this.nullOption = item; - this.itemElements.nullOption.data = { item: item }; - }; + this.eventEmitter.emit('change', self.value); +}; - /** - * Hides the 'no selection' option to be hidden in the view if it doesn't apply - */ - Palette.prototype.toggleNullOption = function () { - const elem = this.domElement.querySelector('.c-palette__item-none'); - - if (elem.style.display === 'none') { - this.domElement.querySelector('.c-palette__item-none').style.display = 'flex'; - } else { - this.domElement.querySelector('.c-palette__item-none').style.display = 'none'; +/** + * Update the view associated with the currently selected item + */ +Palette.prototype.updateSelected = function (item) { + this.domElement.querySelectorAll('.c-palette__item').forEach((paletteItem) => { + if (paletteItem.classList.contains('is-selected')) { + paletteItem.classList.remove('is-selected'); } - }; - - return Palette; -}); + }); + this.itemElements[item].classList.add('is-selected'); + if (item === 'nullOption') { + this.domElement.querySelector('.t-swatch').classList.add('no-selection'); + } else { + this.domElement.querySelector('.t-swatch').classList.remove('no-selection'); + } +}; + +/** + * set the property to be used for the 'no selection' item. If not set, this + * defaults to a single space + * @param {string} item The key to use as the 'no selection' item + */ +Palette.prototype.setNullOption = function (item) { + this.nullOption = item; + this.itemElements.nullOption.data = { item: item }; +}; + +/** + * Hides the 'no selection' option to be hidden in the view if it doesn't apply + */ +Palette.prototype.toggleNullOption = function () { + const elem = this.domElement.querySelector('.c-palette__item-none'); + + if (elem.style.display === 'none') { + this.domElement.querySelector('.c-palette__item-none').style.display = 'flex'; + } else { + this.domElement.querySelector('.c-palette__item-none').style.display = 'none'; + } +}; diff --git a/src/plugins/summaryWidget/src/input/Select.js b/src/plugins/summaryWidget/src/input/Select.js index 5f3e8850935..525701605fd 100644 --- a/src/plugins/summaryWidget/src/input/Select.js +++ b/src/plugins/summaryWidget/src/input/Select.js @@ -1,154 +1,151 @@ -define([ - '../eventHelpers', - '../../res/input/selectTemplate.html', - '../../../../utils/template/templateHelpers', - 'EventEmitter' -], function (eventHelpers, selectTemplate, templateHelpers, EventEmitter) { - /** - * Wraps an HTML select element, and provides methods for dynamically altering - * its composition from the data model - * @constructor - */ - function Select() { - eventHelpers.extend(this); - - const self = this; +import EventEmitter from 'EventEmitter'; - this.domElement = templateHelpers.convertTemplateToHTML(selectTemplate)[0]; +import * as templateHelpers from '../../../../utils/template/templateHelpers'; +import * as selectTemplate from '../../res/input/selectTemplate.html'; +import eventHelpers from '../eventHelpers'; - this.options = []; - this.eventEmitter = new EventEmitter(); - this.supportedCallbacks = ['change']; +/** + * Wraps an HTML select element, and provides methods for dynamically altering + * its composition from the data model + * @constructor + */ +export default function Select() { + eventHelpers.extend(this); - this.populate(); + const self = this; - /** - * Event handler for the wrapped select element. Also invokes any change - * callbacks registered with this select with the new value - * @param {Event} event The change event that triggered this callback - * @private - */ - function onChange(event) { - const elem = event.target; - const value = self.options[elem.selectedIndex]; - - self.eventEmitter.emit('change', value[0]); - } + this.domElement = templateHelpers.convertTemplateToHTML(selectTemplate)[0]; - this.listenTo(this.domElement.querySelector('select'), 'change', onChange, this); - } - - /** - * Get the DOM element representing this Select in the view - * @return {Element} - */ - Select.prototype.getDOM = function () { - return this.domElement; - }; + this.options = []; + this.eventEmitter = new EventEmitter(); + this.supportedCallbacks = ['change']; - /** - * Register a callback with this select: supported callback is change - * @param {string} event The key for the event to listen to - * @param {function} callback The function that this rule will invoke on this event - * @param {Object} context A reference to a scope to use as the context for - * context for the callback function - */ - Select.prototype.on = function (event, callback, context) { - if (this.supportedCallbacks.includes(event)) { - this.eventEmitter.on(event, callback, context || this); - } else { - throw new Error('Unsupported event type' + event); - } - }; + this.populate(); /** - * Update the select element in the view from the current state of the data - * model + * Event handler for the wrapped select element. Also invokes any change + * callbacks registered with this select with the new value + * @param {Event} event The change event that triggered this callback + * @private */ - Select.prototype.populate = function () { - const self = this; - let selectedIndex = 0; - - selectedIndex = this.domElement.querySelector('select').selectedIndex; - - this.domElement.querySelector('select').innerHTML = ''; + function onChange(event) { + const elem = event.target; + const value = self.options[elem.selectedIndex]; - self.options.forEach(function (option) { - const optionElement = document.createElement('option'); - optionElement.value = option[0]; - optionElement.innerText = `+ ${option[1]}`; - - self.domElement.querySelector('select').appendChild(optionElement); - }); - - this.domElement.querySelector('select').selectedIndex = selectedIndex; - }; - - /** - * Add a single option to this select - * @param {string} value The value for the new option - * @param {string} label The human-readable text for the new option - */ - Select.prototype.addOption = function (value, label) { - this.options.push([value, label]); - this.populate(); - }; - - /** - * Set the available options for this select. Replaces any existing options - * @param {string[][]} options An array of [value, label] pairs to display - */ - Select.prototype.setOptions = function (options) { - this.options = options; - this.populate(); - }; - - /** - * Sets the currently selected element an invokes any registered change - * callbacks with the new value. If the value doesn't exist in this select's - * model, its state will not change. - * @param {string} value The value to set as the selected option - */ - Select.prototype.setSelected = function (value) { - let selectedIndex = 0; - let selectedOption; - - this.options.forEach(function (option, index) { - if (option[0] === value) { - selectedIndex = index; - } - }); - this.domElement.querySelector('select').selectedIndex = selectedIndex; - - selectedOption = this.options[selectedIndex]; - this.eventEmitter.emit('change', selectedOption[0]); - }; - - /** - * Get the value of the currently selected item - * @return {string} - */ - Select.prototype.getSelected = function () { - return this.domElement.querySelector('select').value; - }; - - Select.prototype.hide = function () { - this.domElement.classList.add('hidden'); - if (this.domElement.querySelector('.equal-to')) { - this.domElement.querySelector('.equal-to').classList.add('hidden'); - } - }; + self.eventEmitter.emit('change', value[0]); + } - Select.prototype.show = function () { - this.domElement.classList.remove('hidden'); - if (this.domElement.querySelector('.equal-to')) { - this.domElement.querySelector('.equal-to').classList.remove('hidden'); + this.listenTo(this.domElement.querySelector('select'), 'change', onChange, this); +} + +/** + * Get the DOM element representing this Select in the view + * @return {Element} + */ +Select.prototype.getDOM = function () { + return this.domElement; +}; + +/** + * Register a callback with this select: supported callback is change + * @param {string} event The key for the event to listen to + * @param {function} callback The function that this rule will invoke on this event + * @param {Object} context A reference to a scope to use as the context for + * context for the callback function + */ +Select.prototype.on = function (event, callback, context) { + if (this.supportedCallbacks.includes(event)) { + this.eventEmitter.on(event, callback, context || this); + } else { + throw new Error('Unsupported event type' + event); + } +}; + +/** + * Update the select element in the view from the current state of the data + * model + */ +Select.prototype.populate = function () { + const self = this; + let selectedIndex = 0; + + selectedIndex = this.domElement.querySelector('select').selectedIndex; + + this.domElement.querySelector('select').innerHTML = ''; + + self.options.forEach(function (option) { + const optionElement = document.createElement('option'); + optionElement.value = option[0]; + optionElement.innerText = `+ ${option[1]}`; + + self.domElement.querySelector('select').appendChild(optionElement); + }); + + this.domElement.querySelector('select').selectedIndex = selectedIndex; +}; + +/** + * Add a single option to this select + * @param {string} value The value for the new option + * @param {string} label The human-readable text for the new option + */ +Select.prototype.addOption = function (value, label) { + this.options.push([value, label]); + this.populate(); +}; + +/** + * Set the available options for this select. Replaces any existing options + * @param {string[][]} options An array of [value, label] pairs to display + */ +Select.prototype.setOptions = function (options) { + this.options = options; + this.populate(); +}; + +/** + * Sets the currently selected element an invokes any registered change + * callbacks with the new value. If the value doesn't exist in this select's + * model, its state will not change. + * @param {string} value The value to set as the selected option + */ +Select.prototype.setSelected = function (value) { + let selectedIndex = 0; + let selectedOption; + + this.options.forEach(function (option, index) { + if (option[0] === value) { + selectedIndex = index; } - }; + }); + this.domElement.querySelector('select').selectedIndex = selectedIndex; + + selectedOption = this.options[selectedIndex]; + this.eventEmitter.emit('change', selectedOption[0]); +}; + +/** + * Get the value of the currently selected item + * @return {string} + */ +Select.prototype.getSelected = function () { + return this.domElement.querySelector('select').value; +}; + +Select.prototype.hide = function () { + this.domElement.classList.add('hidden'); + if (this.domElement.querySelector('.equal-to')) { + this.domElement.querySelector('.equal-to').classList.add('hidden'); + } +}; - Select.prototype.destroy = function () { - this.stopListening(); - }; +Select.prototype.show = function () { + this.domElement.classList.remove('hidden'); + if (this.domElement.querySelector('.equal-to')) { + this.domElement.querySelector('.equal-to').classList.remove('hidden'); + } +}; - return Select; -}); +Select.prototype.destroy = function () { + this.stopListening(); +}; diff --git a/src/plugins/summaryWidget/src/telemetry/EvaluatorPool.js b/src/plugins/summaryWidget/src/telemetry/EvaluatorPool.js index b6cac0595ef..6a99653a60a 100644 --- a/src/plugins/summaryWidget/src/telemetry/EvaluatorPool.js +++ b/src/plugins/summaryWidget/src/telemetry/EvaluatorPool.js @@ -20,40 +20,40 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define(['./SummaryWidgetEvaluator', 'objectUtils'], function (SummaryWidgetEvaluator, objectUtils) { - function EvaluatorPool(openmct) { - this.openmct = openmct; - this.byObjectId = {}; - this.byEvaluator = new WeakMap(); - } +import objectUtils from 'objectUtils'; + +import SummaryWidgetEvaluator from './SummaryWidgetEvaluator'; - EvaluatorPool.prototype.get = function (domainObject) { - const objectId = objectUtils.makeKeyString(domainObject.identifier); - let poolEntry = this.byObjectId[objectId]; - if (!poolEntry) { - poolEntry = { - leases: 0, - objectId: objectId, - evaluator: new SummaryWidgetEvaluator(domainObject, this.openmct) - }; - this.byEvaluator.set(poolEntry.evaluator, poolEntry); - this.byObjectId[objectId] = poolEntry; - } +export default function EvaluatorPool(openmct) { + this.openmct = openmct; + this.byObjectId = {}; + this.byEvaluator = new WeakMap(); +} - poolEntry.leases += 1; +EvaluatorPool.prototype.get = function (domainObject) { + const objectId = objectUtils.makeKeyString(domainObject.identifier); + let poolEntry = this.byObjectId[objectId]; + if (!poolEntry) { + poolEntry = { + leases: 0, + objectId: objectId, + evaluator: new SummaryWidgetEvaluator(domainObject, this.openmct) + }; + this.byEvaluator.set(poolEntry.evaluator, poolEntry); + this.byObjectId[objectId] = poolEntry; + } - return poolEntry.evaluator; - }; + poolEntry.leases += 1; - EvaluatorPool.prototype.release = function (evaluator) { - const poolEntry = this.byEvaluator.get(evaluator); - poolEntry.leases -= 1; - if (poolEntry.leases === 0) { - evaluator.destroy(); - this.byEvaluator.delete(evaluator); - delete this.byObjectId[poolEntry.objectId]; - } - }; + return poolEntry.evaluator; +}; - return EvaluatorPool; -}); +EvaluatorPool.prototype.release = function (evaluator) { + const poolEntry = this.byEvaluator.get(evaluator); + poolEntry.leases -= 1; + if (poolEntry.leases === 0) { + evaluator.destroy(); + this.byEvaluator.delete(evaluator); + delete this.byObjectId[poolEntry.objectId]; + } +}; diff --git a/src/plugins/summaryWidget/src/telemetry/EvaluatorPoolSpec.js b/src/plugins/summaryWidget/src/telemetry/EvaluatorPoolSpec.js index d5b33982674..6f1a01ee764 100644 --- a/src/plugins/summaryWidget/src/telemetry/EvaluatorPoolSpec.js +++ b/src/plugins/summaryWidget/src/telemetry/EvaluatorPoolSpec.js @@ -20,78 +20,75 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define(['./EvaluatorPool', './SummaryWidgetEvaluator'], function ( - EvaluatorPool, - SummaryWidgetEvaluator -) { - describe('EvaluatorPool', function () { - let pool; - let openmct; - let objectA; - let objectB; +import EvaluatorPool from './EvaluatorPool'; - beforeEach(function () { - openmct = { - composition: jasmine.createSpyObj('compositionAPI', ['get']), - objects: jasmine.createSpyObj('objectAPI', ['observe']) - }; - openmct.composition.get.and.callFake(function () { - const compositionCollection = jasmine.createSpyObj('compositionCollection', [ - 'load', - 'on', - 'off' - ]); - compositionCollection.load.and.returnValue(Promise.resolve()); +describe('EvaluatorPool', function () { + let pool; + let openmct; + let objectA; + let objectB; - return compositionCollection; - }); - openmct.objects.observe.and.callFake(function () { - return function () {}; - }); - pool = new EvaluatorPool(openmct); - objectA = { - identifier: { - namespace: 'someNamespace', - key: 'someKey' - }, - configuration: { - ruleOrder: [] - } - }; - objectB = { - identifier: { - namespace: 'otherNamespace', - key: 'otherKey' - }, - configuration: { - ruleOrder: [] - } - }; - }); + beforeEach(function () { + openmct = { + composition: jasmine.createSpyObj('compositionAPI', ['get']), + objects: jasmine.createSpyObj('objectAPI', ['observe']) + }; + openmct.composition.get.and.callFake(function () { + const compositionCollection = jasmine.createSpyObj('compositionCollection', [ + 'load', + 'on', + 'off' + ]); + compositionCollection.load.and.returnValue(Promise.resolve()); - it('returns new evaluators for different objects', function () { - const evaluatorA = pool.get(objectA); - const evaluatorB = pool.get(objectB); - expect(evaluatorA).not.toBe(evaluatorB); + return compositionCollection; + }); + openmct.objects.observe.and.callFake(function () { + return function () {}; }); + pool = new EvaluatorPool(openmct); + objectA = { + identifier: { + namespace: 'someNamespace', + key: 'someKey' + }, + configuration: { + ruleOrder: [] + } + }; + objectB = { + identifier: { + namespace: 'otherNamespace', + key: 'otherKey' + }, + configuration: { + ruleOrder: [] + } + }; + }); + + it('returns new evaluators for different objects', function () { + const evaluatorA = pool.get(objectA); + const evaluatorB = pool.get(objectB); + expect(evaluatorA).not.toBe(evaluatorB); + }); - it('returns the same evaluator for the same object', function () { - const evaluatorA = pool.get(objectA); - const evaluatorB = pool.get(objectA); - expect(evaluatorA).toBe(evaluatorB); + it('returns the same evaluator for the same object', function () { + const evaluatorA = pool.get(objectA); + const evaluatorB = pool.get(objectA); + expect(evaluatorA).toBe(evaluatorB); - const evaluatorC = pool.get(JSON.parse(JSON.stringify(objectA))); - expect(evaluatorA).toBe(evaluatorC); - }); + const evaluatorC = pool.get(JSON.parse(JSON.stringify(objectA))); + expect(evaluatorA).toBe(evaluatorC); + }); - it('returns new evaluator when old is released', function () { - const evaluatorA = pool.get(objectA); - const evaluatorB = pool.get(objectA); - expect(evaluatorA).toBe(evaluatorB); - pool.release(evaluatorA); - pool.release(evaluatorB); - const evaluatorC = pool.get(objectA); - expect(evaluatorA).not.toBe(evaluatorC); - }); + it('returns new evaluator when old is released', function () { + const evaluatorA = pool.get(objectA); + const evaluatorB = pool.get(objectA); + expect(evaluatorA).toBe(evaluatorB); + pool.release(evaluatorA); + pool.release(evaluatorB); + const evaluatorC = pool.get(objectA); + expect(evaluatorA).not.toBe(evaluatorC); }); }); diff --git a/src/plugins/summaryWidget/src/telemetry/SummaryWidgetCondition.js b/src/plugins/summaryWidget/src/telemetry/SummaryWidgetCondition.js index 621e656d376..59c1e81da33 100644 --- a/src/plugins/summaryWidget/src/telemetry/SummaryWidgetCondition.js +++ b/src/plugins/summaryWidget/src/telemetry/SummaryWidgetCondition.js @@ -20,57 +20,55 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define(['./operations'], function (OPERATIONS) { - function SummaryWidgetCondition(definition) { - this.object = definition.object; - this.key = definition.key; - this.values = definition.values; - if (!definition.operation) { - // TODO: better handling for default rule. - this.evaluate = function () { - return true; - }; - } else { - this.comparator = OPERATIONS[definition.operation].operation; - } +import OPERATIONS from './operations'; + +export default function SummaryWidgetCondition(definition) { + this.object = definition.object; + this.key = definition.key; + this.values = definition.values; + if (!definition.operation) { + // TODO: better handling for default rule. + this.evaluate = function () { + return true; + }; + } else { + this.comparator = OPERATIONS[definition.operation].operation; } +} - SummaryWidgetCondition.prototype.evaluate = function (telemetryState) { - const stateKeys = Object.keys(telemetryState); - let state; - let result; - let i; +SummaryWidgetCondition.prototype.evaluate = function (telemetryState) { + const stateKeys = Object.keys(telemetryState); + let state; + let result; + let i; - if (this.object === 'any') { - for (i = 0; i < stateKeys.length; i++) { - state = telemetryState[stateKeys[i]]; - result = this.evaluateState(state); - if (result) { - return true; - } + if (this.object === 'any') { + for (i = 0; i < stateKeys.length; i++) { + state = telemetryState[stateKeys[i]]; + result = this.evaluateState(state); + if (result) { + return true; } + } - return false; - } else if (this.object === 'all') { - for (i = 0; i < stateKeys.length; i++) { - state = telemetryState[stateKeys[i]]; - result = this.evaluateState(state); - if (!result) { - return false; - } + return false; + } else if (this.object === 'all') { + for (i = 0; i < stateKeys.length; i++) { + state = telemetryState[stateKeys[i]]; + result = this.evaluateState(state); + if (!result) { + return false; } - - return true; - } else { - return this.evaluateState(telemetryState[this.object]); } - }; - SummaryWidgetCondition.prototype.evaluateState = function (state) { - const testValues = [state.formats[this.key].parse(state.lastDatum)].concat(this.values); + return true; + } else { + return this.evaluateState(telemetryState[this.object]); + } +}; - return this.comparator(testValues); - }; +SummaryWidgetCondition.prototype.evaluateState = function (state) { + const testValues = [state.formats[this.key].parse(state.lastDatum)].concat(this.values); - return SummaryWidgetCondition; -}); + return this.comparator(testValues); +}; diff --git a/src/plugins/summaryWidget/src/telemetry/SummaryWidgetConditionSpec.js b/src/plugins/summaryWidget/src/telemetry/SummaryWidgetConditionSpec.js index 9bf35a3f278..687725fa442 100644 --- a/src/plugins/summaryWidget/src/telemetry/SummaryWidgetConditionSpec.js +++ b/src/plugins/summaryWidget/src/telemetry/SummaryWidgetConditionSpec.js @@ -20,106 +20,106 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define(['./SummaryWidgetCondition'], function (SummaryWidgetCondition) { - describe('SummaryWidgetCondition', function () { - let condition; - let telemetryState; +import SummaryWidgetCondition from './SummaryWidgetCondition'; - beforeEach(function () { - // Format map intentionally uses different keys than those present - // in datum, which serves to verify conditions use format map to get - // data. - const formatMap = { - adjusted: { - parse: function (datum) { - return datum.value + 10; - } - }, - raw: { - parse: function (datum) { - return datum.value; - } - } - }; +describe('SummaryWidgetCondition', function () { + let condition; + let telemetryState; - telemetryState = { - objectId: { - formats: formatMap, - lastDatum: {} - }, - otherObjectId: { - formats: formatMap, - lastDatum: {} + beforeEach(function () { + // Format map intentionally uses different keys than those present + // in datum, which serves to verify conditions use format map to get + // data. + const formatMap = { + adjusted: { + parse: function (datum) { + return datum.value + 10; } - }; - }); + }, + raw: { + parse: function (datum) { + return datum.value; + } + } + }; + + telemetryState = { + objectId: { + formats: formatMap, + lastDatum: {} + }, + otherObjectId: { + formats: formatMap, + lastDatum: {} + } + }; + }); - it('can evaluate if a single object matches', function () { - condition = new SummaryWidgetCondition({ - object: 'objectId', - key: 'raw', - operation: 'greaterThan', - values: [10] - }); - telemetryState.objectId.lastDatum.value = 5; - expect(condition.evaluate(telemetryState)).toBe(false); - telemetryState.objectId.lastDatum.value = 15; - expect(condition.evaluate(telemetryState)).toBe(true); + it('can evaluate if a single object matches', function () { + condition = new SummaryWidgetCondition({ + object: 'objectId', + key: 'raw', + operation: 'greaterThan', + values: [10] }); + telemetryState.objectId.lastDatum.value = 5; + expect(condition.evaluate(telemetryState)).toBe(false); + telemetryState.objectId.lastDatum.value = 15; + expect(condition.evaluate(telemetryState)).toBe(true); + }); - it('can evaluate if a single object matches (alternate keys)', function () { - condition = new SummaryWidgetCondition({ - object: 'objectId', - key: 'adjusted', - operation: 'greaterThan', - values: [10] - }); - telemetryState.objectId.lastDatum.value = -5; - expect(condition.evaluate(telemetryState)).toBe(false); - telemetryState.objectId.lastDatum.value = 5; - expect(condition.evaluate(telemetryState)).toBe(true); + it('can evaluate if a single object matches (alternate keys)', function () { + condition = new SummaryWidgetCondition({ + object: 'objectId', + key: 'adjusted', + operation: 'greaterThan', + values: [10] }); + telemetryState.objectId.lastDatum.value = -5; + expect(condition.evaluate(telemetryState)).toBe(false); + telemetryState.objectId.lastDatum.value = 5; + expect(condition.evaluate(telemetryState)).toBe(true); + }); - it('can evaluate "if all objects match"', function () { - condition = new SummaryWidgetCondition({ - object: 'all', - key: 'raw', - operation: 'greaterThan', - values: [10] - }); - telemetryState.objectId.lastDatum.value = 0; - telemetryState.otherObjectId.lastDatum.value = 0; - expect(condition.evaluate(telemetryState)).toBe(false); - telemetryState.objectId.lastDatum.value = 0; - telemetryState.otherObjectId.lastDatum.value = 15; - expect(condition.evaluate(telemetryState)).toBe(false); - telemetryState.objectId.lastDatum.value = 15; - telemetryState.otherObjectId.lastDatum.value = 0; - expect(condition.evaluate(telemetryState)).toBe(false); - telemetryState.objectId.lastDatum.value = 15; - telemetryState.otherObjectId.lastDatum.value = 15; - expect(condition.evaluate(telemetryState)).toBe(true); + it('can evaluate "if all objects match"', function () { + condition = new SummaryWidgetCondition({ + object: 'all', + key: 'raw', + operation: 'greaterThan', + values: [10] }); + telemetryState.objectId.lastDatum.value = 0; + telemetryState.otherObjectId.lastDatum.value = 0; + expect(condition.evaluate(telemetryState)).toBe(false); + telemetryState.objectId.lastDatum.value = 0; + telemetryState.otherObjectId.lastDatum.value = 15; + expect(condition.evaluate(telemetryState)).toBe(false); + telemetryState.objectId.lastDatum.value = 15; + telemetryState.otherObjectId.lastDatum.value = 0; + expect(condition.evaluate(telemetryState)).toBe(false); + telemetryState.objectId.lastDatum.value = 15; + telemetryState.otherObjectId.lastDatum.value = 15; + expect(condition.evaluate(telemetryState)).toBe(true); + }); - it('can evaluate "if any object matches"', function () { - condition = new SummaryWidgetCondition({ - object: 'any', - key: 'raw', - operation: 'greaterThan', - values: [10] - }); - telemetryState.objectId.lastDatum.value = 0; - telemetryState.otherObjectId.lastDatum.value = 0; - expect(condition.evaluate(telemetryState)).toBe(false); - telemetryState.objectId.lastDatum.value = 0; - telemetryState.otherObjectId.lastDatum.value = 15; - expect(condition.evaluate(telemetryState)).toBe(true); - telemetryState.objectId.lastDatum.value = 15; - telemetryState.otherObjectId.lastDatum.value = 0; - expect(condition.evaluate(telemetryState)).toBe(true); - telemetryState.objectId.lastDatum.value = 15; - telemetryState.otherObjectId.lastDatum.value = 15; - expect(condition.evaluate(telemetryState)).toBe(true); + it('can evaluate "if any object matches"', function () { + condition = new SummaryWidgetCondition({ + object: 'any', + key: 'raw', + operation: 'greaterThan', + values: [10] }); + telemetryState.objectId.lastDatum.value = 0; + telemetryState.otherObjectId.lastDatum.value = 0; + expect(condition.evaluate(telemetryState)).toBe(false); + telemetryState.objectId.lastDatum.value = 0; + telemetryState.otherObjectId.lastDatum.value = 15; + expect(condition.evaluate(telemetryState)).toBe(true); + telemetryState.objectId.lastDatum.value = 15; + telemetryState.otherObjectId.lastDatum.value = 0; + expect(condition.evaluate(telemetryState)).toBe(true); + telemetryState.objectId.lastDatum.value = 15; + telemetryState.otherObjectId.lastDatum.value = 15; + expect(condition.evaluate(telemetryState)).toBe(true); }); }); diff --git a/src/plugins/summaryWidget/src/telemetry/SummaryWidgetEvaluator.js b/src/plugins/summaryWidget/src/telemetry/SummaryWidgetEvaluator.js index 3a98e643c48..6bafb36c69f 100644 --- a/src/plugins/summaryWidget/src/telemetry/SummaryWidgetEvaluator.js +++ b/src/plugins/summaryWidget/src/telemetry/SummaryWidgetEvaluator.js @@ -20,251 +20,244 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define(['./SummaryWidgetRule', '../eventHelpers', 'objectUtils', 'lodash'], function ( - SummaryWidgetRule, - eventHelpers, - objectUtils, - _ -) { - /** - * evaluates rules defined in a summary widget against either lad or - * realtime state. - * - */ - function SummaryWidgetEvaluator(domainObject, openmct) { - this.openmct = openmct; - this.baseState = {}; - - this.updateRules(domainObject); - this.removeObserver = openmct.objects.observe(domainObject, '*', this.updateRules.bind(this)); - - const composition = openmct.composition.get(domainObject); - - this.listenTo(composition, 'add', this.addChild, this); - this.listenTo(composition, 'remove', this.removeChild, this); - - this.loadPromise = composition.load(); - } +import _ from 'lodash'; +import objectUtils from 'objectUtils'; - eventHelpers.extend(SummaryWidgetEvaluator.prototype); +import eventHelpers from '../eventHelpers'; +import SummaryWidgetRule from './SummaryWidgetRule'; - /** - * Subscribes to realtime telemetry for the given summary widget. - */ - SummaryWidgetEvaluator.prototype.subscribe = function (callback) { - let active = true; - let unsubscribes = []; +/** + * evaluates rules defined in a summary widget against either lad or + * realtime state. + * + */ +export default function SummaryWidgetEvaluator(domainObject, openmct) { + this.openmct = openmct; + this.baseState = {}; - this.getBaseStateClone().then( - function (realtimeStates) { - if (!active) { - return; - } + this.updateRules(domainObject); + this.removeObserver = openmct.objects.observe(domainObject, '*', this.updateRules.bind(this)); - const updateCallback = function () { - const datum = this.evaluateState(realtimeStates, this.openmct.time.timeSystem().key); - if (datum) { - callback(datum); - } - }.bind(this); - - /* eslint-disable you-dont-need-lodash-underscore/map */ - unsubscribes = _.map( - realtimeStates, - this.subscribeToObjectState.bind(this, updateCallback) - ); - /* eslint-enable you-dont-need-lodash-underscore/map */ - }.bind(this) - ); + const composition = openmct.composition.get(domainObject); - return function () { - active = false; - unsubscribes.forEach(function (unsubscribe) { - unsubscribe(); - }); - }; - }; + this.listenTo(composition, 'add', this.addChild, this); + this.listenTo(composition, 'remove', this.removeChild, this); - /** - * Returns a promise for a telemetry datum obtained by evaluating the - * current lad data. - */ - SummaryWidgetEvaluator.prototype.requestLatest = function (options) { - return this.getBaseStateClone() - .then( - function (ladState) { - const promises = Object.values(ladState).map( - this.updateObjectStateFromLAD.bind(this, options) - ); - - return Promise.all(promises).then(function () { - return ladState; - }); - }.bind(this) - ) - .then( - function (ladStates) { - return this.evaluateState(ladStates, options.domain); - }.bind(this) - ); - }; + this.loadPromise = composition.load(); +} - SummaryWidgetEvaluator.prototype.updateRules = function (domainObject) { - this.rules = domainObject.configuration.ruleOrder.map(function (ruleId) { - return new SummaryWidgetRule(domainObject.configuration.ruleConfigById[ruleId]); - }); - }; +eventHelpers.extend(SummaryWidgetEvaluator.prototype); - SummaryWidgetEvaluator.prototype.addChild = function (childObject) { - const childId = objectUtils.makeKeyString(childObject.identifier); - const metadata = this.openmct.telemetry.getMetadata(childObject); - const formats = this.openmct.telemetry.getFormatMap(metadata); - - this.baseState[childId] = { - id: childId, - domainObject: childObject, - metadata: metadata, - formats: formats - }; - }; +/** + * Subscribes to realtime telemetry for the given summary widget. + */ +SummaryWidgetEvaluator.prototype.subscribe = function (callback) { + let active = true; + let unsubscribes = []; - SummaryWidgetEvaluator.prototype.removeChild = function (childObject) { - const childId = objectUtils.makeKeyString(childObject.identifier); - delete this.baseState[childId]; - }; + this.getBaseStateClone().then( + function (realtimeStates) { + if (!active) { + return; + } - SummaryWidgetEvaluator.prototype.load = function () { - return this.loadPromise; + const updateCallback = function () { + const datum = this.evaluateState(realtimeStates, this.openmct.time.timeSystem().key); + if (datum) { + callback(datum); + } + }.bind(this); + + /* eslint-disable you-dont-need-lodash-underscore/map */ + unsubscribes = _.map(realtimeStates, this.subscribeToObjectState.bind(this, updateCallback)); + /* eslint-enable you-dont-need-lodash-underscore/map */ + }.bind(this) + ); + + return function () { + active = false; + unsubscribes.forEach(function (unsubscribe) { + unsubscribe(); + }); }; +}; + +/** + * Returns a promise for a telemetry datum obtained by evaluating the + * current lad data. + */ +SummaryWidgetEvaluator.prototype.requestLatest = function (options) { + return this.getBaseStateClone() + .then( + function (ladState) { + const promises = Object.values(ladState).map( + this.updateObjectStateFromLAD.bind(this, options) + ); - /** - * Return a promise for a 2-deep clone of the base state object: object - * states are shallow cloned, and then assembled and returned as a new base - * state. Allows object states to be mutated while sharing telemetry - * metadata and formats. - */ - SummaryWidgetEvaluator.prototype.getBaseStateClone = function () { - return this.load().then( - function () { - /* eslint-disable you-dont-need-lodash-underscore/values */ - return _(this.baseState).values().map(_.clone).keyBy('id').value(); - /* eslint-enable you-dont-need-lodash-underscore/values */ + return Promise.all(promises).then(function () { + return ladState; + }); }.bind(this) - ); - }; - - /** - * Subscribes to realtime updates for a given objectState, and invokes - * the supplied callback when objectState has been updated. Returns - * a function to unsubscribe. - * @private. - */ - SummaryWidgetEvaluator.prototype.subscribeToObjectState = function (callback, objectState) { - return this.openmct.telemetry.subscribe( - objectState.domainObject, - function (datum) { - objectState.lastDatum = datum; - objectState.timestamps = this.getTimestamps(objectState.id, datum); - callback(); + ) + .then( + function (ladStates) { + return this.evaluateState(ladStates, options.domain); }.bind(this) ); +}; + +SummaryWidgetEvaluator.prototype.updateRules = function (domainObject) { + this.rules = domainObject.configuration.ruleOrder.map(function (ruleId) { + return new SummaryWidgetRule(domainObject.configuration.ruleConfigById[ruleId]); + }); +}; + +SummaryWidgetEvaluator.prototype.addChild = function (childObject) { + const childId = objectUtils.makeKeyString(childObject.identifier); + const metadata = this.openmct.telemetry.getMetadata(childObject); + const formats = this.openmct.telemetry.getFormatMap(metadata); + + this.baseState[childId] = { + id: childId, + domainObject: childObject, + metadata: metadata, + formats: formats }; +}; + +SummaryWidgetEvaluator.prototype.removeChild = function (childObject) { + const childId = objectUtils.makeKeyString(childObject.identifier); + delete this.baseState[childId]; +}; + +SummaryWidgetEvaluator.prototype.load = function () { + return this.loadPromise; +}; + +/** + * Return a promise for a 2-deep clone of the base state object: object + * states are shallow cloned, and then assembled and returned as a new base + * state. Allows object states to be mutated while sharing telemetry + * metadata and formats. + */ +SummaryWidgetEvaluator.prototype.getBaseStateClone = function () { + return this.load().then( + function () { + /* eslint-disable you-dont-need-lodash-underscore/values */ + return _(this.baseState).values().map(_.clone).keyBy('id').value(); + /* eslint-enable you-dont-need-lodash-underscore/values */ + }.bind(this) + ); +}; + +/** + * Subscribes to realtime updates for a given objectState, and invokes + * the supplied callback when objectState has been updated. Returns + * a function to unsubscribe. + * @private. + */ +SummaryWidgetEvaluator.prototype.subscribeToObjectState = function (callback, objectState) { + return this.openmct.telemetry.subscribe( + objectState.domainObject, + function (datum) { + objectState.lastDatum = datum; + objectState.timestamps = this.getTimestamps(objectState.id, datum); + callback(); + }.bind(this) + ); +}; + +/** + * Given an object state, will return a promise that is resolved when the + * object state has been updated from the LAD. + * @private. + */ +SummaryWidgetEvaluator.prototype.updateObjectStateFromLAD = function (options, objectState) { + options = Object.assign({}, options, { + strategy: 'latest', + size: 1 + }); + + return this.openmct.telemetry.request(objectState.domainObject, options).then( + function (results) { + objectState.lastDatum = results[results.length - 1]; + objectState.timestamps = this.getTimestamps(objectState.id, objectState.lastDatum); + }.bind(this) + ); +}; + +/** + * Returns an object containing all domain values in a datum. + * @private. + */ +SummaryWidgetEvaluator.prototype.getTimestamps = function (childId, datum) { + const timestampedDatum = {}; + this.openmct.time.getAllTimeSystems().forEach(function (timeSystem) { + timestampedDatum[timeSystem.key] = this.baseState[childId].formats[timeSystem.key].parse(datum); + }, this); + + return timestampedDatum; +}; + +/** + * Given a base datum(containing timestamps) and rule index, adds values + * from the matching rule. + * @private + */ +SummaryWidgetEvaluator.prototype.makeDatumFromRule = function (ruleIndex, baseDatum) { + const rule = this.rules[ruleIndex]; + + baseDatum.ruleLabel = rule.label; + baseDatum.ruleName = rule.name; + baseDatum.message = rule.message; + baseDatum.ruleIndex = ruleIndex; + baseDatum.backgroundColor = rule.style['background-color']; + baseDatum.textColor = rule.style.color; + baseDatum.borderColor = rule.style['border-color']; + baseDatum.icon = rule.icon; + + return baseDatum; +}; + +/** + * Evaluate a `state` object and return a summary widget telemetry datum. + * Datum timestamps will be taken from the "latest" datum in the `state` + * where "latest" is the datum with the largest value for the given + * `timestampKey`. + * @private. + */ +SummaryWidgetEvaluator.prototype.evaluateState = function (state, timestampKey) { + const hasRequiredData = Object.keys(state).reduce(function (itDoes, k) { + return itDoes && state[k].lastDatum; + }, true); + if (!hasRequiredData) { + return; + } - /** - * Given an object state, will return a promise that is resolved when the - * object state has been updated from the LAD. - * @private. - */ - SummaryWidgetEvaluator.prototype.updateObjectStateFromLAD = function (options, objectState) { - options = Object.assign({}, options, { - strategy: 'latest', - size: 1 - }); - - return this.openmct.telemetry.request(objectState.domainObject, options).then( - function (results) { - objectState.lastDatum = results[results.length - 1]; - objectState.timestamps = this.getTimestamps(objectState.id, objectState.lastDatum); - }.bind(this) - ); - }; - - /** - * Returns an object containing all domain values in a datum. - * @private. - */ - SummaryWidgetEvaluator.prototype.getTimestamps = function (childId, datum) { - const timestampedDatum = {}; - this.openmct.time.getAllTimeSystems().forEach(function (timeSystem) { - timestampedDatum[timeSystem.key] = - this.baseState[childId].formats[timeSystem.key].parse(datum); - }, this); - - return timestampedDatum; - }; - - /** - * Given a base datum(containing timestamps) and rule index, adds values - * from the matching rule. - * @private - */ - SummaryWidgetEvaluator.prototype.makeDatumFromRule = function (ruleIndex, baseDatum) { - const rule = this.rules[ruleIndex]; - - baseDatum.ruleLabel = rule.label; - baseDatum.ruleName = rule.name; - baseDatum.message = rule.message; - baseDatum.ruleIndex = ruleIndex; - baseDatum.backgroundColor = rule.style['background-color']; - baseDatum.textColor = rule.style.color; - baseDatum.borderColor = rule.style['border-color']; - baseDatum.icon = rule.icon; - - return baseDatum; - }; - - /** - * Evaluate a `state` object and return a summary widget telemetry datum. - * Datum timestamps will be taken from the "latest" datum in the `state` - * where "latest" is the datum with the largest value for the given - * `timestampKey`. - * @private. - */ - SummaryWidgetEvaluator.prototype.evaluateState = function (state, timestampKey) { - const hasRequiredData = Object.keys(state).reduce(function (itDoes, k) { - return itDoes && state[k].lastDatum; - }, true); - if (!hasRequiredData) { - return; - } - - let i; - for (i = this.rules.length - 1; i > 0; i--) { - if (this.rules[i].evaluate(state, false)) { - break; - } + let i; + for (i = this.rules.length - 1; i > 0; i--) { + if (this.rules[i].evaluate(state, false)) { + break; } + } - /* eslint-disable you-dont-need-lodash-underscore/map */ - let latestTimestamp = _(state).map('timestamps').sortBy(timestampKey).last(); - /* eslint-enable you-dont-need-lodash-underscore/map */ + /* eslint-disable you-dont-need-lodash-underscore/map */ + let latestTimestamp = _(state).map('timestamps').sortBy(timestampKey).last(); + /* eslint-enable you-dont-need-lodash-underscore/map */ - if (!latestTimestamp) { - latestTimestamp = {}; - } - - const baseDatum = _.clone(latestTimestamp); + if (!latestTimestamp) { + latestTimestamp = {}; + } - return this.makeDatumFromRule(i, baseDatum); - }; + const baseDatum = _.clone(latestTimestamp); - /** - * remove all listeners and clean up any resources. - */ - SummaryWidgetEvaluator.prototype.destroy = function () { - this.stopListening(); - this.removeObserver(); - }; + return this.makeDatumFromRule(i, baseDatum); +}; - return SummaryWidgetEvaluator; -}); +/** + * remove all listeners and clean up any resources. + */ +SummaryWidgetEvaluator.prototype.destroy = function () { + this.stopListening(); + this.removeObserver(); +}; diff --git a/src/plugins/summaryWidget/src/telemetry/SummaryWidgetMetadataProvider.js b/src/plugins/summaryWidget/src/telemetry/SummaryWidgetMetadataProvider.js index 796e60cb8b3..0b4b000efd9 100644 --- a/src/plugins/summaryWidget/src/telemetry/SummaryWidgetMetadataProvider.js +++ b/src/plugins/summaryWidget/src/telemetry/SummaryWidgetMetadataProvider.js @@ -20,94 +20,90 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define([], function () { - function SummaryWidgetMetadataProvider(openmct) { - this.openmct = openmct; - } +export default function SummaryWidgetMetadataProvider(openmct) { + this.openmct = openmct; +} - SummaryWidgetMetadataProvider.prototype.supportsMetadata = function (domainObject) { - return domainObject.type === 'summary-widget'; - }; +SummaryWidgetMetadataProvider.prototype.supportsMetadata = function (domainObject) { + return domainObject.type === 'summary-widget'; +}; + +SummaryWidgetMetadataProvider.prototype.getDomains = function (domainObject) { + return this.openmct.time.getAllTimeSystems().map(function (ts, i) { + return { + key: ts.key, + name: ts.name, + format: ts.timeFormat, + hints: { + domain: i + } + }; + }); +}; - SummaryWidgetMetadataProvider.prototype.getDomains = function (domainObject) { - return this.openmct.time.getAllTimeSystems().map(function (ts, i) { +SummaryWidgetMetadataProvider.prototype.getMetadata = function (domainObject) { + const ruleOrder = domainObject.configuration.ruleOrder || []; + const enumerations = ruleOrder + .filter(function (ruleId) { + return Boolean(domainObject.configuration.ruleConfigById[ruleId]); + }) + .map(function (ruleId, ruleIndex) { return { - key: ts.key, - name: ts.name, - format: ts.timeFormat, - hints: { - domain: i - } + string: domainObject.configuration.ruleConfigById[ruleId].label, + value: ruleIndex }; }); - }; - - SummaryWidgetMetadataProvider.prototype.getMetadata = function (domainObject) { - const ruleOrder = domainObject.configuration.ruleOrder || []; - const enumerations = ruleOrder - .filter(function (ruleId) { - return Boolean(domainObject.configuration.ruleConfigById[ruleId]); - }) - .map(function (ruleId, ruleIndex) { - return { - string: domainObject.configuration.ruleConfigById[ruleId].label, - value: ruleIndex - }; - }); - const metadata = { - // Generally safe assumption is that we have one domain per timeSystem. - values: this.getDomains().concat([ - { - name: 'State', - key: 'state', - source: 'ruleIndex', - format: 'enum', - enumerations: enumerations, - hints: { - range: 1 - } - }, - { - name: 'Rule Label', - key: 'ruleLabel', - format: 'string' - }, - { - name: 'Rule Name', - key: 'ruleName', - format: 'string' - }, - { - name: 'Message', - key: 'message', - format: 'string' - }, - { - name: 'Background Color', - key: 'backgroundColor', - format: 'string' - }, - { - name: 'Text Color', - key: 'textColor', - format: 'string' - }, - { - name: 'Border Color', - key: 'borderColor', - format: 'string' - }, - { - name: 'Display Icon', - key: 'icon', - format: 'string' + const metadata = { + // Generally safe assumption is that we have one domain per timeSystem. + values: this.getDomains().concat([ + { + name: 'State', + key: 'state', + source: 'ruleIndex', + format: 'enum', + enumerations: enumerations, + hints: { + range: 1 } - ]) - }; - - return metadata; + }, + { + name: 'Rule Label', + key: 'ruleLabel', + format: 'string' + }, + { + name: 'Rule Name', + key: 'ruleName', + format: 'string' + }, + { + name: 'Message', + key: 'message', + format: 'string' + }, + { + name: 'Background Color', + key: 'backgroundColor', + format: 'string' + }, + { + name: 'Text Color', + key: 'textColor', + format: 'string' + }, + { + name: 'Border Color', + key: 'borderColor', + format: 'string' + }, + { + name: 'Display Icon', + key: 'icon', + format: 'string' + } + ]) }; - return SummaryWidgetMetadataProvider; -}); + return metadata; +}; diff --git a/src/plugins/summaryWidget/src/telemetry/SummaryWidgetRule.js b/src/plugins/summaryWidget/src/telemetry/SummaryWidgetRule.js index f6115069864..43242d30744 100644 --- a/src/plugins/summaryWidget/src/telemetry/SummaryWidgetRule.js +++ b/src/plugins/summaryWidget/src/telemetry/SummaryWidgetRule.js @@ -20,51 +20,49 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define(['./SummaryWidgetCondition'], function (SummaryWidgetCondition) { - function SummaryWidgetRule(definition) { - this.name = definition.name; - this.label = definition.label; - this.id = definition.id; - this.icon = definition.icon; - this.style = definition.style; - this.message = definition.message; - this.description = definition.description; - this.conditions = definition.conditions.map(function (cDefinition) { - return new SummaryWidgetCondition(cDefinition); - }); - this.trigger = definition.trigger; - } +import SummaryWidgetCondition from './SummaryWidgetCondition'; - /** - * Evaluate the given rule against a telemetryState and return true if it - * matches. - */ - SummaryWidgetRule.prototype.evaluate = function (telemetryState) { - let i; - let result; +export default function SummaryWidgetRule(definition) { + this.name = definition.name; + this.label = definition.label; + this.id = definition.id; + this.icon = definition.icon; + this.style = definition.style; + this.message = definition.message; + this.description = definition.description; + this.conditions = definition.conditions.map(function (cDefinition) { + return new SummaryWidgetCondition(cDefinition); + }); + this.trigger = definition.trigger; +} - if (this.trigger === 'all') { - for (i = 0; i < this.conditions.length; i++) { - result = this.conditions[i].evaluate(telemetryState); - if (!result) { - return false; - } - } +/** + * Evaluate the given rule against a telemetryState and return true if it + * matches. + */ +SummaryWidgetRule.prototype.evaluate = function (telemetryState) { + let i; + let result; - return true; - } else if (this.trigger === 'any') { - for (i = 0; i < this.conditions.length; i++) { - result = this.conditions[i].evaluate(telemetryState); - if (result) { - return true; - } + if (this.trigger === 'all') { + for (i = 0; i < this.conditions.length; i++) { + result = this.conditions[i].evaluate(telemetryState); + if (!result) { + return false; } + } - return false; - } else { - throw new Error('Invalid rule trigger: ' + this.trigger); + return true; + } else if (this.trigger === 'any') { + for (i = 0; i < this.conditions.length; i++) { + result = this.conditions[i].evaluate(telemetryState); + if (result) { + return true; + } } - }; - return SummaryWidgetRule; -}); + return false; + } else { + throw new Error('Invalid rule trigger: ' + this.trigger); + } +}; diff --git a/src/plugins/summaryWidget/src/telemetry/SummaryWidgetRuleSpec.js b/src/plugins/summaryWidget/src/telemetry/SummaryWidgetRuleSpec.js index 5f740bca9fc..0b4a3d9c0a6 100644 --- a/src/plugins/summaryWidget/src/telemetry/SummaryWidgetRuleSpec.js +++ b/src/plugins/summaryWidget/src/telemetry/SummaryWidgetRuleSpec.js @@ -20,134 +20,134 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define(['./SummaryWidgetRule'], function (SummaryWidgetRule) { - describe('SummaryWidgetRule', function () { - let rule; - let telemetryState; +import SummaryWidgetRule from './SummaryWidgetRule'; - beforeEach(function () { - const formatMap = { - raw: { - parse: function (datum) { - return datum.value; - } - } - }; +describe('SummaryWidgetRule', function () { + let rule; + let telemetryState; - telemetryState = { - objectId: { - formats: formatMap, - lastDatum: {} - }, - otherObjectId: { - formats: formatMap, - lastDatum: {} + beforeEach(function () { + const formatMap = { + raw: { + parse: function (datum) { + return datum.value; } - }; - }); + } + }; - it('allows single condition rules with any', function () { - rule = new SummaryWidgetRule({ - trigger: 'any', - conditions: [ - { - object: 'objectId', - key: 'raw', - operation: 'greaterThan', - values: [10] - } - ] - }); + telemetryState = { + objectId: { + formats: formatMap, + lastDatum: {} + }, + otherObjectId: { + formats: formatMap, + lastDatum: {} + } + }; + }); - telemetryState.objectId.lastDatum.value = 5; - expect(rule.evaluate(telemetryState)).toBe(false); - telemetryState.objectId.lastDatum.value = 15; - expect(rule.evaluate(telemetryState)).toBe(true); + it('allows single condition rules with any', function () { + rule = new SummaryWidgetRule({ + trigger: 'any', + conditions: [ + { + object: 'objectId', + key: 'raw', + operation: 'greaterThan', + values: [10] + } + ] }); - it('allows single condition rules with all', function () { - rule = new SummaryWidgetRule({ - trigger: 'all', - conditions: [ - { - object: 'objectId', - key: 'raw', - operation: 'greaterThan', - values: [10] - } - ] - }); + telemetryState.objectId.lastDatum.value = 5; + expect(rule.evaluate(telemetryState)).toBe(false); + telemetryState.objectId.lastDatum.value = 15; + expect(rule.evaluate(telemetryState)).toBe(true); + }); - telemetryState.objectId.lastDatum.value = 5; - expect(rule.evaluate(telemetryState)).toBe(false); - telemetryState.objectId.lastDatum.value = 15; - expect(rule.evaluate(telemetryState)).toBe(true); + it('allows single condition rules with all', function () { + rule = new SummaryWidgetRule({ + trigger: 'all', + conditions: [ + { + object: 'objectId', + key: 'raw', + operation: 'greaterThan', + values: [10] + } + ] }); - it('can combine multiple conditions with all', function () { - rule = new SummaryWidgetRule({ - trigger: 'all', - conditions: [ - { - object: 'objectId', - key: 'raw', - operation: 'greaterThan', - values: [10] - }, - { - object: 'otherObjectId', - key: 'raw', - operation: 'greaterThan', - values: [20] - } - ] - }); + telemetryState.objectId.lastDatum.value = 5; + expect(rule.evaluate(telemetryState)).toBe(false); + telemetryState.objectId.lastDatum.value = 15; + expect(rule.evaluate(telemetryState)).toBe(true); + }); - telemetryState.objectId.lastDatum.value = 5; - telemetryState.otherObjectId.lastDatum.value = 5; - expect(rule.evaluate(telemetryState)).toBe(false); - telemetryState.objectId.lastDatum.value = 5; - telemetryState.otherObjectId.lastDatum.value = 25; - expect(rule.evaluate(telemetryState)).toBe(false); - telemetryState.objectId.lastDatum.value = 15; - telemetryState.otherObjectId.lastDatum.value = 5; - expect(rule.evaluate(telemetryState)).toBe(false); - telemetryState.objectId.lastDatum.value = 15; - telemetryState.otherObjectId.lastDatum.value = 25; - expect(rule.evaluate(telemetryState)).toBe(true); + it('can combine multiple conditions with all', function () { + rule = new SummaryWidgetRule({ + trigger: 'all', + conditions: [ + { + object: 'objectId', + key: 'raw', + operation: 'greaterThan', + values: [10] + }, + { + object: 'otherObjectId', + key: 'raw', + operation: 'greaterThan', + values: [20] + } + ] }); - it('can combine multiple conditions with any', function () { - rule = new SummaryWidgetRule({ - trigger: 'any', - conditions: [ - { - object: 'objectId', - key: 'raw', - operation: 'greaterThan', - values: [10] - }, - { - object: 'otherObjectId', - key: 'raw', - operation: 'greaterThan', - values: [20] - } - ] - }); + telemetryState.objectId.lastDatum.value = 5; + telemetryState.otherObjectId.lastDatum.value = 5; + expect(rule.evaluate(telemetryState)).toBe(false); + telemetryState.objectId.lastDatum.value = 5; + telemetryState.otherObjectId.lastDatum.value = 25; + expect(rule.evaluate(telemetryState)).toBe(false); + telemetryState.objectId.lastDatum.value = 15; + telemetryState.otherObjectId.lastDatum.value = 5; + expect(rule.evaluate(telemetryState)).toBe(false); + telemetryState.objectId.lastDatum.value = 15; + telemetryState.otherObjectId.lastDatum.value = 25; + expect(rule.evaluate(telemetryState)).toBe(true); + }); - telemetryState.objectId.lastDatum.value = 5; - telemetryState.otherObjectId.lastDatum.value = 5; - expect(rule.evaluate(telemetryState)).toBe(false); - telemetryState.objectId.lastDatum.value = 5; - telemetryState.otherObjectId.lastDatum.value = 25; - expect(rule.evaluate(telemetryState)).toBe(true); - telemetryState.objectId.lastDatum.value = 15; - telemetryState.otherObjectId.lastDatum.value = 5; - expect(rule.evaluate(telemetryState)).toBe(true); - telemetryState.objectId.lastDatum.value = 15; - telemetryState.otherObjectId.lastDatum.value = 25; - expect(rule.evaluate(telemetryState)).toBe(true); + it('can combine multiple conditions with any', function () { + rule = new SummaryWidgetRule({ + trigger: 'any', + conditions: [ + { + object: 'objectId', + key: 'raw', + operation: 'greaterThan', + values: [10] + }, + { + object: 'otherObjectId', + key: 'raw', + operation: 'greaterThan', + values: [20] + } + ] }); + + telemetryState.objectId.lastDatum.value = 5; + telemetryState.otherObjectId.lastDatum.value = 5; + expect(rule.evaluate(telemetryState)).toBe(false); + telemetryState.objectId.lastDatum.value = 5; + telemetryState.otherObjectId.lastDatum.value = 25; + expect(rule.evaluate(telemetryState)).toBe(true); + telemetryState.objectId.lastDatum.value = 15; + telemetryState.otherObjectId.lastDatum.value = 5; + expect(rule.evaluate(telemetryState)).toBe(true); + telemetryState.objectId.lastDatum.value = 15; + telemetryState.otherObjectId.lastDatum.value = 25; + expect(rule.evaluate(telemetryState)).toBe(true); }); }); diff --git a/src/plugins/summaryWidget/src/telemetry/SummaryWidgetTelemetryProvider.js b/src/plugins/summaryWidget/src/telemetry/SummaryWidgetTelemetryProvider.js index 13a37373604..10159b31816 100644 --- a/src/plugins/summaryWidget/src/telemetry/SummaryWidgetTelemetryProvider.js +++ b/src/plugins/summaryWidget/src/telemetry/SummaryWidgetTelemetryProvider.js @@ -20,44 +20,42 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define(['./EvaluatorPool'], function (EvaluatorPool) { - function SummaryWidgetTelemetryProvider(openmct) { - this.pool = new EvaluatorPool(openmct); - } +import EvaluatorPool from './EvaluatorPool'; - SummaryWidgetTelemetryProvider.prototype.supportsRequest = function (domainObject, options) { - return domainObject.type === 'summary-widget'; - }; +export default function SummaryWidgetTelemetryProvider(openmct) { + this.pool = new EvaluatorPool(openmct); +} - SummaryWidgetTelemetryProvider.prototype.request = function (domainObject, options) { - if (options.strategy !== 'latest' && options.size !== 1) { - return Promise.resolve([]); - } +SummaryWidgetTelemetryProvider.prototype.supportsRequest = function (domainObject, options) { + return domainObject.type === 'summary-widget'; +}; - const evaluator = this.pool.get(domainObject); +SummaryWidgetTelemetryProvider.prototype.request = function (domainObject, options) { + if (options.strategy !== 'latest' && options.size !== 1) { + return Promise.resolve([]); + } - return evaluator.requestLatest(options).then( - function (latestDatum) { - this.pool.release(evaluator); + const evaluator = this.pool.get(domainObject); - return latestDatum ? [latestDatum] : []; - }.bind(this) - ); - }; + return evaluator.requestLatest(options).then( + function (latestDatum) { + this.pool.release(evaluator); - SummaryWidgetTelemetryProvider.prototype.supportsSubscribe = function (domainObject) { - return domainObject.type === 'summary-widget'; - }; + return latestDatum ? [latestDatum] : []; + }.bind(this) + ); +}; - SummaryWidgetTelemetryProvider.prototype.subscribe = function (domainObject, callback) { - const evaluator = this.pool.get(domainObject); - const unsubscribe = evaluator.subscribe(callback); +SummaryWidgetTelemetryProvider.prototype.supportsSubscribe = function (domainObject) { + return domainObject.type === 'summary-widget'; +}; - return function () { - this.pool.release(evaluator); - unsubscribe(); - }.bind(this); - }; +SummaryWidgetTelemetryProvider.prototype.subscribe = function (domainObject, callback) { + const evaluator = this.pool.get(domainObject); + const unsubscribe = evaluator.subscribe(callback); - return SummaryWidgetTelemetryProvider; -}); + return function () { + this.pool.release(evaluator); + unsubscribe(); + }.bind(this); +}; diff --git a/src/plugins/summaryWidget/src/telemetry/SummaryWidgetTelemetryProviderSpec.js b/src/plugins/summaryWidget/src/telemetry/SummaryWidgetTelemetryProviderSpec.js index ec7853f1300..7ec2ae7e09f 100644 --- a/src/plugins/summaryWidget/src/telemetry/SummaryWidgetTelemetryProviderSpec.js +++ b/src/plugins/summaryWidget/src/telemetry/SummaryWidgetTelemetryProviderSpec.js @@ -20,444 +20,444 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define(['./SummaryWidgetTelemetryProvider'], function (SummaryWidgetTelemetryProvider) { - xdescribe('SummaryWidgetTelemetryProvider', function () { - let telemObjectA; - let telemObjectB; - let summaryWidgetObject; - let openmct; - let telemUnsubscribes; - let unobserver; - let composition; - let telemetryProvider; - let loader; - - beforeEach(function () { - telemObjectA = { - identifier: { - namespace: 'a', - key: 'telem' - } - }; - telemObjectB = { - identifier: { - namespace: 'b', - key: 'telem' - } - }; - summaryWidgetObject = { - name: 'Summary Widget', - type: 'summary-widget', - identifier: { - namespace: 'base', - key: 'widgetId' - }, - composition: ['a:telem', 'b:telem'], - configuration: { - ruleOrder: ['default', 'rule0', 'rule1'], - ruleConfigById: { - default: { - name: 'safe', - label: "Don't Worry", - message: "It's Ok", - id: 'default', - icon: 'a-ok', - style: { - color: '#ffffff', - 'background-color': '#38761d', - 'border-color': 'rgba(0,0,0,0)' - }, - conditions: [ - { - object: '', - key: '', - operation: '', - values: [] - } - ], - trigger: 'any' +import SummaryWidgetTelemetryProvider from './SummaryWidgetTelemetryProvider'; + +xdescribe('SummaryWidgetTelemetryProvider', function () { + let telemObjectA; + let telemObjectB; + let summaryWidgetObject; + let openmct; + let telemUnsubscribes; + let unobserver; + let composition; + let telemetryProvider; + let loader; + + beforeEach(function () { + telemObjectA = { + identifier: { + namespace: 'a', + key: 'telem' + } + }; + telemObjectB = { + identifier: { + namespace: 'b', + key: 'telem' + } + }; + summaryWidgetObject = { + name: 'Summary Widget', + type: 'summary-widget', + identifier: { + namespace: 'base', + key: 'widgetId' + }, + composition: ['a:telem', 'b:telem'], + configuration: { + ruleOrder: ['default', 'rule0', 'rule1'], + ruleConfigById: { + default: { + name: 'safe', + label: "Don't Worry", + message: "It's Ok", + id: 'default', + icon: 'a-ok', + style: { + color: '#ffffff', + 'background-color': '#38761d', + 'border-color': 'rgba(0,0,0,0)' + }, + conditions: [ + { + object: '', + key: '', + operation: '', + values: [] + } + ], + trigger: 'any' + }, + rule0: { + name: 'A High', + label: 'Start Worrying', + message: 'A is a little high...', + id: 'rule0', + icon: 'a-high', + style: { + color: '#000000', + 'background-color': '#ffff00', + 'border-color': 'rgba(1,1,0,0)' }, - rule0: { - name: 'A High', - label: 'Start Worrying', - message: 'A is a little high...', - id: 'rule0', - icon: 'a-high', - style: { - color: '#000000', - 'background-color': '#ffff00', - 'border-color': 'rgba(1,1,0,0)' - }, - conditions: [ - { - object: 'a:telem', - key: 'measurement', - operation: 'greaterThan', - values: [50] - } - ], - trigger: 'any' + conditions: [ + { + object: 'a:telem', + key: 'measurement', + operation: 'greaterThan', + values: [50] + } + ], + trigger: 'any' + }, + rule1: { + name: 'B Low', + label: 'WORRY!', + message: 'B is Low', + id: 'rule1', + icon: 'b-low', + style: { + color: '#ff00ff', + 'background-color': '#ff0000', + 'border-color': 'rgba(1,0,0,0)' }, - rule1: { - name: 'B Low', - label: 'WORRY!', - message: 'B is Low', - id: 'rule1', - icon: 'b-low', - style: { - color: '#ff00ff', - 'background-color': '#ff0000', - 'border-color': 'rgba(1,0,0,0)' - }, - conditions: [ - { - object: 'b:telem', - key: 'measurement', - operation: 'lessThan', - values: [10] - } - ], - trigger: 'any' - } + conditions: [ + { + object: 'b:telem', + key: 'measurement', + operation: 'lessThan', + values: [10] + } + ], + trigger: 'any' } } - }; - openmct = { - objects: jasmine.createSpyObj('objectAPI', ['get', 'observe']), - telemetry: jasmine.createSpyObj('telemetryAPI', [ - 'getMetadata', - 'getFormatMap', - 'request', - 'subscribe', - 'addProvider' - ]), - composition: jasmine.createSpyObj('compositionAPI', ['get']), - time: jasmine.createSpyObj('timeAPI', ['getAllTimeSystems', 'timeSystem']) - }; - - openmct.time.getAllTimeSystems.and.returnValue([{ key: 'timestamp' }]); - openmct.time.timeSystem.and.returnValue({ key: 'timestamp' }); - - unobserver = jasmine.createSpy('unobserver'); - openmct.objects.observe.and.returnValue(unobserver); - - composition = jasmine.createSpyObj('compositionCollection', ['on', 'off', 'load']); - - function notify(eventName, a, b) { - composition.on.calls - .all() - .filter(function (c) { - return c.args[0] === eventName; - }) - .forEach(function (c) { - if (c.args[2]) { - // listener w/ context. - c.args[1].call(c.args[2], a, b); - } else { - // listener w/o context. - c.args[1](a, b); - } - }); } + }; + openmct = { + objects: jasmine.createSpyObj('objectAPI', ['get', 'observe']), + telemetry: jasmine.createSpyObj('telemetryAPI', [ + 'getMetadata', + 'getFormatMap', + 'request', + 'subscribe', + 'addProvider' + ]), + composition: jasmine.createSpyObj('compositionAPI', ['get']), + time: jasmine.createSpyObj('timeAPI', ['getAllTimeSystems', 'timeSystem']) + }; + + openmct.time.getAllTimeSystems.and.returnValue([{ key: 'timestamp' }]); + openmct.time.timeSystem.and.returnValue({ key: 'timestamp' }); + + unobserver = jasmine.createSpy('unobserver'); + openmct.objects.observe.and.returnValue(unobserver); + + composition = jasmine.createSpyObj('compositionCollection', ['on', 'off', 'load']); + + function notify(eventName, a, b) { + composition.on.calls + .all() + .filter(function (c) { + return c.args[0] === eventName; + }) + .forEach(function (c) { + if (c.args[2]) { + // listener w/ context. + c.args[1].call(c.args[2], a, b); + } else { + // listener w/o context. + c.args[1](a, b); + } + }); + } - loader = {}; - loader.promise = new Promise(function (resolve, reject) { - loader.resolve = resolve; - loader.reject = reject; - }); + loader = {}; + loader.promise = new Promise(function (resolve, reject) { + loader.resolve = resolve; + loader.reject = reject; + }); - composition.load.and.callFake(function () { + composition.load.and.callFake(function () { + setTimeout(function () { + notify('add', telemObjectA); setTimeout(function () { - notify('add', telemObjectA); + notify('add', telemObjectB); setTimeout(function () { - notify('add', telemObjectB); - setTimeout(function () { - loader.resolve(); - }); + loader.resolve(); }); }); - - return loader.promise; }); - openmct.composition.get.and.returnValue(composition); - telemUnsubscribes = []; - openmct.telemetry.subscribe.and.callFake(function () { - const unsubscriber = jasmine.createSpy('unsubscriber' + telemUnsubscribes.length); - telemUnsubscribes.push(unsubscriber); + return loader.promise; + }); + openmct.composition.get.and.returnValue(composition); - return unsubscriber; - }); + telemUnsubscribes = []; + openmct.telemetry.subscribe.and.callFake(function () { + const unsubscriber = jasmine.createSpy('unsubscriber' + telemUnsubscribes.length); + telemUnsubscribes.push(unsubscriber); - openmct.telemetry.getMetadata.and.callFake(function (object) { - return { - name: 'fake metadata manager', - object: object, - keys: ['timestamp', 'measurement'] - }; - }); + return unsubscriber; + }); + + openmct.telemetry.getMetadata.and.callFake(function (object) { + return { + name: 'fake metadata manager', + object: object, + keys: ['timestamp', 'measurement'] + }; + }); - openmct.telemetry.getFormatMap.and.callFake(function (metadata) { - expect(metadata.name).toBe('fake metadata manager'); + openmct.telemetry.getFormatMap.and.callFake(function (metadata) { + expect(metadata.name).toBe('fake metadata manager'); - return { - metadata: metadata, - timestamp: { - parse: function (datum) { - return datum.t; - } - }, - measurement: { - parse: function (datum) { - return datum.m; - } + return { + metadata: metadata, + timestamp: { + parse: function (datum) { + return datum.t; } - }; - }); - telemetryProvider = new SummaryWidgetTelemetryProvider(openmct); + }, + measurement: { + parse: function (datum) { + return datum.m; + } + } + }; }); + telemetryProvider = new SummaryWidgetTelemetryProvider(openmct); + }); - it('supports subscription for summary widgets', function () { - expect(telemetryProvider.supportsSubscribe(summaryWidgetObject)).toBe(true); - }); + it('supports subscription for summary widgets', function () { + expect(telemetryProvider.supportsSubscribe(summaryWidgetObject)).toBe(true); + }); - it('supports requests for summary widgets', function () { - expect(telemetryProvider.supportsRequest(summaryWidgetObject)).toBe(true); - }); + it('supports requests for summary widgets', function () { + expect(telemetryProvider.supportsRequest(summaryWidgetObject)).toBe(true); + }); - it('does not support other requests or subscriptions', function () { - expect(telemetryProvider.supportsSubscribe(telemObjectA)).toBe(false); - expect(telemetryProvider.supportsRequest(telemObjectA)).toBe(false); - }); + it('does not support other requests or subscriptions', function () { + expect(telemetryProvider.supportsSubscribe(telemObjectA)).toBe(false); + expect(telemetryProvider.supportsRequest(telemObjectA)).toBe(false); + }); - it('Returns no results for basic requests', function () { - return telemetryProvider.request(summaryWidgetObject, {}).then(function (result) { - expect(result).toEqual([]); - }); + it('Returns no results for basic requests', function () { + return telemetryProvider.request(summaryWidgetObject, {}).then(function (result) { + expect(result).toEqual([]); }); + }); - it('provides realtime telemetry', function () { - const callback = jasmine.createSpy('callback'); - telemetryProvider.subscribe(summaryWidgetObject, callback); + it('provides realtime telemetry', function () { + const callback = jasmine.createSpy('callback'); + telemetryProvider.subscribe(summaryWidgetObject, callback); - return loader.promise - .then(function () { - return new Promise(function (resolve) { - setTimeout(resolve); - }); - }) - .then(function () { - expect(openmct.telemetry.subscribe.calls.count()).toBe(2); - expect(openmct.telemetry.subscribe).toHaveBeenCalledWith( - telemObjectA, - jasmine.any(Function) - ); - expect(openmct.telemetry.subscribe).toHaveBeenCalledWith( - telemObjectB, - jasmine.any(Function) - ); - - const aCallback = openmct.telemetry.subscribe.calls.all()[0].args[1]; - const bCallback = openmct.telemetry.subscribe.calls.all()[1].args[1]; - - aCallback({ - t: 123, - m: 25 - }); - expect(callback).not.toHaveBeenCalled(); - bCallback({ - t: 123, - m: 25 - }); - expect(callback).toHaveBeenCalledWith({ - timestamp: 123, - ruleLabel: "Don't Worry", - ruleName: 'safe', - message: "It's Ok", - ruleIndex: 0, - backgroundColor: '#38761d', - textColor: '#ffffff', - borderColor: 'rgba(0,0,0,0)', - icon: 'a-ok' - }); + return loader.promise + .then(function () { + return new Promise(function (resolve) { + setTimeout(resolve); + }); + }) + .then(function () { + expect(openmct.telemetry.subscribe.calls.count()).toBe(2); + expect(openmct.telemetry.subscribe).toHaveBeenCalledWith( + telemObjectA, + jasmine.any(Function) + ); + expect(openmct.telemetry.subscribe).toHaveBeenCalledWith( + telemObjectB, + jasmine.any(Function) + ); + + const aCallback = openmct.telemetry.subscribe.calls.all()[0].args[1]; + const bCallback = openmct.telemetry.subscribe.calls.all()[1].args[1]; + + aCallback({ + t: 123, + m: 25 + }); + expect(callback).not.toHaveBeenCalled(); + bCallback({ + t: 123, + m: 25 + }); + expect(callback).toHaveBeenCalledWith({ + timestamp: 123, + ruleLabel: "Don't Worry", + ruleName: 'safe', + message: "It's Ok", + ruleIndex: 0, + backgroundColor: '#38761d', + textColor: '#ffffff', + borderColor: 'rgba(0,0,0,0)', + icon: 'a-ok' + }); - aCallback({ - t: 140, - m: 55 - }); - expect(callback).toHaveBeenCalledWith({ - timestamp: 140, - ruleLabel: 'Start Worrying', - ruleName: 'A High', - message: 'A is a little high...', - ruleIndex: 1, - backgroundColor: '#ffff00', - textColor: '#000000', - borderColor: 'rgba(1,1,0,0)', - icon: 'a-high' - }); + aCallback({ + t: 140, + m: 55 + }); + expect(callback).toHaveBeenCalledWith({ + timestamp: 140, + ruleLabel: 'Start Worrying', + ruleName: 'A High', + message: 'A is a little high...', + ruleIndex: 1, + backgroundColor: '#ffff00', + textColor: '#000000', + borderColor: 'rgba(1,1,0,0)', + icon: 'a-high' + }); - bCallback({ - t: 140, - m: -10 - }); - expect(callback).toHaveBeenCalledWith({ - timestamp: 140, - ruleLabel: 'WORRY!', - ruleName: 'B Low', - message: 'B is Low', - ruleIndex: 2, - backgroundColor: '#ff0000', - textColor: '#ff00ff', - borderColor: 'rgba(1,0,0,0)', - icon: 'b-low' - }); + bCallback({ + t: 140, + m: -10 + }); + expect(callback).toHaveBeenCalledWith({ + timestamp: 140, + ruleLabel: 'WORRY!', + ruleName: 'B Low', + message: 'B is Low', + ruleIndex: 2, + backgroundColor: '#ff0000', + textColor: '#ff00ff', + borderColor: 'rgba(1,0,0,0)', + icon: 'b-low' + }); - aCallback({ - t: 160, - m: 25 - }); - expect(callback).toHaveBeenCalledWith({ - timestamp: 160, - ruleLabel: 'WORRY!', - ruleName: 'B Low', - message: 'B is Low', - ruleIndex: 2, - backgroundColor: '#ff0000', - textColor: '#ff00ff', - borderColor: 'rgba(1,0,0,0)', - icon: 'b-low' - }); + aCallback({ + t: 160, + m: 25 + }); + expect(callback).toHaveBeenCalledWith({ + timestamp: 160, + ruleLabel: 'WORRY!', + ruleName: 'B Low', + message: 'B is Low', + ruleIndex: 2, + backgroundColor: '#ff0000', + textColor: '#ff00ff', + borderColor: 'rgba(1,0,0,0)', + icon: 'b-low' + }); - bCallback({ - t: 160, - m: 25 - }); - expect(callback).toHaveBeenCalledWith({ - timestamp: 160, - ruleLabel: "Don't Worry", - ruleName: 'safe', - message: "It's Ok", - ruleIndex: 0, - backgroundColor: '#38761d', - textColor: '#ffffff', - borderColor: 'rgba(0,0,0,0)', - icon: 'a-ok' - }); + bCallback({ + t: 160, + m: 25 }); - }); + expect(callback).toHaveBeenCalledWith({ + timestamp: 160, + ruleLabel: "Don't Worry", + ruleName: 'safe', + message: "It's Ok", + ruleIndex: 0, + backgroundColor: '#38761d', + textColor: '#ffffff', + borderColor: 'rgba(0,0,0,0)', + icon: 'a-ok' + }); + }); + }); + + describe('providing lad telemetry', function () { + let responseDatums; + let resultsShouldBe; - describe('providing lad telemetry', function () { - let responseDatums; - let resultsShouldBe; + beforeEach(function () { + openmct.telemetry.request.and.callFake(function (rObj, options) { + expect(rObj).toEqual(jasmine.any(Object)); + expect(options).toEqual({ + size: 1, + strategy: 'latest', + domain: 'timestamp' + }); + expect(responseDatums[rObj.identifier.namespace]).toBeDefined(); - beforeEach(function () { - openmct.telemetry.request.and.callFake(function (rObj, options) { - expect(rObj).toEqual(jasmine.any(Object)); - expect(options).toEqual({ + return Promise.resolve([responseDatums[rObj.identifier.namespace]]); + }); + responseDatums = {}; + + resultsShouldBe = function (results) { + return telemetryProvider + .request(summaryWidgetObject, { size: 1, strategy: 'latest', domain: 'timestamp' + }) + .then(function (r) { + expect(r).toEqual(results); }); - expect(responseDatums[rObj.identifier.namespace]).toBeDefined(); - - return Promise.resolve([responseDatums[rObj.identifier.namespace]]); - }); - responseDatums = {}; - - resultsShouldBe = function (results) { - return telemetryProvider - .request(summaryWidgetObject, { - size: 1, - strategy: 'latest', - domain: 'timestamp' - }) - .then(function (r) { - expect(r).toEqual(results); - }); - }; - }); + }; + }); - it('returns default when no rule matches', function () { - responseDatums = { - a: { - t: 122, - m: 25 - }, - b: { - t: 111, - m: 25 - } - }; + it('returns default when no rule matches', function () { + responseDatums = { + a: { + t: 122, + m: 25 + }, + b: { + t: 111, + m: 25 + } + }; - return resultsShouldBe([ - { - timestamp: 122, - ruleLabel: "Don't Worry", - ruleName: 'safe', - message: "It's Ok", - ruleIndex: 0, - backgroundColor: '#38761d', - textColor: '#ffffff', - borderColor: 'rgba(0,0,0,0)', - icon: 'a-ok' - } - ]); - }); + return resultsShouldBe([ + { + timestamp: 122, + ruleLabel: "Don't Worry", + ruleName: 'safe', + message: "It's Ok", + ruleIndex: 0, + backgroundColor: '#38761d', + textColor: '#ffffff', + borderColor: 'rgba(0,0,0,0)', + icon: 'a-ok' + } + ]); + }); - it('returns highest priority when multiple match', function () { - responseDatums = { - a: { - t: 131, - m: 55 - }, - b: { - t: 139, - m: 5 - } - }; + it('returns highest priority when multiple match', function () { + responseDatums = { + a: { + t: 131, + m: 55 + }, + b: { + t: 139, + m: 5 + } + }; - return resultsShouldBe([ - { - timestamp: 139, - ruleLabel: 'WORRY!', - ruleName: 'B Low', - message: 'B is Low', - ruleIndex: 2, - backgroundColor: '#ff0000', - textColor: '#ff00ff', - borderColor: 'rgba(1,0,0,0)', - icon: 'b-low' - } - ]); - }); + return resultsShouldBe([ + { + timestamp: 139, + ruleLabel: 'WORRY!', + ruleName: 'B Low', + message: 'B is Low', + ruleIndex: 2, + backgroundColor: '#ff0000', + textColor: '#ff00ff', + borderColor: 'rgba(1,0,0,0)', + icon: 'b-low' + } + ]); + }); - it('returns matching rule', function () { - responseDatums = { - a: { - t: 144, - m: 55 - }, - b: { - t: 141, - m: 15 - } - }; + it('returns matching rule', function () { + responseDatums = { + a: { + t: 144, + m: 55 + }, + b: { + t: 141, + m: 15 + } + }; - return resultsShouldBe([ - { - timestamp: 144, - ruleLabel: 'Start Worrying', - ruleName: 'A High', - message: 'A is a little high...', - ruleIndex: 1, - backgroundColor: '#ffff00', - textColor: '#000000', - borderColor: 'rgba(1,1,0,0)', - icon: 'a-high' - } - ]); - }); + return resultsShouldBe([ + { + timestamp: 144, + ruleLabel: 'Start Worrying', + ruleName: 'A High', + message: 'A is a little high...', + ruleIndex: 1, + backgroundColor: '#ffff00', + textColor: '#000000', + borderColor: 'rgba(1,1,0,0)', + icon: 'a-high' + } + ]); }); }); }); diff --git a/src/plugins/summaryWidget/src/telemetry/operations.js b/src/plugins/summaryWidget/src/telemetry/operations.js index 01f87c21793..b15bcbdfb21 100644 --- a/src/plugins/summaryWidget/src/telemetry/operations.js +++ b/src/plugins/summaryWidget/src/telemetry/operations.js @@ -20,196 +20,194 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define([], function () { - const OPERATIONS = { - equalTo: { - operation: function (input) { - return input[0] === input[1]; - }, - text: 'is equal to', - appliesTo: ['number'], - inputCount: 1, - getDescription: function (values) { - return ' == ' + values[0]; - } - }, - notEqualTo: { - operation: function (input) { - return input[0] !== input[1]; - }, - text: 'is not equal to', - appliesTo: ['number'], - inputCount: 1, - getDescription: function (values) { - return ' != ' + values[0]; - } - }, - greaterThan: { - operation: function (input) { - return input[0] > input[1]; - }, - text: 'is greater than', - appliesTo: ['number'], - inputCount: 1, - getDescription: function (values) { - return ' > ' + values[0]; - } - }, - lessThan: { - operation: function (input) { - return input[0] < input[1]; - }, - text: 'is less than', - appliesTo: ['number'], - inputCount: 1, - getDescription: function (values) { - return ' < ' + values[0]; - } - }, - greaterThanOrEq: { - operation: function (input) { - return input[0] >= input[1]; - }, - text: 'is greater than or equal to', - appliesTo: ['number'], - inputCount: 1, - getDescription: function (values) { - return ' >= ' + values[0]; - } - }, - lessThanOrEq: { - operation: function (input) { - return input[0] <= input[1]; - }, - text: 'is less than or equal to', - appliesTo: ['number'], - inputCount: 1, - getDescription: function (values) { - return ' <= ' + values[0]; - } - }, - between: { - operation: function (input) { - return input[0] > input[1] && input[0] < input[2]; - }, - text: 'is between', - appliesTo: ['number'], - inputCount: 2, - getDescription: function (values) { - return ' between ' + values[0] + ' and ' + values[1]; - } - }, - notBetween: { - operation: function (input) { - return input[0] < input[1] || input[0] > input[2]; - }, - text: 'is not between', - appliesTo: ['number'], - inputCount: 2, - getDescription: function (values) { - return ' not between ' + values[0] + ' and ' + values[1]; - } - }, - textContains: { - operation: function (input) { - return input[0] && input[1] && input[0].includes(input[1]); - }, - text: 'text contains', - appliesTo: ['string'], - inputCount: 1, - getDescription: function (values) { - return ' contains ' + values[0]; - } - }, - textDoesNotContain: { - operation: function (input) { - return input[0] && input[1] && !input[0].includes(input[1]); - }, - text: 'text does not contain', - appliesTo: ['string'], - inputCount: 1, - getDescription: function (values) { - return ' does not contain ' + values[0]; - } - }, - textStartsWith: { - operation: function (input) { - return input[0].startsWith(input[1]); - }, - text: 'text starts with', - appliesTo: ['string'], - inputCount: 1, - getDescription: function (values) { - return ' starts with ' + values[0]; - } - }, - textEndsWith: { - operation: function (input) { - return input[0].endsWith(input[1]); - }, - text: 'text ends with', - appliesTo: ['string'], - inputCount: 1, - getDescription: function (values) { - return ' ends with ' + values[0]; - } - }, - textIsExactly: { - operation: function (input) { - return input[0] === input[1]; - }, - text: 'text is exactly', - appliesTo: ['string'], - inputCount: 1, - getDescription: function (values) { - return ' is exactly ' + values[0]; - } - }, - isUndefined: { - operation: function (input) { - return typeof input[0] === 'undefined'; - }, - text: 'is undefined', - appliesTo: ['string', 'number', 'enum'], - inputCount: 0, - getDescription: function () { - return ' is undefined'; - } - }, - isDefined: { - operation: function (input) { - return typeof input[0] !== 'undefined'; - }, - text: 'is defined', - appliesTo: ['string', 'number', 'enum'], - inputCount: 0, - getDescription: function () { - return ' is defined'; - } - }, - enumValueIs: { - operation: function (input) { - return input[0] === input[1]; - }, - text: 'is', - appliesTo: ['enum'], - inputCount: 1, - getDescription: function (values) { - return ' == ' + values[0]; - } - }, - enumValueIsNot: { - operation: function (input) { - return input[0] !== input[1]; - }, - text: 'is not', - appliesTo: ['enum'], - inputCount: 1, - getDescription: function (values) { - return ' != ' + values[0]; - } - } - }; +const OPERATIONS = { + equalTo: { + operation: function (input) { + return input[0] === input[1]; + }, + text: 'is equal to', + appliesTo: ['number'], + inputCount: 1, + getDescription: function (values) { + return ' == ' + values[0]; + } + }, + notEqualTo: { + operation: function (input) { + return input[0] !== input[1]; + }, + text: 'is not equal to', + appliesTo: ['number'], + inputCount: 1, + getDescription: function (values) { + return ' != ' + values[0]; + } + }, + greaterThan: { + operation: function (input) { + return input[0] > input[1]; + }, + text: 'is greater than', + appliesTo: ['number'], + inputCount: 1, + getDescription: function (values) { + return ' > ' + values[0]; + } + }, + lessThan: { + operation: function (input) { + return input[0] < input[1]; + }, + text: 'is less than', + appliesTo: ['number'], + inputCount: 1, + getDescription: function (values) { + return ' < ' + values[0]; + } + }, + greaterThanOrEq: { + operation: function (input) { + return input[0] >= input[1]; + }, + text: 'is greater than or equal to', + appliesTo: ['number'], + inputCount: 1, + getDescription: function (values) { + return ' >= ' + values[0]; + } + }, + lessThanOrEq: { + operation: function (input) { + return input[0] <= input[1]; + }, + text: 'is less than or equal to', + appliesTo: ['number'], + inputCount: 1, + getDescription: function (values) { + return ' <= ' + values[0]; + } + }, + between: { + operation: function (input) { + return input[0] > input[1] && input[0] < input[2]; + }, + text: 'is between', + appliesTo: ['number'], + inputCount: 2, + getDescription: function (values) { + return ' between ' + values[0] + ' and ' + values[1]; + } + }, + notBetween: { + operation: function (input) { + return input[0] < input[1] || input[0] > input[2]; + }, + text: 'is not between', + appliesTo: ['number'], + inputCount: 2, + getDescription: function (values) { + return ' not between ' + values[0] + ' and ' + values[1]; + } + }, + textContains: { + operation: function (input) { + return input[0] && input[1] && input[0].includes(input[1]); + }, + text: 'text contains', + appliesTo: ['string'], + inputCount: 1, + getDescription: function (values) { + return ' contains ' + values[0]; + } + }, + textDoesNotContain: { + operation: function (input) { + return input[0] && input[1] && !input[0].includes(input[1]); + }, + text: 'text does not contain', + appliesTo: ['string'], + inputCount: 1, + getDescription: function (values) { + return ' does not contain ' + values[0]; + } + }, + textStartsWith: { + operation: function (input) { + return input[0].startsWith(input[1]); + }, + text: 'text starts with', + appliesTo: ['string'], + inputCount: 1, + getDescription: function (values) { + return ' starts with ' + values[0]; + } + }, + textEndsWith: { + operation: function (input) { + return input[0].endsWith(input[1]); + }, + text: 'text ends with', + appliesTo: ['string'], + inputCount: 1, + getDescription: function (values) { + return ' ends with ' + values[0]; + } + }, + textIsExactly: { + operation: function (input) { + return input[0] === input[1]; + }, + text: 'text is exactly', + appliesTo: ['string'], + inputCount: 1, + getDescription: function (values) { + return ' is exactly ' + values[0]; + } + }, + isUndefined: { + operation: function (input) { + return typeof input[0] === 'undefined'; + }, + text: 'is undefined', + appliesTo: ['string', 'number', 'enum'], + inputCount: 0, + getDescription: function () { + return ' is undefined'; + } + }, + isDefined: { + operation: function (input) { + return typeof input[0] !== 'undefined'; + }, + text: 'is defined', + appliesTo: ['string', 'number', 'enum'], + inputCount: 0, + getDescription: function () { + return ' is defined'; + } + }, + enumValueIs: { + operation: function (input) { + return input[0] === input[1]; + }, + text: 'is', + appliesTo: ['enum'], + inputCount: 1, + getDescription: function (values) { + return ' == ' + values[0]; + } + }, + enumValueIsNot: { + operation: function (input) { + return input[0] !== input[1]; + }, + text: 'is not', + appliesTo: ['enum'], + inputCount: 1, + getDescription: function (values) { + return ' != ' + values[0]; + } + } +}; - return OPERATIONS; -}); +export default OPERATIONS; diff --git a/src/plugins/summaryWidget/src/views/SummaryWidgetView.js b/src/plugins/summaryWidget/src/views/SummaryWidgetView.js index a3dff127036..a6c7e6a2081 100644 --- a/src/plugins/summaryWidget/src/views/SummaryWidgetView.js +++ b/src/plugins/summaryWidget/src/views/SummaryWidgetView.js @@ -1,106 +1,103 @@ -define(['./summary-widget.html', '@braintree/sanitize-url'], function ( - summaryWidgetTemplate, - urlSanitizeLib -) { - const WIDGET_ICON_CLASS = 'c-sw__icon js-sw__icon'; +import * as urlSanitizeLib from '@braintree/sanitize-url'; - function SummaryWidgetView(domainObject, openmct) { - this.openmct = openmct; - this.domainObject = domainObject; - this.hasUpdated = false; - this.render = this.render.bind(this); - } +import * as summaryWidgetTemplate from './summary-widget.html'; + +const WIDGET_ICON_CLASS = 'c-sw__icon js-sw__icon'; - SummaryWidgetView.prototype.updateState = function (datum) { - this.hasUpdated = true; - this.widget.style.color = datum.textColor; - this.widget.style.backgroundColor = datum.backgroundColor; - this.widget.style.borderColor = datum.borderColor; - this.widget.title = datum.message; - this.label.title = datum.message; - this.label.innerHTML = datum.ruleLabel; - this.icon.className = WIDGET_ICON_CLASS + ' ' + datum.icon; - }; +export default function SummaryWidgetView(domainObject, openmct) { + this.openmct = openmct; + this.domainObject = domainObject; + this.hasUpdated = false; + this.render = this.render.bind(this); +} - SummaryWidgetView.prototype.render = function () { - if (this.unsubscribe) { - this.unsubscribe(); - } +SummaryWidgetView.prototype.updateState = function (datum) { + this.hasUpdated = true; + this.widget.style.color = datum.textColor; + this.widget.style.backgroundColor = datum.backgroundColor; + this.widget.style.borderColor = datum.borderColor; + this.widget.title = datum.message; + this.label.title = datum.message; + this.label.innerHTML = datum.ruleLabel; + this.icon.className = WIDGET_ICON_CLASS + ' ' + datum.icon; +}; - this.hasUpdated = false; +SummaryWidgetView.prototype.render = function () { + if (this.unsubscribe) { + this.unsubscribe(); + } - this.container.innerHTML = summaryWidgetTemplate; - this.widget = this.container.querySelector('a'); - this.icon = this.container.querySelector('#widgetIcon'); - this.label = this.container.querySelector('.js-sw__label'); + this.hasUpdated = false; - let url = this.domainObject.url; - if (url) { - this.widget.setAttribute('href', urlSanitizeLib.sanitizeUrl(url)); - } else { - this.widget.removeAttribute('href'); - } + this.container.innerHTML = summaryWidgetTemplate; + this.widget = this.container.querySelector('a'); + this.icon = this.container.querySelector('#widgetIcon'); + this.label = this.container.querySelector('.js-sw__label'); - if (this.domainObject.openNewTab === 'newTab') { - this.widget.setAttribute('target', '_blank'); - } else { - this.widget.removeAttribute('target'); - } + let url = this.domainObject.url; + if (url) { + this.widget.setAttribute('href', urlSanitizeLib.sanitizeUrl(url)); + } else { + this.widget.removeAttribute('href'); + } - const renderTracker = {}; - this.renderTracker = renderTracker; - this.openmct.telemetry - .request(this.domainObject, { - strategy: 'latest', - size: 1 - }) - .then( - function (results) { - if ( - this.destroyed || - this.hasUpdated || - this.renderTracker !== renderTracker || - results.length === 0 - ) { - return; - } + if (this.domainObject.openNewTab === 'newTab') { + this.widget.setAttribute('target', '_blank'); + } else { + this.widget.removeAttribute('target'); + } - this.updateState(results[results.length - 1]); - }.bind(this) - ); + const renderTracker = {}; + this.renderTracker = renderTracker; + this.openmct.telemetry + .request(this.domainObject, { + strategy: 'latest', + size: 1 + }) + .then( + function (results) { + if ( + this.destroyed || + this.hasUpdated || + this.renderTracker !== renderTracker || + results.length === 0 + ) { + return; + } - this.unsubscribe = this.openmct.telemetry.subscribe( - this.domainObject, - this.updateState.bind(this) + this.updateState(results[results.length - 1]); + }.bind(this) ); - }; - SummaryWidgetView.prototype.show = function (container) { - this.container = container; - this.render(); - this.removeMutationListener = this.openmct.objects.observe( - this.domainObject, - '*', - this.onMutation.bind(this) - ); - this.openmct.time.on('timeSystem', this.render); - }; + this.unsubscribe = this.openmct.telemetry.subscribe( + this.domainObject, + this.updateState.bind(this) + ); +}; - SummaryWidgetView.prototype.onMutation = function (domainObject) { - this.domainObject = domainObject; - this.render(); - }; +SummaryWidgetView.prototype.show = function (container) { + this.container = container; + this.render(); + this.removeMutationListener = this.openmct.objects.observe( + this.domainObject, + '*', + this.onMutation.bind(this) + ); + this.openmct.time.on('timeSystem', this.render); +}; - SummaryWidgetView.prototype.destroy = function (container) { - this.unsubscribe(); - this.removeMutationListener(); - this.openmct.time.off('timeSystem', this.render); - this.destroyed = true; - delete this.widget; - delete this.label; - delete this.openmct; - delete this.domainObject; - }; +SummaryWidgetView.prototype.onMutation = function (domainObject) { + this.domainObject = domainObject; + this.render(); +}; - return SummaryWidgetView; -}); +SummaryWidgetView.prototype.destroy = function (container) { + this.unsubscribe(); + this.removeMutationListener(); + this.openmct.time.off('timeSystem', this.render); + this.destroyed = true; + delete this.widget; + delete this.label; + delete this.openmct; + delete this.domainObject; +}; diff --git a/src/plugins/summaryWidget/src/views/SummaryWidgetViewProvider.js b/src/plugins/summaryWidget/src/views/SummaryWidgetViewProvider.js index aa90739e64e..88210a1eb4c 100644 --- a/src/plugins/summaryWidget/src/views/SummaryWidgetViewProvider.js +++ b/src/plugins/summaryWidget/src/views/SummaryWidgetViewProvider.js @@ -1,38 +1,52 @@ -define(['../SummaryWidget', './SummaryWidgetView', 'objectUtils'], function ( - SummaryWidgetEditView, - SummaryWidgetView, - objectUtils -) { - const DEFAULT_VIEW_PRIORITY = 100; - /** - * - */ - function SummaryWidgetViewProvider(openmct) { - return { - key: 'summary-widget-viewer', - name: 'Summary View', - cssClass: 'icon-summary-widget', - canView: function (domainObject) { - return domainObject.type === 'summary-widget'; - }, - canEdit: function (domainObject) { - return domainObject.type === 'summary-widget'; - }, - view: function (domainObject) { - return new SummaryWidgetView(domainObject, openmct); - }, - edit: function (domainObject) { - return new SummaryWidgetEditView(domainObject, openmct); - }, - priority: function (domainObject) { - if (domainObject.type === 'summary-widget') { - return Number.MAX_VALUE; - } else { - return DEFAULT_VIEW_PRIORITY; - } - } - }; - } +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2023, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT is licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * Open MCT includes source code licensed under additional open source + * licenses. See the Open Source Licenses file (LICENSES.md) included with + * this source code distribution or the Licensing information page available + * at runtime from the About dialog for additional information. + *****************************************************************************/ + +import SummaryWidgetEditView from '../SummaryWidget'; +import SummaryWidgetView from './SummaryWidgetView'; - return SummaryWidgetViewProvider; -}); +const DEFAULT_VIEW_PRIORITY = 100; +export default function SummaryWidgetViewProvider(openmct) { + return { + key: 'summary-widget-viewer', + name: 'Summary View', + cssClass: 'icon-summary-widget', + canView: function (domainObject) { + return domainObject.type === 'summary-widget'; + }, + canEdit: function (domainObject) { + return domainObject.type === 'summary-widget'; + }, + view: function (domainObject) { + return new SummaryWidgetView(domainObject, openmct); + }, + edit: function (domainObject) { + return new SummaryWidgetEditView(domainObject, openmct); + }, + priority: function (domainObject) { + if (domainObject.type === 'summary-widget') { + return Number.MAX_VALUE; + } else { + return DEFAULT_VIEW_PRIORITY; + } + } + }; +} diff --git a/src/plugins/summaryWidget/test/ConditionEvaluatorSpec.js b/src/plugins/summaryWidget/test/ConditionEvaluatorSpec.js index b9fddbb4635..4a8de803dbf 100644 --- a/src/plugins/summaryWidget/test/ConditionEvaluatorSpec.js +++ b/src/plugins/summaryWidget/test/ConditionEvaluatorSpec.js @@ -1,363 +1,363 @@ -define(['../src/ConditionEvaluator'], function (ConditionEvaluator) { - describe('A Summary Widget Rule Evaluator', function () { - let evaluator; - let testEvaluator; - let testOperation; - let mockCache; - let mockTestCache; - let mockComposition; - let mockConditions; - let mockConditionsEmpty; - let mockConditionsUndefined; - let mockConditionsAnyTrue; - let mockConditionsAllTrue; - let mockConditionsAnyFalse; - let mockConditionsAllFalse; - let mockOperations; +import ConditionEvaluator from '../src/ConditionEvaluator'; - beforeEach(function () { - mockCache = { - a: { - alpha: 3, - beta: 9, - gamma: 'Testing 1 2 3' - }, - b: { - alpha: 44, - beta: 23, - gamma: 'Hello World' - }, - c: { - foo: 'bar', - iAm: 'The Walrus', - creature: { - type: 'Centaur' - } - } - }; - mockTestCache = { - a: { - alpha: 1, - beta: 1, - gamma: 'Testing 4 5 6' - }, - b: { - alpha: 2, - beta: 2, - gamma: 'Goodbye world' - } - }; - mockComposition = { - a: {}, - b: {}, - c: {} - }; - mockConditions = [ - { - object: 'a', - key: 'alpha', - operation: 'greaterThan', - values: [2] - }, - { - object: 'b', - key: 'gamma', - operation: 'lessThan', - values: [5] - } - ]; - mockConditionsEmpty = [ - { - object: '', - key: '', - operation: '', - values: [] +describe('A Summary Widget Rule Evaluator', function () { + let evaluator; + let testEvaluator; + let testOperation; + let mockCache; + let mockTestCache; + let mockComposition; + let mockConditions; + let mockConditionsEmpty; + let mockConditionsUndefined; + let mockConditionsAnyTrue; + let mockConditionsAllTrue; + let mockConditionsAnyFalse; + let mockConditionsAllFalse; + let mockOperations; + + beforeEach(function () { + mockCache = { + a: { + alpha: 3, + beta: 9, + gamma: 'Testing 1 2 3' + }, + b: { + alpha: 44, + beta: 23, + gamma: 'Hello World' + }, + c: { + foo: 'bar', + iAm: 'The Walrus', + creature: { + type: 'Centaur' } - ]; - mockConditionsUndefined = [ - { - object: 'No Such Object', - key: '', - operation: '', - values: [] - }, - { - object: 'a', - key: 'No Such Key', - operation: '', - values: [] - }, - { - object: 'a', - key: 'alpha', - operation: 'No Such Operation', - values: [] - }, - { - object: 'all', - key: 'Nonexistent Field', - operation: 'Random Operation', - values: [] + } + }; + mockTestCache = { + a: { + alpha: 1, + beta: 1, + gamma: 'Testing 4 5 6' + }, + b: { + alpha: 2, + beta: 2, + gamma: 'Goodbye world' + } + }; + mockComposition = { + a: {}, + b: {}, + c: {} + }; + mockConditions = [ + { + object: 'a', + key: 'alpha', + operation: 'greaterThan', + values: [2] + }, + { + object: 'b', + key: 'gamma', + operation: 'lessThan', + values: [5] + } + ]; + mockConditionsEmpty = [ + { + object: '', + key: '', + operation: '', + values: [] + } + ]; + mockConditionsUndefined = [ + { + object: 'No Such Object', + key: '', + operation: '', + values: [] + }, + { + object: 'a', + key: 'No Such Key', + operation: '', + values: [] + }, + { + object: 'a', + key: 'alpha', + operation: 'No Such Operation', + values: [] + }, + { + object: 'all', + key: 'Nonexistent Field', + operation: 'Random Operation', + values: [] + }, + { + object: 'any', + key: 'Nonexistent Field', + operation: 'Whatever Operation', + values: [] + } + ]; + mockConditionsAnyTrue = [ + { + object: 'any', + key: 'alpha', + operation: 'greaterThan', + values: [5] + } + ]; + mockConditionsAnyFalse = [ + { + object: 'any', + key: 'alpha', + operation: 'greaterThan', + values: [1000] + } + ]; + mockConditionsAllFalse = [ + { + object: 'all', + key: 'alpha', + operation: 'greaterThan', + values: [5] + } + ]; + mockConditionsAllTrue = [ + { + object: 'all', + key: 'alpha', + operation: 'greaterThan', + values: [0] + } + ]; + mockOperations = { + greaterThan: { + operation: function (input) { + return input[0] > input[1]; }, - { - object: 'any', - key: 'Nonexistent Field', - operation: 'Whatever Operation', - values: [] - } - ]; - mockConditionsAnyTrue = [ - { - object: 'any', - key: 'alpha', - operation: 'greaterThan', - values: [5] - } - ]; - mockConditionsAnyFalse = [ - { - object: 'any', - key: 'alpha', - operation: 'greaterThan', - values: [1000] - } - ]; - mockConditionsAllFalse = [ - { - object: 'all', - key: 'alpha', - operation: 'greaterThan', - values: [5] - } - ]; - mockConditionsAllTrue = [ - { - object: 'all', - key: 'alpha', - operation: 'greaterThan', - values: [0] + text: 'is greater than', + appliesTo: ['number'], + inputCount: 1, + getDescription: function (values) { + return ' > ' + values[0]; } - ]; - mockOperations = { - greaterThan: { - operation: function (input) { - return input[0] > input[1]; - }, - text: 'is greater than', - appliesTo: ['number'], - inputCount: 1, - getDescription: function (values) { - return ' > ' + values[0]; - } + }, + lessThan: { + operation: function (input) { + return input[0] < input[1]; }, - lessThan: { - operation: function (input) { - return input[0] < input[1]; - }, - text: 'is less than', - appliesTo: ['number'], - inputCount: 1 + text: 'is less than', + appliesTo: ['number'], + inputCount: 1 + }, + textContains: { + operation: function (input) { + return input[0] && input[1] && input[0].includes(input[1]); }, - textContains: { - operation: function (input) { - return input[0] && input[1] && input[0].includes(input[1]); - }, - text: 'text contains', - appliesTo: ['string'], - inputCount: 1 + text: 'text contains', + appliesTo: ['string'], + inputCount: 1 + }, + textIsExactly: { + operation: function (input) { + return input[0] === input[1]; }, - textIsExactly: { - operation: function (input) { - return input[0] === input[1]; - }, - text: 'text is exactly', - appliesTo: ['string'], - inputCount: 1 + text: 'text is exactly', + appliesTo: ['string'], + inputCount: 1 + }, + isHalfHorse: { + operation: function (input) { + return input[0].type === 'Centaur'; }, - isHalfHorse: { - operation: function (input) { - return input[0].type === 'Centaur'; - }, - text: 'is Half Horse', - appliesTo: ['mythicalCreature'], - inputCount: 0, - getDescription: function () { - return 'is half horse'; - } + text: 'is Half Horse', + appliesTo: ['mythicalCreature'], + inputCount: 0, + getDescription: function () { + return 'is half horse'; } - }; - evaluator = new ConditionEvaluator(mockCache, mockComposition); - testEvaluator = new ConditionEvaluator(mockCache, mockComposition); - evaluator.operations = mockOperations; - }); + } + }; + evaluator = new ConditionEvaluator(mockCache, mockComposition); + testEvaluator = new ConditionEvaluator(mockCache, mockComposition); + evaluator.operations = mockOperations; + }); - it('evaluates a condition when it has no configuration', function () { - expect(evaluator.execute(mockConditionsEmpty, 'any')).toEqual(false); - expect(evaluator.execute(mockConditionsEmpty, 'all')).toEqual(false); - }); + it('evaluates a condition when it has no configuration', function () { + expect(evaluator.execute(mockConditionsEmpty, 'any')).toEqual(false); + expect(evaluator.execute(mockConditionsEmpty, 'all')).toEqual(false); + }); - it('correctly evaluates a set of conditions', function () { - expect(evaluator.execute(mockConditions, 'any')).toEqual(true); - expect(evaluator.execute(mockConditions, 'all')).toEqual(false); - }); + it('correctly evaluates a set of conditions', function () { + expect(evaluator.execute(mockConditions, 'any')).toEqual(true); + expect(evaluator.execute(mockConditions, 'all')).toEqual(false); + }); - it('correctly evaluates conditions involving "any telemetry"', function () { - expect(evaluator.execute(mockConditionsAnyTrue, 'any')).toEqual(true); - expect(evaluator.execute(mockConditionsAnyFalse, 'any')).toEqual(false); - }); + it('correctly evaluates conditions involving "any telemetry"', function () { + expect(evaluator.execute(mockConditionsAnyTrue, 'any')).toEqual(true); + expect(evaluator.execute(mockConditionsAnyFalse, 'any')).toEqual(false); + }); - it('correctly evaluates conditions involving "all telemetry"', function () { - expect(evaluator.execute(mockConditionsAllTrue, 'any')).toEqual(true); - expect(evaluator.execute(mockConditionsAllFalse, 'any')).toEqual(false); - }); + it('correctly evaluates conditions involving "all telemetry"', function () { + expect(evaluator.execute(mockConditionsAllTrue, 'any')).toEqual(true); + expect(evaluator.execute(mockConditionsAllFalse, 'any')).toEqual(false); + }); - it('handles malformed conditions gracefully', function () { - //if no conditions are fully defined, should return false for any mode - expect(evaluator.execute(mockConditionsUndefined, 'any')).toEqual(false); - expect(evaluator.execute(mockConditionsUndefined, 'all')).toEqual(false); - //these conditions are true: evaluator should ignore undefined conditions, - //and evaluate the rule as true - mockConditionsUndefined.push({ - object: 'a', - key: 'gamma', - operation: 'textContains', - values: ['Testing'] - }); - expect(evaluator.execute(mockConditionsUndefined, 'any')).toEqual(true); - mockConditionsUndefined.push({ - object: 'c', - key: 'iAm', - operation: 'textContains', - values: ['Walrus'] - }); - expect(evaluator.execute(mockConditionsUndefined, 'all')).toEqual(true); + it('handles malformed conditions gracefully', function () { + //if no conditions are fully defined, should return false for any mode + expect(evaluator.execute(mockConditionsUndefined, 'any')).toEqual(false); + expect(evaluator.execute(mockConditionsUndefined, 'all')).toEqual(false); + //these conditions are true: evaluator should ignore undefined conditions, + //and evaluate the rule as true + mockConditionsUndefined.push({ + object: 'a', + key: 'gamma', + operation: 'textContains', + values: ['Testing'] }); - - it('gets the keys for possible operations', function () { - expect(evaluator.getOperationKeys()).toEqual([ - 'greaterThan', - 'lessThan', - 'textContains', - 'textIsExactly', - 'isHalfHorse' - ]); + expect(evaluator.execute(mockConditionsUndefined, 'any')).toEqual(true); + mockConditionsUndefined.push({ + object: 'c', + key: 'iAm', + operation: 'textContains', + values: ['Walrus'] }); + expect(evaluator.execute(mockConditionsUndefined, 'all')).toEqual(true); + }); - it('gets output text for a given operation', function () { - expect(evaluator.getOperationText('isHalfHorse')).toEqual('is Half Horse'); - }); + it('gets the keys for possible operations', function () { + expect(evaluator.getOperationKeys()).toEqual([ + 'greaterThan', + 'lessThan', + 'textContains', + 'textIsExactly', + 'isHalfHorse' + ]); + }); - it('correctly returns whether an operation applies to a given type', function () { - expect(evaluator.operationAppliesTo('isHalfHorse', 'mythicalCreature')).toEqual(true); - expect(evaluator.operationAppliesTo('isHalfHorse', 'spaceJunk')).toEqual(false); - }); + it('gets output text for a given operation', function () { + expect(evaluator.getOperationText('isHalfHorse')).toEqual('is Half Horse'); + }); - it('returns the HTML input type associated with a given data type', function () { - expect(evaluator.getInputTypeById('string')).toEqual('text'); - }); + it('correctly returns whether an operation applies to a given type', function () { + expect(evaluator.operationAppliesTo('isHalfHorse', 'mythicalCreature')).toEqual(true); + expect(evaluator.operationAppliesTo('isHalfHorse', 'spaceJunk')).toEqual(false); + }); - it('gets the number of inputs required for a given operation', function () { - expect(evaluator.getInputCount('isHalfHorse')).toEqual(0); - expect(evaluator.getInputCount('greaterThan')).toEqual(1); - }); + it('returns the HTML input type associated with a given data type', function () { + expect(evaluator.getInputTypeById('string')).toEqual('text'); + }); - it('gets a human-readable description of a condition', function () { - expect(evaluator.getOperationDescription('isHalfHorse')).toEqual('is half horse'); - expect(evaluator.getOperationDescription('greaterThan', [1])).toEqual(' > 1'); - }); + it('gets the number of inputs required for a given operation', function () { + expect(evaluator.getInputCount('isHalfHorse')).toEqual(0); + expect(evaluator.getInputCount('greaterThan')).toEqual(1); + }); - it('allows setting a substitute cache for testing purposes, and toggling its use', function () { - evaluator.setTestDataCache(mockTestCache); - evaluator.useTestData(true); - expect(evaluator.execute(mockConditions, 'any')).toEqual(false); - expect(evaluator.execute(mockConditions, 'all')).toEqual(false); - mockConditions.push({ - object: 'a', - key: 'gamma', - operation: 'textContains', - values: ['4 5 6'] - }); - expect(evaluator.execute(mockConditions, 'any')).toEqual(true); - expect(evaluator.execute(mockConditions, 'all')).toEqual(false); - mockConditions.pop(); - evaluator.useTestData(false); - expect(evaluator.execute(mockConditions, 'any')).toEqual(true); - expect(evaluator.execute(mockConditions, 'all')).toEqual(false); - }); + it('gets a human-readable description of a condition', function () { + expect(evaluator.getOperationDescription('isHalfHorse')).toEqual('is half horse'); + expect(evaluator.getOperationDescription('greaterThan', [1])).toEqual(' > 1'); + }); - it('supports all required operations', function () { - //equal to - testOperation = testEvaluator.operations.equalTo.operation; - expect(testOperation([33, 33])).toEqual(true); - expect(testOperation([55, 147])).toEqual(false); - //not equal to - testOperation = testEvaluator.operations.notEqualTo.operation; - expect(testOperation([33, 33])).toEqual(false); - expect(testOperation([55, 147])).toEqual(true); - //greater than - testOperation = testEvaluator.operations.greaterThan.operation; - expect(testOperation([100, 33])).toEqual(true); - expect(testOperation([33, 33])).toEqual(false); - expect(testOperation([55, 147])).toEqual(false); - //less than - testOperation = testEvaluator.operations.lessThan.operation; - expect(testOperation([100, 33])).toEqual(false); - expect(testOperation([33, 33])).toEqual(false); - expect(testOperation([55, 147])).toEqual(true); - //greater than or equal to - testOperation = testEvaluator.operations.greaterThanOrEq.operation; - expect(testOperation([100, 33])).toEqual(true); - expect(testOperation([33, 33])).toEqual(true); - expect(testOperation([55, 147])).toEqual(false); - //less than or equal to - testOperation = testEvaluator.operations.lessThanOrEq.operation; - expect(testOperation([100, 33])).toEqual(false); - expect(testOperation([33, 33])).toEqual(true); - expect(testOperation([55, 147])).toEqual(true); - //between - testOperation = testEvaluator.operations.between.operation; - expect(testOperation([100, 33, 66])).toEqual(false); - expect(testOperation([1, 33, 66])).toEqual(false); - expect(testOperation([45, 33, 66])).toEqual(true); - //not between - testOperation = testEvaluator.operations.notBetween.operation; - expect(testOperation([100, 33, 66])).toEqual(true); - expect(testOperation([1, 33, 66])).toEqual(true); - expect(testOperation([45, 33, 66])).toEqual(false); - //text contains - testOperation = testEvaluator.operations.textContains.operation; - expect(testOperation(['Testing', 'tin'])).toEqual(true); - expect(testOperation(['Testing', 'bind'])).toEqual(false); - //text does not contain - testOperation = testEvaluator.operations.textDoesNotContain.operation; - expect(testOperation(['Testing', 'tin'])).toEqual(false); - expect(testOperation(['Testing', 'bind'])).toEqual(true); - //text starts with - testOperation = testEvaluator.operations.textStartsWith.operation; - expect(testOperation(['Testing', 'Tes'])).toEqual(true); - expect(testOperation(['Testing', 'ting'])).toEqual(false); - //text ends with - testOperation = testEvaluator.operations.textEndsWith.operation; - expect(testOperation(['Testing', 'Tes'])).toEqual(false); - expect(testOperation(['Testing', 'ting'])).toEqual(true); - //text is exactly - testOperation = testEvaluator.operations.textIsExactly.operation; - expect(testOperation(['Testing', 'Testing'])).toEqual(true); - expect(testOperation(['Testing', 'Test'])).toEqual(false); - //undefined - testOperation = testEvaluator.operations.isUndefined.operation; - expect(testOperation([1])).toEqual(false); - expect(testOperation([])).toEqual(true); - //isDefined - testOperation = testEvaluator.operations.isDefined.operation; - expect(testOperation([1])).toEqual(true); - expect(testOperation([])).toEqual(false); + it('allows setting a substitute cache for testing purposes, and toggling its use', function () { + evaluator.setTestDataCache(mockTestCache); + evaluator.useTestData(true); + expect(evaluator.execute(mockConditions, 'any')).toEqual(false); + expect(evaluator.execute(mockConditions, 'all')).toEqual(false); + mockConditions.push({ + object: 'a', + key: 'gamma', + operation: 'textContains', + values: ['4 5 6'] }); + expect(evaluator.execute(mockConditions, 'any')).toEqual(true); + expect(evaluator.execute(mockConditions, 'all')).toEqual(false); + mockConditions.pop(); + evaluator.useTestData(false); + expect(evaluator.execute(mockConditions, 'any')).toEqual(true); + expect(evaluator.execute(mockConditions, 'all')).toEqual(false); + }); + + it('supports all required operations', function () { + //equal to + testOperation = testEvaluator.operations.equalTo.operation; + expect(testOperation([33, 33])).toEqual(true); + expect(testOperation([55, 147])).toEqual(false); + //not equal to + testOperation = testEvaluator.operations.notEqualTo.operation; + expect(testOperation([33, 33])).toEqual(false); + expect(testOperation([55, 147])).toEqual(true); + //greater than + testOperation = testEvaluator.operations.greaterThan.operation; + expect(testOperation([100, 33])).toEqual(true); + expect(testOperation([33, 33])).toEqual(false); + expect(testOperation([55, 147])).toEqual(false); + //less than + testOperation = testEvaluator.operations.lessThan.operation; + expect(testOperation([100, 33])).toEqual(false); + expect(testOperation([33, 33])).toEqual(false); + expect(testOperation([55, 147])).toEqual(true); + //greater than or equal to + testOperation = testEvaluator.operations.greaterThanOrEq.operation; + expect(testOperation([100, 33])).toEqual(true); + expect(testOperation([33, 33])).toEqual(true); + expect(testOperation([55, 147])).toEqual(false); + //less than or equal to + testOperation = testEvaluator.operations.lessThanOrEq.operation; + expect(testOperation([100, 33])).toEqual(false); + expect(testOperation([33, 33])).toEqual(true); + expect(testOperation([55, 147])).toEqual(true); + //between + testOperation = testEvaluator.operations.between.operation; + expect(testOperation([100, 33, 66])).toEqual(false); + expect(testOperation([1, 33, 66])).toEqual(false); + expect(testOperation([45, 33, 66])).toEqual(true); + //not between + testOperation = testEvaluator.operations.notBetween.operation; + expect(testOperation([100, 33, 66])).toEqual(true); + expect(testOperation([1, 33, 66])).toEqual(true); + expect(testOperation([45, 33, 66])).toEqual(false); + //text contains + testOperation = testEvaluator.operations.textContains.operation; + expect(testOperation(['Testing', 'tin'])).toEqual(true); + expect(testOperation(['Testing', 'bind'])).toEqual(false); + //text does not contain + testOperation = testEvaluator.operations.textDoesNotContain.operation; + expect(testOperation(['Testing', 'tin'])).toEqual(false); + expect(testOperation(['Testing', 'bind'])).toEqual(true); + //text starts with + testOperation = testEvaluator.operations.textStartsWith.operation; + expect(testOperation(['Testing', 'Tes'])).toEqual(true); + expect(testOperation(['Testing', 'ting'])).toEqual(false); + //text ends with + testOperation = testEvaluator.operations.textEndsWith.operation; + expect(testOperation(['Testing', 'Tes'])).toEqual(false); + expect(testOperation(['Testing', 'ting'])).toEqual(true); + //text is exactly + testOperation = testEvaluator.operations.textIsExactly.operation; + expect(testOperation(['Testing', 'Testing'])).toEqual(true); + expect(testOperation(['Testing', 'Test'])).toEqual(false); + //undefined + testOperation = testEvaluator.operations.isUndefined.operation; + expect(testOperation([1])).toEqual(false); + expect(testOperation([])).toEqual(true); + //isDefined + testOperation = testEvaluator.operations.isDefined.operation; + expect(testOperation([1])).toEqual(true); + expect(testOperation([])).toEqual(false); + }); - it('can produce a description for all supported operations', function () { - testEvaluator.getOperationKeys().forEach(function (key) { - expect(testEvaluator.getOperationDescription(key, [])).toBeDefined(); - }); + it('can produce a description for all supported operations', function () { + testEvaluator.getOperationKeys().forEach(function (key) { + expect(testEvaluator.getOperationDescription(key, [])).toBeDefined(); }); }); }); diff --git a/src/plugins/summaryWidget/test/ConditionManagerSpec.js b/src/plugins/summaryWidget/test/ConditionManagerSpec.js index fa717c981c2..d2e350746c8 100644 --- a/src/plugins/summaryWidget/test/ConditionManagerSpec.js +++ b/src/plugins/summaryWidget/test/ConditionManagerSpec.js @@ -20,270 +20,71 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define(['../src/ConditionManager'], function (ConditionManager) { - xdescribe('A Summary Widget Condition Manager', function () { - let conditionManager; - let mockDomainObject; - let mockCompObject1; - let mockCompObject2; - let mockCompObject3; - let mockMetadata; - let mockTelemetryCallbacks; - let mockEventCallbacks; - let unsubscribeSpies; - let unregisterSpies; - let mockMetadataManagers; - let mockComposition; - let mockOpenMCT; - let mockTelemetryAPI; - let addCallbackSpy; - let loadCallbackSpy; - let removeCallbackSpy; - let telemetryCallbackSpy; - let metadataCallbackSpy; - let telemetryRequests; - let mockTelemetryValues; - let mockTelemetryValues2; - let mockConditionEvaluator; +import ConditionManager from '../src/ConditionManager'; - beforeEach(function () { - mockDomainObject = { - identifier: { - key: 'testKey' - }, - name: 'Test Object', - composition: [ - { - mockCompObject1: { - key: 'mockCompObject1' - }, - mockCompObject2: { - key: 'mockCompObject2' - } - } - ], - configuration: {} - }; - mockCompObject1 = { - identifier: { - key: 'mockCompObject1' - }, - name: 'Object 1' - }; - mockCompObject2 = { - identifier: { - key: 'mockCompObject2' - }, - name: 'Object 2' - }; - mockCompObject3 = { - identifier: { - key: 'mockCompObject3' - }, - name: 'Object 3' - }; - mockMetadata = { - mockCompObject1: { - property1: { - key: 'property1', - name: 'Property 1', - format: 'string', - hints: {} - }, - property2: { - key: 'property2', - name: 'Property 2', - hints: { - domain: 1 - } - } - }, - mockCompObject2: { - property3: { - key: 'property3', - name: 'Property 3', - format: 'string', - hints: {} - }, - property4: { - key: 'property4', - name: 'Property 4', - hints: { - range: 1 - } - } - }, - mockCompObject3: { - property1: { - key: 'property1', - name: 'Property 1', - hints: {} +xdescribe('A Summary Widget Condition Manager', function () { + let conditionManager; + let mockDomainObject; + let mockCompObject1; + let mockCompObject2; + let mockCompObject3; + let mockMetadata; + let mockTelemetryCallbacks; + let mockEventCallbacks; + let unsubscribeSpies; + let unregisterSpies; + let mockMetadataManagers; + let mockComposition; + let mockOpenMCT; + let mockTelemetryAPI; + let addCallbackSpy; + let loadCallbackSpy; + let removeCallbackSpy; + let telemetryCallbackSpy; + let metadataCallbackSpy; + let telemetryRequests; + let mockTelemetryValues; + let mockTelemetryValues2; + let mockConditionEvaluator; + + beforeEach(function () { + mockDomainObject = { + identifier: { + key: 'testKey' + }, + name: 'Test Object', + composition: [ + { + mockCompObject1: { + key: 'mockCompObject1' }, - property2: { - key: 'property2', - name: 'Property 2', - hints: {} + mockCompObject2: { + key: 'mockCompObject2' } } - }; - mockTelemetryCallbacks = {}; - mockEventCallbacks = {}; - unsubscribeSpies = jasmine.createSpyObj('mockUnsubscribeFunction', [ - 'mockCompObject1', - 'mockCompObject2', - 'mockCompObject3' - ]); - unregisterSpies = jasmine.createSpyObj('mockUnregisterFunctions', ['load', 'remove', 'add']); - mockTelemetryValues = { - mockCompObject1: { - property1: 'Its a string', - property2: 42 - }, - mockCompObject2: { - property3: 'Execute order:', - property4: 66 - }, - mockCompObject3: { - property1: 'Testing 1 2 3', - property2: 9000 - } - }; - mockTelemetryValues2 = { - mockCompObject1: { - property1: 'Its a different string', - property2: 44 - }, - mockCompObject2: { - property3: 'Execute catch:', - property4: 22 - }, - mockCompObject3: { - property1: 'Walrus', - property2: 22 - } - }; - mockMetadataManagers = { - mockCompObject1: { - values: jasmine - .createSpy('metadataManager') - .and.returnValue(Object.values(mockMetadata.mockCompObject1)) - }, - mockCompObject2: { - values: jasmine - .createSpy('metadataManager') - .and.returnValue(Object.values(mockMetadata.mockCompObject2)) - }, - mockCompObject3: { - values: jasmine - .createSpy('metadataManager') - .and.returnValue(Object.values(mockMetadata.mockCompObject2)) - } - }; - - mockComposition = jasmine.createSpyObj('composition', [ - 'on', - 'off', - 'load', - 'triggerCallback' - ]); - mockComposition.on.and.callFake(function (event, callback, context) { - mockEventCallbacks[event] = callback.bind(context); - }); - mockComposition.off.and.callFake(function (event) { - unregisterSpies[event](); - }); - mockComposition.load.and.callFake(function () { - mockComposition.triggerCallback('add', mockCompObject1); - mockComposition.triggerCallback('add', mockCompObject2); - mockComposition.triggerCallback('load'); - }); - mockComposition.triggerCallback.and.callFake(function (event, obj) { - if (event === 'add') { - mockEventCallbacks.add(obj); - } else if (event === 'remove') { - mockEventCallbacks.remove(obj.identifier); - } else { - mockEventCallbacks[event](); - } - }); - telemetryRequests = []; - mockTelemetryAPI = jasmine.createSpyObj('telemetryAPI', [ - 'request', - 'isTelemetryObject', - 'getMetadata', - 'subscribe', - 'triggerTelemetryCallback' - ]); - mockTelemetryAPI.request.and.callFake(function (obj) { - const req = { - object: obj - }; - req.promise = new Promise(function (resolve, reject) { - req.resolve = resolve; - req.reject = reject; - }); - telemetryRequests.push(req); - - return req.promise; - }); - mockTelemetryAPI.isTelemetryObject.and.returnValue(true); - mockTelemetryAPI.getMetadata.and.callFake(function (obj) { - return mockMetadataManagers[obj.identifier.key]; - }); - mockTelemetryAPI.subscribe.and.callFake(function (obj, callback) { - mockTelemetryCallbacks[obj.identifier.key] = callback; - - return unsubscribeSpies[obj.identifier.key]; - }); - mockTelemetryAPI.triggerTelemetryCallback.and.callFake(function (key) { - mockTelemetryCallbacks[key](mockTelemetryValues2[key]); - }); - - mockOpenMCT = { - telemetry: mockTelemetryAPI, - composition: {} - }; - mockOpenMCT.composition.get = jasmine.createSpy('get').and.returnValue(mockComposition); - - loadCallbackSpy = jasmine.createSpy('loadCallbackSpy'); - addCallbackSpy = jasmine.createSpy('addCallbackSpy'); - removeCallbackSpy = jasmine.createSpy('removeCallbackSpy'); - metadataCallbackSpy = jasmine.createSpy('metadataCallbackSpy'); - telemetryCallbackSpy = jasmine.createSpy('telemetryCallbackSpy'); - - conditionManager = new ConditionManager(mockDomainObject, mockOpenMCT); - conditionManager.on('load', loadCallbackSpy); - conditionManager.on('add', addCallbackSpy); - conditionManager.on('remove', removeCallbackSpy); - conditionManager.on('metadata', metadataCallbackSpy); - conditionManager.on('receiveTelemetry', telemetryCallbackSpy); - - mockConditionEvaluator = jasmine.createSpy('mockConditionEvaluator'); - mockConditionEvaluator.execute = jasmine.createSpy('execute'); - conditionManager.evaluator = mockConditionEvaluator; - }); - - it('loads the initial composition and invokes the appropriate handlers', function () { - mockComposition.triggerCallback('load'); - expect(conditionManager.getComposition()).toEqual({ - mockCompObject1: mockCompObject1, - mockCompObject2: mockCompObject2 - }); - expect(loadCallbackSpy).toHaveBeenCalled(); - expect(conditionManager.loadCompleted()).toEqual(true); - }); - - it('loads metadata from composition and gets it upon request', function () { - expect(conditionManager.getTelemetryMetadata('mockCompObject1')).toEqual( - mockMetadata.mockCompObject1 - ); - expect(conditionManager.getTelemetryMetadata('mockCompObject2')).toEqual( - mockMetadata.mockCompObject2 - ); - }); - - it('maintains lists of global metadata, and does not duplicate repeated fields', function () { - const allKeys = { + ], + configuration: {} + }; + mockCompObject1 = { + identifier: { + key: 'mockCompObject1' + }, + name: 'Object 1' + }; + mockCompObject2 = { + identifier: { + key: 'mockCompObject2' + }, + name: 'Object 2' + }; + mockCompObject3 = { + identifier: { + key: 'mockCompObject3' + }, + name: 'Object 3' + }; + mockMetadata = { + mockCompObject1: { property1: { key: 'property1', name: 'Property 1', @@ -296,7 +97,9 @@ define(['../src/ConditionManager'], function (ConditionManager) { hints: { domain: 1 } - }, + } + }, + mockCompObject2: { property3: { key: 'property3', name: 'Property 3', @@ -310,134 +113,326 @@ define(['../src/ConditionManager'], function (ConditionManager) { range: 1 } } - }; - expect(conditionManager.getTelemetryMetadata('all')).toEqual(allKeys); - expect(conditionManager.getTelemetryMetadata('any')).toEqual(allKeys); - mockComposition.triggerCallback('add', mockCompObject3); - expect(conditionManager.getTelemetryMetadata('all')).toEqual(allKeys); - expect(conditionManager.getTelemetryMetadata('any')).toEqual(allKeys); - }); + }, + mockCompObject3: { + property1: { + key: 'property1', + name: 'Property 1', + hints: {} + }, + property2: { + key: 'property2', + name: 'Property 2', + hints: {} + } + } + }; + mockTelemetryCallbacks = {}; + mockEventCallbacks = {}; + unsubscribeSpies = jasmine.createSpyObj('mockUnsubscribeFunction', [ + 'mockCompObject1', + 'mockCompObject2', + 'mockCompObject3' + ]); + unregisterSpies = jasmine.createSpyObj('mockUnregisterFunctions', ['load', 'remove', 'add']); + mockTelemetryValues = { + mockCompObject1: { + property1: 'Its a string', + property2: 42 + }, + mockCompObject2: { + property3: 'Execute order:', + property4: 66 + }, + mockCompObject3: { + property1: 'Testing 1 2 3', + property2: 9000 + } + }; + mockTelemetryValues2 = { + mockCompObject1: { + property1: 'Its a different string', + property2: 44 + }, + mockCompObject2: { + property3: 'Execute catch:', + property4: 22 + }, + mockCompObject3: { + property1: 'Walrus', + property2: 22 + } + }; + mockMetadataManagers = { + mockCompObject1: { + values: jasmine + .createSpy('metadataManager') + .and.returnValue(Object.values(mockMetadata.mockCompObject1)) + }, + mockCompObject2: { + values: jasmine + .createSpy('metadataManager') + .and.returnValue(Object.values(mockMetadata.mockCompObject2)) + }, + mockCompObject3: { + values: jasmine + .createSpy('metadataManager') + .and.returnValue(Object.values(mockMetadata.mockCompObject2)) + } + }; - it('loads and gets telemetry property types', function () { - conditionManager.parseAllPropertyTypes(); - expect(conditionManager.getTelemetryPropertyType('mockCompObject1', 'property1')).toEqual( - 'string' - ); - expect(conditionManager.getTelemetryPropertyType('mockCompObject2', 'property4')).toEqual( - 'number' - ); - expect(conditionManager.metadataLoadCompleted()).toEqual(true); - expect(metadataCallbackSpy).toHaveBeenCalled(); + mockComposition = jasmine.createSpyObj('composition', ['on', 'off', 'load', 'triggerCallback']); + mockComposition.on.and.callFake(function (event, callback, context) { + mockEventCallbacks[event] = callback.bind(context); }); - - it('responds to a composition add event and invokes the appropriate handlers', function () { - mockComposition.triggerCallback('add', mockCompObject3); - expect(addCallbackSpy).toHaveBeenCalledWith(mockCompObject3); - expect(conditionManager.getComposition()).toEqual({ - mockCompObject1: mockCompObject1, - mockCompObject2: mockCompObject2, - mockCompObject3: mockCompObject3 - }); + mockComposition.off.and.callFake(function (event) { + unregisterSpies[event](); }); - - it('responds to a composition remove event and invokes the appropriate handlers', function () { - mockComposition.triggerCallback('remove', mockCompObject2); - expect(removeCallbackSpy).toHaveBeenCalledWith({ - key: 'mockCompObject2' - }); - expect(unsubscribeSpies.mockCompObject2).toHaveBeenCalled(); - expect(conditionManager.getComposition()).toEqual({ - mockCompObject1: mockCompObject1 + mockComposition.load.and.callFake(function () { + mockComposition.triggerCallback('add', mockCompObject1); + mockComposition.triggerCallback('add', mockCompObject2); + mockComposition.triggerCallback('load'); + }); + mockComposition.triggerCallback.and.callFake(function (event, obj) { + if (event === 'add') { + mockEventCallbacks.add(obj); + } else if (event === 'remove') { + mockEventCallbacks.remove(obj.identifier); + } else { + mockEventCallbacks[event](); + } + }); + telemetryRequests = []; + mockTelemetryAPI = jasmine.createSpyObj('telemetryAPI', [ + 'request', + 'isTelemetryObject', + 'getMetadata', + 'subscribe', + 'triggerTelemetryCallback' + ]); + mockTelemetryAPI.request.and.callFake(function (obj) { + const req = { + object: obj + }; + req.promise = new Promise(function (resolve, reject) { + req.resolve = resolve; + req.reject = reject; }); + telemetryRequests.push(req); + + return req.promise; }); + mockTelemetryAPI.isTelemetryObject.and.returnValue(true); + mockTelemetryAPI.getMetadata.and.callFake(function (obj) { + return mockMetadataManagers[obj.identifier.key]; + }); + mockTelemetryAPI.subscribe.and.callFake(function (obj, callback) { + mockTelemetryCallbacks[obj.identifier.key] = callback; - it('unregisters telemetry subscriptions and composition listeners on destroy', function () { - mockComposition.triggerCallback('add', mockCompObject3); - conditionManager.destroy(); - Object.values(unsubscribeSpies).forEach(function (spy) { - expect(spy).toHaveBeenCalled(); - }); - Object.values(unregisterSpies).forEach(function (spy) { - expect(spy).toHaveBeenCalled(); - }); + return unsubscribeSpies[obj.identifier.key]; + }); + mockTelemetryAPI.triggerTelemetryCallback.and.callFake(function (key) { + mockTelemetryCallbacks[key](mockTelemetryValues2[key]); }); - it('populates its LAD cache with historical data on load, if available', function (done) { - expect(telemetryRequests.length).toBe(2); - expect(telemetryRequests[0].object).toBe(mockCompObject1); - expect(telemetryRequests[1].object).toBe(mockCompObject2); + mockOpenMCT = { + telemetry: mockTelemetryAPI, + composition: {} + }; + mockOpenMCT.composition.get = jasmine.createSpy('get').and.returnValue(mockComposition); - expect(telemetryCallbackSpy).not.toHaveBeenCalled(); + loadCallbackSpy = jasmine.createSpy('loadCallbackSpy'); + addCallbackSpy = jasmine.createSpy('addCallbackSpy'); + removeCallbackSpy = jasmine.createSpy('removeCallbackSpy'); + metadataCallbackSpy = jasmine.createSpy('metadataCallbackSpy'); + telemetryCallbackSpy = jasmine.createSpy('telemetryCallbackSpy'); - telemetryCallbackSpy.and.callFake(function () { - if (telemetryCallbackSpy.calls.count() === 2) { - expect(conditionManager.subscriptionCache.mockCompObject1.property1).toEqual( - 'Its a string' - ); - expect(conditionManager.subscriptionCache.mockCompObject2.property4).toEqual(66); - done(); - } - }); + conditionManager = new ConditionManager(mockDomainObject, mockOpenMCT); + conditionManager.on('load', loadCallbackSpy); + conditionManager.on('add', addCallbackSpy); + conditionManager.on('remove', removeCallbackSpy); + conditionManager.on('metadata', metadataCallbackSpy); + conditionManager.on('receiveTelemetry', telemetryCallbackSpy); - telemetryRequests[0].resolve([mockTelemetryValues.mockCompObject1]); - telemetryRequests[1].resolve([mockTelemetryValues.mockCompObject2]); - }); + mockConditionEvaluator = jasmine.createSpy('mockConditionEvaluator'); + mockConditionEvaluator.execute = jasmine.createSpy('execute'); + conditionManager.evaluator = mockConditionEvaluator; + }); - it('updates its LAD cache upon receiving telemetry and invokes the appropriate handlers', function () { - mockTelemetryAPI.triggerTelemetryCallback('mockCompObject1'); - expect(conditionManager.subscriptionCache.mockCompObject1.property1).toEqual( - 'Its a different string' - ); - mockTelemetryAPI.triggerTelemetryCallback('mockCompObject2'); - expect(conditionManager.subscriptionCache.mockCompObject2.property4).toEqual(22); - expect(telemetryCallbackSpy).toHaveBeenCalled(); + it('loads the initial composition and invokes the appropriate handlers', function () { + mockComposition.triggerCallback('load'); + expect(conditionManager.getComposition()).toEqual({ + mockCompObject1: mockCompObject1, + mockCompObject2: mockCompObject2 }); + expect(loadCallbackSpy).toHaveBeenCalled(); + expect(conditionManager.loadCompleted()).toEqual(true); + }); - it( - 'evaluates a set of rules and returns the id of the ' + - 'last active rule, or the first if no rules are active', - function () { - const mockRuleOrder = ['default', 'rule0', 'rule1']; - const mockRules = { - default: { - getProperty: function () {} - }, - rule0: { - getProperty: function () {} - }, - rule1: { - getProperty: function () {} - } - }; + it('loads metadata from composition and gets it upon request', function () { + expect(conditionManager.getTelemetryMetadata('mockCompObject1')).toEqual( + mockMetadata.mockCompObject1 + ); + expect(conditionManager.getTelemetryMetadata('mockCompObject2')).toEqual( + mockMetadata.mockCompObject2 + ); + }); - mockConditionEvaluator.execute.and.returnValue(false); - expect(conditionManager.executeRules(mockRuleOrder, mockRules)).toEqual('default'); - mockConditionEvaluator.execute.and.returnValue(true); - expect(conditionManager.executeRules(mockRuleOrder, mockRules)).toEqual('rule1'); + it('maintains lists of global metadata, and does not duplicate repeated fields', function () { + const allKeys = { + property1: { + key: 'property1', + name: 'Property 1', + format: 'string', + hints: {} + }, + property2: { + key: 'property2', + name: 'Property 2', + hints: { + domain: 1 + } + }, + property3: { + key: 'property3', + name: 'Property 3', + format: 'string', + hints: {} + }, + property4: { + key: 'property4', + name: 'Property 4', + hints: { + range: 1 + } } + }; + expect(conditionManager.getTelemetryMetadata('all')).toEqual(allKeys); + expect(conditionManager.getTelemetryMetadata('any')).toEqual(allKeys); + mockComposition.triggerCallback('add', mockCompObject3); + expect(conditionManager.getTelemetryMetadata('all')).toEqual(allKeys); + expect(conditionManager.getTelemetryMetadata('any')).toEqual(allKeys); + }); + + it('loads and gets telemetry property types', function () { + conditionManager.parseAllPropertyTypes(); + expect(conditionManager.getTelemetryPropertyType('mockCompObject1', 'property1')).toEqual( + 'string' ); + expect(conditionManager.getTelemetryPropertyType('mockCompObject2', 'property4')).toEqual( + 'number' + ); + expect(conditionManager.metadataLoadCompleted()).toEqual(true); + expect(metadataCallbackSpy).toHaveBeenCalled(); + }); - it('gets the human-readable name of a composition object', function () { - expect(conditionManager.getObjectName('mockCompObject1')).toEqual('Object 1'); - expect(conditionManager.getObjectName('all')).toEqual('all Telemetry'); + it('responds to a composition add event and invokes the appropriate handlers', function () { + mockComposition.triggerCallback('add', mockCompObject3); + expect(addCallbackSpy).toHaveBeenCalledWith(mockCompObject3); + expect(conditionManager.getComposition()).toEqual({ + mockCompObject1: mockCompObject1, + mockCompObject2: mockCompObject2, + mockCompObject3: mockCompObject3 }); + }); - it('gets the human-readable name of a telemetry field', function () { - expect(conditionManager.getTelemetryPropertyName('mockCompObject1', 'property1')).toEqual( - 'Property 1' - ); - expect(conditionManager.getTelemetryPropertyName('mockCompObject2', 'property4')).toEqual( - 'Property 4' - ); + it('responds to a composition remove event and invokes the appropriate handlers', function () { + mockComposition.triggerCallback('remove', mockCompObject2); + expect(removeCallbackSpy).toHaveBeenCalledWith({ + key: 'mockCompObject2' + }); + expect(unsubscribeSpies.mockCompObject2).toHaveBeenCalled(); + expect(conditionManager.getComposition()).toEqual({ + mockCompObject1: mockCompObject1 }); + }); - it('gets its associated ConditionEvaluator', function () { - expect(conditionManager.getEvaluator()).toEqual(mockConditionEvaluator); + it('unregisters telemetry subscriptions and composition listeners on destroy', function () { + mockComposition.triggerCallback('add', mockCompObject3); + conditionManager.destroy(); + Object.values(unsubscribeSpies).forEach(function (spy) { + expect(spy).toHaveBeenCalled(); + }); + Object.values(unregisterSpies).forEach(function (spy) { + expect(spy).toHaveBeenCalled(); }); + }); + + it('populates its LAD cache with historical data on load, if available', function (done) { + expect(telemetryRequests.length).toBe(2); + expect(telemetryRequests[0].object).toBe(mockCompObject1); + expect(telemetryRequests[1].object).toBe(mockCompObject2); - it('allows forcing a receive telemetry event', function () { - conditionManager.triggerTelemetryCallback(); - expect(telemetryCallbackSpy).toHaveBeenCalled(); + expect(telemetryCallbackSpy).not.toHaveBeenCalled(); + + telemetryCallbackSpy.and.callFake(function () { + if (telemetryCallbackSpy.calls.count() === 2) { + expect(conditionManager.subscriptionCache.mockCompObject1.property1).toEqual( + 'Its a string' + ); + expect(conditionManager.subscriptionCache.mockCompObject2.property4).toEqual(66); + done(); + } }); + + telemetryRequests[0].resolve([mockTelemetryValues.mockCompObject1]); + telemetryRequests[1].resolve([mockTelemetryValues.mockCompObject2]); + }); + + it('updates its LAD cache upon receiving telemetry and invokes the appropriate handlers', function () { + mockTelemetryAPI.triggerTelemetryCallback('mockCompObject1'); + expect(conditionManager.subscriptionCache.mockCompObject1.property1).toEqual( + 'Its a different string' + ); + mockTelemetryAPI.triggerTelemetryCallback('mockCompObject2'); + expect(conditionManager.subscriptionCache.mockCompObject2.property4).toEqual(22); + expect(telemetryCallbackSpy).toHaveBeenCalled(); + }); + + it( + 'evaluates a set of rules and returns the id of the ' + + 'last active rule, or the first if no rules are active', + function () { + const mockRuleOrder = ['default', 'rule0', 'rule1']; + const mockRules = { + default: { + getProperty: function () {} + }, + rule0: { + getProperty: function () {} + }, + rule1: { + getProperty: function () {} + } + }; + + mockConditionEvaluator.execute.and.returnValue(false); + expect(conditionManager.executeRules(mockRuleOrder, mockRules)).toEqual('default'); + mockConditionEvaluator.execute.and.returnValue(true); + expect(conditionManager.executeRules(mockRuleOrder, mockRules)).toEqual('rule1'); + } + ); + + it('gets the human-readable name of a composition object', function () { + expect(conditionManager.getObjectName('mockCompObject1')).toEqual('Object 1'); + expect(conditionManager.getObjectName('all')).toEqual('all Telemetry'); + }); + + it('gets the human-readable name of a telemetry field', function () { + expect(conditionManager.getTelemetryPropertyName('mockCompObject1', 'property1')).toEqual( + 'Property 1' + ); + expect(conditionManager.getTelemetryPropertyName('mockCompObject2', 'property4')).toEqual( + 'Property 4' + ); + }); + + it('gets its associated ConditionEvaluator', function () { + expect(conditionManager.getEvaluator()).toEqual(mockConditionEvaluator); + }); + + it('allows forcing a receive telemetry event', function () { + conditionManager.triggerTelemetryCallback(); + expect(telemetryCallbackSpy).toHaveBeenCalled(); }); }); diff --git a/src/plugins/summaryWidget/test/ConditionSpec.js b/src/plugins/summaryWidget/test/ConditionSpec.js index 657fd3b843c..f136caef838 100644 --- a/src/plugins/summaryWidget/test/ConditionSpec.js +++ b/src/plugins/summaryWidget/test/ConditionSpec.js @@ -20,186 +20,186 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define(['../src/Condition'], function (Condition) { - xdescribe('A summary widget condition', function () { - let testCondition; - let mockConfig; - let mockConditionManager; - let mockContainer; - let mockEvaluator; - let changeSpy; - let duplicateSpy; - let removeSpy; - let generateValuesSpy; - - beforeEach(function () { - mockContainer = document.createElement('div'); - - mockConfig = { - object: 'object1', - key: 'property1', - operation: 'operation1', - values: [1, 2, 3] - }; - - mockEvaluator = {}; - mockEvaluator.getInputCount = jasmine.createSpy('inputCount'); - mockEvaluator.getInputType = jasmine.createSpy('inputType'); - - mockConditionManager = jasmine.createSpyObj('mockConditionManager', [ - 'on', - 'getComposition', - 'loadCompleted', - 'getEvaluator', - 'getTelemetryMetadata', - 'metadataLoadCompleted', - 'getObjectName', - 'getTelemetryPropertyName' - ]); - mockConditionManager.loadCompleted.and.returnValue(false); - mockConditionManager.metadataLoadCompleted.and.returnValue(false); - mockConditionManager.getEvaluator.and.returnValue(mockEvaluator); - mockConditionManager.getComposition.and.returnValue({}); - mockConditionManager.getTelemetryMetadata.and.returnValue({}); - mockConditionManager.getObjectName.and.returnValue('Object Name'); - mockConditionManager.getTelemetryPropertyName.and.returnValue('Property Name'); - - duplicateSpy = jasmine.createSpy('duplicate'); - removeSpy = jasmine.createSpy('remove'); - changeSpy = jasmine.createSpy('change'); - generateValuesSpy = jasmine.createSpy('generateValueInputs'); - - testCondition = new Condition(mockConfig, 54, mockConditionManager); - - testCondition.on('duplicate', duplicateSpy); - testCondition.on('remove', removeSpy); - testCondition.on('change', changeSpy); - }); +import Condition from '../src/Condition'; + +xdescribe('A summary widget condition', function () { + let testCondition; + let mockConfig; + let mockConditionManager; + let mockContainer; + let mockEvaluator; + let changeSpy; + let duplicateSpy; + let removeSpy; + let generateValuesSpy; + + beforeEach(function () { + mockContainer = document.createElement('div'); + + mockConfig = { + object: 'object1', + key: 'property1', + operation: 'operation1', + values: [1, 2, 3] + }; + + mockEvaluator = {}; + mockEvaluator.getInputCount = jasmine.createSpy('inputCount'); + mockEvaluator.getInputType = jasmine.createSpy('inputType'); + + mockConditionManager = jasmine.createSpyObj('mockConditionManager', [ + 'on', + 'getComposition', + 'loadCompleted', + 'getEvaluator', + 'getTelemetryMetadata', + 'metadataLoadCompleted', + 'getObjectName', + 'getTelemetryPropertyName' + ]); + mockConditionManager.loadCompleted.and.returnValue(false); + mockConditionManager.metadataLoadCompleted.and.returnValue(false); + mockConditionManager.getEvaluator.and.returnValue(mockEvaluator); + mockConditionManager.getComposition.and.returnValue({}); + mockConditionManager.getTelemetryMetadata.and.returnValue({}); + mockConditionManager.getObjectName.and.returnValue('Object Name'); + mockConditionManager.getTelemetryPropertyName.and.returnValue('Property Name'); + + duplicateSpy = jasmine.createSpy('duplicate'); + removeSpy = jasmine.createSpy('remove'); + changeSpy = jasmine.createSpy('change'); + generateValuesSpy = jasmine.createSpy('generateValueInputs'); + + testCondition = new Condition(mockConfig, 54, mockConditionManager); + + testCondition.on('duplicate', duplicateSpy); + testCondition.on('remove', removeSpy); + testCondition.on('change', changeSpy); + }); - it('exposes a DOM element to represent itself in the view', function () { - mockContainer.append(testCondition.getDOM()); - expect(mockContainer.querySelectorAll('.t-condition').length).toEqual(1); - }); + it('exposes a DOM element to represent itself in the view', function () { + mockContainer.append(testCondition.getDOM()); + expect(mockContainer.querySelectorAll('.t-condition').length).toEqual(1); + }); - it('responds to a change in its object select', function () { - testCondition.selects.object.setSelected(''); - expect(changeSpy).toHaveBeenCalledWith({ - value: '', - property: 'object', - index: 54 - }); + it('responds to a change in its object select', function () { + testCondition.selects.object.setSelected(''); + expect(changeSpy).toHaveBeenCalledWith({ + value: '', + property: 'object', + index: 54 }); + }); - it('responds to a change in its key select', function () { - testCondition.selects.key.setSelected(''); - expect(changeSpy).toHaveBeenCalledWith({ - value: '', - property: 'key', - index: 54 - }); + it('responds to a change in its key select', function () { + testCondition.selects.key.setSelected(''); + expect(changeSpy).toHaveBeenCalledWith({ + value: '', + property: 'key', + index: 54 }); + }); - it('responds to a change in its operation select', function () { - testCondition.generateValueInputs = generateValuesSpy; - testCondition.selects.operation.setSelected(''); - expect(changeSpy).toHaveBeenCalledWith({ - value: '', - property: 'operation', - index: 54 - }); - expect(generateValuesSpy).toHaveBeenCalledWith(''); + it('responds to a change in its operation select', function () { + testCondition.generateValueInputs = generateValuesSpy; + testCondition.selects.operation.setSelected(''); + expect(changeSpy).toHaveBeenCalledWith({ + value: '', + property: 'operation', + index: 54 }); + expect(generateValuesSpy).toHaveBeenCalledWith(''); + }); - it('generates value inputs of the appropriate type and quantity', function () { - let inputs; + it('generates value inputs of the appropriate type and quantity', function () { + let inputs; - mockContainer.append(testCondition.getDOM()); - mockEvaluator.getInputType.and.returnValue('number'); - mockEvaluator.getInputCount.and.returnValue(3); - testCondition.generateValueInputs(''); + mockContainer.append(testCondition.getDOM()); + mockEvaluator.getInputType.and.returnValue('number'); + mockEvaluator.getInputCount.and.returnValue(3); + testCondition.generateValueInputs(''); - inputs = mockContainer.querySelectorAll('input'); - const numberInputs = Array.from(inputs).filter((input) => input.type === 'number'); + inputs = mockContainer.querySelectorAll('input'); + const numberInputs = Array.from(inputs).filter((input) => input.type === 'number'); - expect(numberInputs.length).toEqual(3); - expect(numberInputs[0].valueAsNumber).toEqual(1); - expect(numberInputs[1].valueAsNumber).toEqual(2); - expect(numberInputs[2].valueAsNumber).toEqual(3); + expect(numberInputs.length).toEqual(3); + expect(numberInputs[0].valueAsNumber).toEqual(1); + expect(numberInputs[1].valueAsNumber).toEqual(2); + expect(numberInputs[2].valueAsNumber).toEqual(3); - mockEvaluator.getInputType.and.returnValue('text'); - mockEvaluator.getInputCount.and.returnValue(2); - testCondition.config.values = ['Text I Am', 'Text It Is']; - testCondition.generateValueInputs(''); + mockEvaluator.getInputType.and.returnValue('text'); + mockEvaluator.getInputCount.and.returnValue(2); + testCondition.config.values = ['Text I Am', 'Text It Is']; + testCondition.generateValueInputs(''); - inputs = mockContainer.querySelectorAll('input'); - const textInputs = Array.from(inputs).filter((input) => input.type === 'text'); + inputs = mockContainer.querySelectorAll('input'); + const textInputs = Array.from(inputs).filter((input) => input.type === 'text'); - expect(textInputs.length).toEqual(2); - expect(textInputs[0].value).toEqual('Text I Am'); - expect(textInputs[1].value).toEqual('Text It Is'); - }); + expect(textInputs.length).toEqual(2); + expect(textInputs[0].value).toEqual('Text I Am'); + expect(textInputs[1].value).toEqual('Text It Is'); + }); - it('ensures reasonable defaults on values if none are provided', function () { - let inputs; + it('ensures reasonable defaults on values if none are provided', function () { + let inputs; - mockContainer.append(testCondition.getDOM()); - mockEvaluator.getInputType.and.returnValue('number'); - mockEvaluator.getInputCount.and.returnValue(3); - testCondition.config.values = []; - testCondition.generateValueInputs(''); + mockContainer.append(testCondition.getDOM()); + mockEvaluator.getInputType.and.returnValue('number'); + mockEvaluator.getInputCount.and.returnValue(3); + testCondition.config.values = []; + testCondition.generateValueInputs(''); - inputs = Array.from(mockContainer.querySelectorAll('input')); + inputs = Array.from(mockContainer.querySelectorAll('input')); - expect(inputs[0].valueAsNumber).toEqual(0); - expect(inputs[1].valueAsNumber).toEqual(0); - expect(inputs[2].valueAsNumber).toEqual(0); - expect(testCondition.config.values).toEqual([0, 0, 0]); + expect(inputs[0].valueAsNumber).toEqual(0); + expect(inputs[1].valueAsNumber).toEqual(0); + expect(inputs[2].valueAsNumber).toEqual(0); + expect(testCondition.config.values).toEqual([0, 0, 0]); - mockEvaluator.getInputType.and.returnValue('text'); - mockEvaluator.getInputCount.and.returnValue(2); - testCondition.config.values = []; - testCondition.generateValueInputs(''); + mockEvaluator.getInputType.and.returnValue('text'); + mockEvaluator.getInputCount.and.returnValue(2); + testCondition.config.values = []; + testCondition.generateValueInputs(''); - inputs = Array.from(mockContainer.querySelectorAll('input')); + inputs = Array.from(mockContainer.querySelectorAll('input')); - expect(inputs[0].value).toEqual(''); - expect(inputs[1].value).toEqual(''); - expect(testCondition.config.values).toEqual(['', '']); - }); + expect(inputs[0].value).toEqual(''); + expect(inputs[1].value).toEqual(''); + expect(testCondition.config.values).toEqual(['', '']); + }); + + it('responds to a change in its value inputs', function () { + mockContainer.append(testCondition.getDOM()); + mockEvaluator.getInputType.and.returnValue('number'); + mockEvaluator.getInputCount.and.returnValue(3); + testCondition.generateValueInputs(''); - it('responds to a change in its value inputs', function () { - mockContainer.append(testCondition.getDOM()); - mockEvaluator.getInputType.and.returnValue('number'); - mockEvaluator.getInputCount.and.returnValue(3); - testCondition.generateValueInputs(''); - - const event = new Event('input', { - bubbles: true, - cancelable: true - }); - const inputs = mockContainer.querySelectorAll('input'); - - inputs[1].value = 9001; - inputs[1].dispatchEvent(event); - - expect(changeSpy).toHaveBeenCalledWith({ - value: 9001, - property: 'values[1]', - index: 54 - }); + const event = new Event('input', { + bubbles: true, + cancelable: true }); + const inputs = mockContainer.querySelectorAll('input'); - it('can remove itself from the configuration', function () { - testCondition.remove(); - expect(removeSpy).toHaveBeenCalledWith(54); + inputs[1].value = 9001; + inputs[1].dispatchEvent(event); + + expect(changeSpy).toHaveBeenCalledWith({ + value: 9001, + property: 'values[1]', + index: 54 }); + }); + + it('can remove itself from the configuration', function () { + testCondition.remove(); + expect(removeSpy).toHaveBeenCalledWith(54); + }); - it('can duplicate itself', function () { - testCondition.duplicate(); - expect(duplicateSpy).toHaveBeenCalledWith({ - sourceCondition: mockConfig, - index: 54 - }); + it('can duplicate itself', function () { + testCondition.duplicate(); + expect(duplicateSpy).toHaveBeenCalledWith({ + sourceCondition: mockConfig, + index: 54 }); }); }); diff --git a/src/plugins/summaryWidget/test/RuleSpec.js b/src/plugins/summaryWidget/test/RuleSpec.js index 880708529a2..b8c99c05b74 100644 --- a/src/plugins/summaryWidget/test/RuleSpec.js +++ b/src/plugins/summaryWidget/test/RuleSpec.js @@ -1,143 +1,53 @@ -define(['../src/Rule'], function (Rule) { - describe('A Summary Widget Rule', function () { - let mockRuleConfig; - let mockDomainObject; - let mockOpenMCT; - let mockConditionManager; - let mockWidgetDnD; - let mockEvaluator; - let mockContainer; - let testRule; - let removeSpy; - let duplicateSpy; - let changeSpy; - let conditionChangeSpy; - - beforeEach(function () { - mockRuleConfig = { - name: 'Name', - id: 'mockRule', - icon: 'test-icon-name', - style: { - 'background-color': '', - 'border-color': '', - color: '' - }, - expanded: true, - conditions: [ - { - object: '', - key: '', - operation: '', - values: [] - }, - { - object: 'blah', - key: 'blah', - operation: 'blah', - values: ['blah.', 'blah!', 'blah?'] - } - ] - }; - mockDomainObject = { - configuration: { - ruleConfigById: { - mockRule: mockRuleConfig, - otherRule: {} - }, - ruleOrder: ['default', 'mockRule', 'otherRule'] - } - }; - - mockOpenMCT = {}; - mockOpenMCT.objects = {}; - mockOpenMCT.objects.mutate = jasmine.createSpy('mutate'); - - mockEvaluator = {}; - mockEvaluator.getOperationDescription = jasmine - .createSpy('evaluator') - .and.returnValue('Operation Description'); - - mockConditionManager = jasmine.createSpyObj('mockConditionManager', [ - 'on', - 'getComposition', - 'loadCompleted', - 'getEvaluator', - 'getTelemetryMetadata', - 'metadataLoadCompleted', - 'getObjectName', - 'getTelemetryPropertyName' - ]); - mockConditionManager.loadCompleted.and.returnValue(false); - mockConditionManager.metadataLoadCompleted.and.returnValue(false); - mockConditionManager.getEvaluator.and.returnValue(mockEvaluator); - mockConditionManager.getComposition.and.returnValue({}); - mockConditionManager.getTelemetryMetadata.and.returnValue({}); - mockConditionManager.getObjectName.and.returnValue('Object Name'); - mockConditionManager.getTelemetryPropertyName.and.returnValue('Property Name'); - - mockWidgetDnD = jasmine.createSpyObj('dnd', ['on', 'setDragImage', 'dragStart']); - - mockContainer = document.createElement('div'); - - removeSpy = jasmine.createSpy('removeCallback'); - duplicateSpy = jasmine.createSpy('duplicateCallback'); - changeSpy = jasmine.createSpy('changeCallback'); - conditionChangeSpy = jasmine.createSpy('conditionChangeCallback'); - - testRule = new Rule( - mockRuleConfig, - mockDomainObject, - mockOpenMCT, - mockConditionManager, - mockWidgetDnD - ); - testRule.on('remove', removeSpy); - testRule.on('duplicate', duplicateSpy); - testRule.on('change', changeSpy); - testRule.on('conditionChange', conditionChangeSpy); - }); - - it('closes its configuration panel on initial load', function () { - expect(testRule.getProperty('expanded')).toEqual(false); - }); - - it('gets its DOM element', function () { - mockContainer.append(testRule.getDOM()); - expect(mockContainer.querySelectorAll('.l-widget-rule').length).toBeGreaterThan(0); - }); - - it('gets its configuration properties', function () { - expect(testRule.getProperty('name')).toEqual('Name'); - expect(testRule.getProperty('icon')).toEqual('test-icon-name'); - }); - - it('can duplicate itself', function () { - testRule.duplicate(); - mockRuleConfig.expanded = true; - expect(duplicateSpy).toHaveBeenCalledWith(mockRuleConfig); - }); - - it('can remove itself from the configuration', function () { - testRule.remove(); - expect(removeSpy).toHaveBeenCalled(); - expect(mockDomainObject.configuration.ruleConfigById.mockRule).not.toBeDefined(); - expect(mockDomainObject.configuration.ruleOrder).toEqual(['default', 'otherRule']); - }); - - it('updates its configuration on a condition change and invokes callbacks', function () { - testRule.onConditionChange({ - value: 'newValue', - property: 'object', - index: 0 - }); - expect(testRule.getProperty('conditions')[0].object).toEqual('newValue'); - expect(conditionChangeSpy).toHaveBeenCalled(); - }); - - it('allows initializing a new condition with a default configuration', function () { - testRule.initCondition(); - expect(mockRuleConfig.conditions).toEqual([ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2023, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT is licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * Open MCT includes source code licensed under additional open source + * licenses. See the Open Source Licenses file (LICENSES.md) included with + * this source code distribution or the Licensing information page available + * at runtime from the About dialog for additional information. + *****************************************************************************/ + +import Rule from '../src/Rule'; + +describe('A Summary Widget Rule', function () { + let mockRuleConfig; + let mockDomainObject; + let mockOpenMCT; + let mockConditionManager; + let mockWidgetDnD; + let mockEvaluator; + let mockContainer; + let testRule; + let removeSpy; + let duplicateSpy; + let changeSpy; + let conditionChangeSpy; + + beforeEach(function () { + mockRuleConfig = { + name: 'Name', + id: 'mockRule', + icon: 'test-icon-name', + style: { + 'background-color': '', + 'border-color': '', + color: '' + }, + expanded: true, + conditions: [ { object: '', key: '', @@ -149,145 +59,257 @@ define(['../src/Rule'], function (Rule) { key: 'blah', operation: 'blah', values: ['blah.', 'blah!', 'blah?'] - }, - { - object: '', - key: '', - operation: '', - values: [] } - ]); - }); - - it('allows initializing a new condition from a given configuration', function () { - testRule.initCondition({ - sourceCondition: { - object: 'object1', - key: 'key1', - operation: 'operation1', - values: [1, 2, 3] - }, - index: 0 - }); - expect(mockRuleConfig.conditions).toEqual([ - { - object: '', - key: '', - operation: '', - values: [] - }, - { - object: 'object1', - key: 'key1', - operation: 'operation1', - values: [1, 2, 3] + ] + }; + mockDomainObject = { + configuration: { + ruleConfigById: { + mockRule: mockRuleConfig, + otherRule: {} }, - { - object: 'blah', - key: 'blah', - operation: 'blah', - values: ['blah.', 'blah!', 'blah?'] - } - ]); - }); + ruleOrder: ['default', 'mockRule', 'otherRule'] + } + }; + + mockOpenMCT = {}; + mockOpenMCT.objects = {}; + mockOpenMCT.objects.mutate = jasmine.createSpy('mutate'); + + mockEvaluator = {}; + mockEvaluator.getOperationDescription = jasmine + .createSpy('evaluator') + .and.returnValue('Operation Description'); + + mockConditionManager = jasmine.createSpyObj('mockConditionManager', [ + 'on', + 'getComposition', + 'loadCompleted', + 'getEvaluator', + 'getTelemetryMetadata', + 'metadataLoadCompleted', + 'getObjectName', + 'getTelemetryPropertyName' + ]); + mockConditionManager.loadCompleted.and.returnValue(false); + mockConditionManager.metadataLoadCompleted.and.returnValue(false); + mockConditionManager.getEvaluator.and.returnValue(mockEvaluator); + mockConditionManager.getComposition.and.returnValue({}); + mockConditionManager.getTelemetryMetadata.and.returnValue({}); + mockConditionManager.getObjectName.and.returnValue('Object Name'); + mockConditionManager.getTelemetryPropertyName.and.returnValue('Property Name'); + + mockWidgetDnD = jasmine.createSpyObj('dnd', ['on', 'setDragImage', 'dragStart']); + + mockContainer = document.createElement('div'); + + removeSpy = jasmine.createSpy('removeCallback'); + duplicateSpy = jasmine.createSpy('duplicateCallback'); + changeSpy = jasmine.createSpy('changeCallback'); + conditionChangeSpy = jasmine.createSpy('conditionChangeCallback'); + + testRule = new Rule( + mockRuleConfig, + mockDomainObject, + mockOpenMCT, + mockConditionManager, + mockWidgetDnD + ); + testRule.on('remove', removeSpy); + testRule.on('duplicate', duplicateSpy); + testRule.on('change', changeSpy); + testRule.on('conditionChange', conditionChangeSpy); + }); - it('invokes mutate when updating the domain object', function () { - testRule.updateDomainObject(); - expect(mockOpenMCT.objects.mutate).toHaveBeenCalled(); - }); + it('closes its configuration panel on initial load', function () { + expect(testRule.getProperty('expanded')).toEqual(false); + }); - it('builds condition view from condition configuration', function () { - mockContainer.append(testRule.getDOM()); - expect(mockContainer.querySelectorAll('.t-condition').length).toEqual(2); - }); + it('gets its DOM element', function () { + mockContainer.append(testRule.getDOM()); + expect(mockContainer.querySelectorAll('.l-widget-rule').length).toBeGreaterThan(0); + }); + + it('gets its configuration properties', function () { + expect(testRule.getProperty('name')).toEqual('Name'); + expect(testRule.getProperty('icon')).toEqual('test-icon-name'); + }); - it('responds to input of style properties, and updates the preview', function () { - testRule.colorInputs['background-color'].set('#434343'); - expect(mockRuleConfig.style['background-color']).toEqual('#434343'); - testRule.colorInputs['border-color'].set('#666666'); - expect(mockRuleConfig.style['border-color']).toEqual('#666666'); - testRule.colorInputs.color.set('#999999'); - expect(mockRuleConfig.style.color).toEqual('#999999'); + it('can duplicate itself', function () { + testRule.duplicate(); + mockRuleConfig.expanded = true; + expect(duplicateSpy).toHaveBeenCalledWith(mockRuleConfig); + }); - expect(testRule.thumbnail.style['background-color']).toEqual('rgb(67, 67, 67)'); - expect(testRule.thumbnail.style['border-color']).toEqual('rgb(102, 102, 102)'); - expect(testRule.thumbnail.style.color).toEqual('rgb(153, 153, 153)'); + it('can remove itself from the configuration', function () { + testRule.remove(); + expect(removeSpy).toHaveBeenCalled(); + expect(mockDomainObject.configuration.ruleConfigById.mockRule).not.toBeDefined(); + expect(mockDomainObject.configuration.ruleOrder).toEqual(['default', 'otherRule']); + }); - expect(changeSpy).toHaveBeenCalled(); + it('updates its configuration on a condition change and invokes callbacks', function () { + testRule.onConditionChange({ + value: 'newValue', + property: 'object', + index: 0 }); + expect(testRule.getProperty('conditions')[0].object).toEqual('newValue'); + expect(conditionChangeSpy).toHaveBeenCalled(); + }); - it('responds to input for the icon property', function () { - testRule.iconInput.set('icon-alert-rect'); - expect(mockRuleConfig.icon).toEqual('icon-alert-rect'); - expect(changeSpy).toHaveBeenCalled(); - }); + it('allows initializing a new condition with a default configuration', function () { + testRule.initCondition(); + expect(mockRuleConfig.conditions).toEqual([ + { + object: '', + key: '', + operation: '', + values: [] + }, + { + object: 'blah', + key: 'blah', + operation: 'blah', + values: ['blah.', 'blah!', 'blah?'] + }, + { + object: '', + key: '', + operation: '', + values: [] + } + ]); + }); - /* - test for js condition commented out for v1 - */ - - // it('responds to input of text properties', function () { - // var testInputs = ['name', 'label', 'message', 'jsCondition'], - // input; - - // testInputs.forEach(function (key) { - // input = testRule.textInputs[key]; - // input.prop('value', 'A new ' + key); - // input.trigger('input'); - // expect(mockRuleConfig[key]).toEqual('A new ' + key); - // }); - - // expect(changeSpy).toHaveBeenCalled(); - // }); - - it('allows input for when the rule triggers', function () { - testRule.trigger.value = 'all'; - const event = new Event('change', { - bubbles: true, - cancelable: true - }); - testRule.trigger.dispatchEvent(event); - expect(testRule.config.trigger).toEqual('all'); - expect(conditionChangeSpy).toHaveBeenCalled(); + it('allows initializing a new condition from a given configuration', function () { + testRule.initCondition({ + sourceCondition: { + object: 'object1', + key: 'key1', + operation: 'operation1', + values: [1, 2, 3] + }, + index: 0 }); + expect(mockRuleConfig.conditions).toEqual([ + { + object: '', + key: '', + operation: '', + values: [] + }, + { + object: 'object1', + key: 'key1', + operation: 'operation1', + values: [1, 2, 3] + }, + { + object: 'blah', + key: 'blah', + operation: 'blah', + values: ['blah.', 'blah!', 'blah?'] + } + ]); + }); - it('generates a human-readable description from its conditions', function () { - testRule.generateDescription(); - expect(testRule.config.description).toContain( - "Object Name's Property Name Operation Description" - ); - testRule.config.trigger = 'js'; - testRule.generateDescription(); - expect(testRule.config.description).toContain( - 'when a custom JavaScript condition evaluates to true' - ); - }); + it('invokes mutate when updating the domain object', function () { + testRule.updateDomainObject(); + expect(mockOpenMCT.objects.mutate).toHaveBeenCalled(); + }); - it('initiates a drag event when its grippy is clicked', function () { - const event = new Event('mousedown', { - bubbles: true, - cancelable: true - }); - testRule.grippy.dispatchEvent(event); + it('builds condition view from condition configuration', function () { + mockContainer.append(testRule.getDOM()); + expect(mockContainer.querySelectorAll('.t-condition').length).toEqual(2); + }); + + it('responds to input of style properties, and updates the preview', function () { + testRule.colorInputs['background-color'].set('#434343'); + expect(mockRuleConfig.style['background-color']).toEqual('#434343'); + testRule.colorInputs['border-color'].set('#666666'); + expect(mockRuleConfig.style['border-color']).toEqual('#666666'); + testRule.colorInputs.color.set('#999999'); + expect(mockRuleConfig.style.color).toEqual('#999999'); + + expect(testRule.thumbnail.style['background-color']).toEqual('rgb(67, 67, 67)'); + expect(testRule.thumbnail.style['border-color']).toEqual('rgb(102, 102, 102)'); + expect(testRule.thumbnail.style.color).toEqual('rgb(153, 153, 153)'); + + expect(changeSpy).toHaveBeenCalled(); + }); - expect(mockWidgetDnD.setDragImage).toHaveBeenCalled(); - expect(mockWidgetDnD.dragStart).toHaveBeenCalledWith('mockRule'); + it('responds to input for the icon property', function () { + testRule.iconInput.set('icon-alert-rect'); + expect(mockRuleConfig.icon).toEqual('icon-alert-rect'); + expect(changeSpy).toHaveBeenCalled(); + }); + + /* + test for js condition commented out for v1 + */ + + // it('responds to input of text properties', function () { + // var testInputs = ['name', 'label', 'message', 'jsCondition'], + // input; + + // testInputs.forEach(function (key) { + // input = testRule.textInputs[key]; + // input.prop('value', 'A new ' + key); + // input.trigger('input'); + // expect(mockRuleConfig[key]).toEqual('A new ' + key); + // }); + + // expect(changeSpy).toHaveBeenCalled(); + // }); + + it('allows input for when the rule triggers', function () { + testRule.trigger.value = 'all'; + const event = new Event('change', { + bubbles: true, + cancelable: true }); + testRule.trigger.dispatchEvent(event); + expect(testRule.config.trigger).toEqual('all'); + expect(conditionChangeSpy).toHaveBeenCalled(); + }); - /* - test for js condition commented out for v1 - */ + it('generates a human-readable description from its conditions', function () { + testRule.generateDescription(); + expect(testRule.config.description).toContain( + "Object Name's Property Name Operation Description" + ); + testRule.config.trigger = 'js'; + testRule.generateDescription(); + expect(testRule.config.description).toContain( + 'when a custom JavaScript condition evaluates to true' + ); + }); - it('can remove a condition from its configuration', function () { - testRule.removeCondition(0); - expect(testRule.config.conditions).toEqual([ - { - object: 'blah', - key: 'blah', - operation: 'blah', - values: ['blah.', 'blah!', 'blah?'] - } - ]); + it('initiates a drag event when its grippy is clicked', function () { + const event = new Event('mousedown', { + bubbles: true, + cancelable: true }); + testRule.grippy.dispatchEvent(event); + + expect(mockWidgetDnD.setDragImage).toHaveBeenCalled(); + expect(mockWidgetDnD.dragStart).toHaveBeenCalledWith('mockRule'); + }); + + /* + test for js condition commented out for v1 + */ + + it('can remove a condition from its configuration', function () { + testRule.removeCondition(0); + expect(testRule.config.conditions).toEqual([ + { + object: 'blah', + key: 'blah', + operation: 'blah', + values: ['blah.', 'blah!', 'blah?'] + } + ]); }); }); diff --git a/src/plugins/summaryWidget/test/SummaryWidgetSpec.js b/src/plugins/summaryWidget/test/SummaryWidgetSpec.js index 9021a4cfc8e..fdcb0ad5f66 100644 --- a/src/plugins/summaryWidget/test/SummaryWidgetSpec.js +++ b/src/plugins/summaryWidget/test/SummaryWidgetSpec.js @@ -20,173 +20,173 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define(['../src/SummaryWidget'], function (SummaryWidget) { - xdescribe('The Summary Widget', function () { - let summaryWidget; - let mockDomainObject; - let mockOldDomainObject; - let mockOpenMCT; - let mockObjectService; - let mockStatusCapability; - let mockComposition; - let mockContainer; - let listenCallback; - let listenCallbackSpy; - - beforeEach(function () { - mockDomainObject = { - identifier: { - key: 'testKey', - namespace: 'testNamespace' - }, - name: 'testName', - composition: [], - configuration: {} - }; - mockComposition = jasmine.createSpyObj('composition', ['on', 'off', 'load']); - mockStatusCapability = jasmine.createSpyObj('statusCapability', [ - 'get', - 'listen', - 'triggerCallback' - ]); - - listenCallbackSpy = jasmine.createSpy('listenCallbackSpy', function () {}); - mockStatusCapability.get.and.returnValue([]); - mockStatusCapability.listen.and.callFake(function (callback) { - listenCallback = callback; - - return listenCallbackSpy; - }); - mockStatusCapability.triggerCallback.and.callFake(function () { - listenCallback(['editing']); - }); - - mockOldDomainObject = {}; - mockOldDomainObject.getCapability = jasmine.createSpy('capability'); - mockOldDomainObject.getCapability.and.returnValue(mockStatusCapability); - - mockObjectService = {}; - mockObjectService.getObjects = jasmine.createSpy('objectService'); - mockObjectService.getObjects.and.returnValue( - new Promise(function (resolve, reject) { - resolve({ - 'testNamespace:testKey': mockOldDomainObject - }); - }) - ); - mockOpenMCT = jasmine.createSpyObj('openmct', ['$injector', 'composition', 'objects']); - mockOpenMCT.$injector.get = jasmine.createSpy('get'); - mockOpenMCT.$injector.get.and.returnValue(mockObjectService); - mockOpenMCT.composition = jasmine.createSpyObj('composition', ['get', 'on']); - mockOpenMCT.composition.get.and.returnValue(mockComposition); - mockOpenMCT.objects.mutate = jasmine.createSpy('mutate'); - mockOpenMCT.objects.observe = jasmine.createSpy('observe'); - mockOpenMCT.objects.observe.and.returnValue(function () {}); - - summaryWidget = new SummaryWidget(mockDomainObject, mockOpenMCT); - mockContainer = document.createElement('div'); - summaryWidget.show(mockContainer); +import SummaryWidget from '../src/SummaryWidget'; + +xdescribe('The Summary Widget', function () { + let summaryWidget; + let mockDomainObject; + let mockOldDomainObject; + let mockOpenMCT; + let mockObjectService; + let mockStatusCapability; + let mockComposition; + let mockContainer; + let listenCallback; + let listenCallbackSpy; + + beforeEach(function () { + mockDomainObject = { + identifier: { + key: 'testKey', + namespace: 'testNamespace' + }, + name: 'testName', + composition: [], + configuration: {} + }; + mockComposition = jasmine.createSpyObj('composition', ['on', 'off', 'load']); + mockStatusCapability = jasmine.createSpyObj('statusCapability', [ + 'get', + 'listen', + 'triggerCallback' + ]); + + listenCallbackSpy = jasmine.createSpy('listenCallbackSpy', function () {}); + mockStatusCapability.get.and.returnValue([]); + mockStatusCapability.listen.and.callFake(function (callback) { + listenCallback = callback; + + return listenCallbackSpy; }); - - it('queries with legacyId', function () { - expect(mockObjectService.getObjects).toHaveBeenCalledWith(['testNamespace:testKey']); + mockStatusCapability.triggerCallback.and.callFake(function () { + listenCallback(['editing']); }); - it('adds its DOM element to the view', function () { - expect(mockContainer.getElementsByClassName('w-summary-widget').length).toBeGreaterThan(0); - }); + mockOldDomainObject = {}; + mockOldDomainObject.getCapability = jasmine.createSpy('capability'); + mockOldDomainObject.getCapability.and.returnValue(mockStatusCapability); + + mockObjectService = {}; + mockObjectService.getObjects = jasmine.createSpy('objectService'); + mockObjectService.getObjects.and.returnValue( + new Promise(function (resolve, reject) { + resolve({ + 'testNamespace:testKey': mockOldDomainObject + }); + }) + ); + mockOpenMCT = jasmine.createSpyObj('openmct', ['$injector', 'composition', 'objects']); + mockOpenMCT.$injector.get = jasmine.createSpy('get'); + mockOpenMCT.$injector.get.and.returnValue(mockObjectService); + mockOpenMCT.composition = jasmine.createSpyObj('composition', ['get', 'on']); + mockOpenMCT.composition.get.and.returnValue(mockComposition); + mockOpenMCT.objects.mutate = jasmine.createSpy('mutate'); + mockOpenMCT.objects.observe = jasmine.createSpy('observe'); + mockOpenMCT.objects.observe.and.returnValue(function () {}); + + summaryWidget = new SummaryWidget(mockDomainObject, mockOpenMCT); + mockContainer = document.createElement('div'); + summaryWidget.show(mockContainer); + }); - it('initializes a default rule', function () { - expect(mockDomainObject.configuration.ruleConfigById.default).toBeDefined(); - expect(mockDomainObject.configuration.ruleOrder).toEqual(['default']); - }); + it('queries with legacyId', function () { + expect(mockObjectService.getObjects).toHaveBeenCalledWith(['testNamespace:testKey']); + }); - it('builds rules and rule placeholders in view from configuration', function () { - expect(summaryWidget.ruleArea.querySelectorAll('.l-widget-rule').length).toEqual(2); - }); + it('adds its DOM element to the view', function () { + expect(mockContainer.getElementsByClassName('w-summary-widget').length).toBeGreaterThan(0); + }); - it('allows initializing a new rule with a particular identifier', function () { - summaryWidget.initRule('rule0', 'Rule'); - expect(mockDomainObject.configuration.ruleConfigById.rule0).toBeDefined(); - }); + it('initializes a default rule', function () { + expect(mockDomainObject.configuration.ruleConfigById.default).toBeDefined(); + expect(mockDomainObject.configuration.ruleOrder).toEqual(['default']); + }); - it('allows adding a new rule with a unique identifier to the configuration and view', function () { - summaryWidget.addRule(); - expect(mockDomainObject.configuration.ruleOrder.length).toEqual(2); - mockDomainObject.configuration.ruleOrder.forEach(function (ruleId) { - expect(mockDomainObject.configuration.ruleConfigById[ruleId]).toBeDefined(); - }); - summaryWidget.addRule(); - expect(mockDomainObject.configuration.ruleOrder.length).toEqual(3); - mockDomainObject.configuration.ruleOrder.forEach(function (ruleId) { - expect(mockDomainObject.configuration.ruleConfigById[ruleId]).toBeDefined(); - }); - expect(summaryWidget.ruleArea.querySelectorAll('.l-widget-rule').length).toEqual(6); - }); + it('builds rules and rule placeholders in view from configuration', function () { + expect(summaryWidget.ruleArea.querySelectorAll('.l-widget-rule').length).toEqual(2); + }); - it('allows duplicating a rule from source configuration', function () { - const sourceConfig = JSON.parse( - JSON.stringify(mockDomainObject.configuration.ruleConfigById.default) - ); - summaryWidget.duplicateRule(sourceConfig); - expect(Object.keys(mockDomainObject.configuration.ruleConfigById).length).toEqual(2); - }); + it('allows initializing a new rule with a particular identifier', function () { + summaryWidget.initRule('rule0', 'Rule'); + expect(mockDomainObject.configuration.ruleConfigById.rule0).toBeDefined(); + }); - it('does not duplicate an existing rule in the configuration', function () { - summaryWidget.initRule('default', 'Default'); - expect(Object.keys(mockDomainObject.configuration.ruleConfigById).length).toEqual(1); + it('allows adding a new rule with a unique identifier to the configuration and view', function () { + summaryWidget.addRule(); + expect(mockDomainObject.configuration.ruleOrder.length).toEqual(2); + mockDomainObject.configuration.ruleOrder.forEach(function (ruleId) { + expect(mockDomainObject.configuration.ruleConfigById[ruleId]).toBeDefined(); }); - - it('uses mutate when updating the domain object only when in edit mode', function () { - summaryWidget.editing = true; - summaryWidget.updateDomainObject(); - expect(mockOpenMCT.objects.mutate).toHaveBeenCalled(); + summaryWidget.addRule(); + expect(mockDomainObject.configuration.ruleOrder.length).toEqual(3); + mockDomainObject.configuration.ruleOrder.forEach(function (ruleId) { + expect(mockDomainObject.configuration.ruleConfigById[ruleId]).toBeDefined(); }); + expect(summaryWidget.ruleArea.querySelectorAll('.l-widget-rule').length).toEqual(6); + }); - it('shows configuration interfaces when in edit mode, and hides them otherwise', function () { - setTimeout(function () { - summaryWidget.onEdit([]); - expect(summaryWidget.editing).toEqual(false); - expect(summaryWidget.ruleArea.css('display')).toEqual('none'); - expect(summaryWidget.testDataArea.css('display')).toEqual('none'); - expect(summaryWidget.addRuleButton.css('display')).toEqual('none'); - summaryWidget.onEdit(['editing']); - expect(summaryWidget.editing).toEqual(true); - expect(summaryWidget.ruleArea.css('display')).not.toEqual('none'); - expect(summaryWidget.testDataArea.css('display')).not.toEqual('none'); - expect(summaryWidget.addRuleButton.css('display')).not.toEqual('none'); - }, 100); - }); + it('allows duplicating a rule from source configuration', function () { + const sourceConfig = JSON.parse( + JSON.stringify(mockDomainObject.configuration.ruleConfigById.default) + ); + summaryWidget.duplicateRule(sourceConfig); + expect(Object.keys(mockDomainObject.configuration.ruleConfigById).length).toEqual(2); + }); - it('unregisters any registered listeners on a destroy', function () { - setTimeout(function () { - summaryWidget.destroy(); - expect(listenCallbackSpy).toHaveBeenCalled(); - }, 100); - }); + it('does not duplicate an existing rule in the configuration', function () { + summaryWidget.initRule('default', 'Default'); + expect(Object.keys(mockDomainObject.configuration.ruleConfigById).length).toEqual(1); + }); - it('allows reorders of rules', function () { - summaryWidget.initRule('rule0'); - summaryWidget.initRule('rule1'); - summaryWidget.domainObject.configuration.ruleOrder = ['default', 'rule0', 'rule1']; - summaryWidget.reorder({ - draggingId: 'rule1', - dropTarget: 'default' - }); - expect(summaryWidget.domainObject.configuration.ruleOrder).toEqual([ - 'default', - 'rule1', - 'rule0' - ]); - }); + it('uses mutate when updating the domain object only when in edit mode', function () { + summaryWidget.editing = true; + summaryWidget.updateDomainObject(); + expect(mockOpenMCT.objects.mutate).toHaveBeenCalled(); + }); - it('adds hyperlink to the widget button and sets newTab preference', function () { - summaryWidget.addHyperlink('https://www.nasa.gov', 'newTab'); + it('shows configuration interfaces when in edit mode, and hides them otherwise', function () { + setTimeout(function () { + summaryWidget.onEdit([]); + expect(summaryWidget.editing).toEqual(false); + expect(summaryWidget.ruleArea.css('display')).toEqual('none'); + expect(summaryWidget.testDataArea.css('display')).toEqual('none'); + expect(summaryWidget.addRuleButton.css('display')).toEqual('none'); + summaryWidget.onEdit(['editing']); + expect(summaryWidget.editing).toEqual(true); + expect(summaryWidget.ruleArea.css('display')).not.toEqual('none'); + expect(summaryWidget.testDataArea.css('display')).not.toEqual('none'); + expect(summaryWidget.addRuleButton.css('display')).not.toEqual('none'); + }, 100); + }); - const widgetButton = mockContainer.querySelector('#widget'); + it('unregisters any registered listeners on a destroy', function () { + setTimeout(function () { + summaryWidget.destroy(); + expect(listenCallbackSpy).toHaveBeenCalled(); + }, 100); + }); - expect(widgetButton.href).toEqual('https://www.nasa.gov/'); - expect(widgetButton.target).toEqual('_blank'); + it('allows reorders of rules', function () { + summaryWidget.initRule('rule0'); + summaryWidget.initRule('rule1'); + summaryWidget.domainObject.configuration.ruleOrder = ['default', 'rule0', 'rule1']; + summaryWidget.reorder({ + draggingId: 'rule1', + dropTarget: 'default' }); + expect(summaryWidget.domainObject.configuration.ruleOrder).toEqual([ + 'default', + 'rule1', + 'rule0' + ]); + }); + + it('adds hyperlink to the widget button and sets newTab preference', function () { + summaryWidget.addHyperlink('https://www.nasa.gov', 'newTab'); + + const widgetButton = mockContainer.querySelector('#widget'); + + expect(widgetButton.href).toEqual('https://www.nasa.gov/'); + expect(widgetButton.target).toEqual('_blank'); }); }); diff --git a/src/plugins/summaryWidget/test/SummaryWidgetViewPolicySpec.js b/src/plugins/summaryWidget/test/SummaryWidgetViewPolicySpec.js index e10a5caaa4a..c8c483edb81 100644 --- a/src/plugins/summaryWidget/test/SummaryWidgetViewPolicySpec.js +++ b/src/plugins/summaryWidget/test/SummaryWidgetViewPolicySpec.js @@ -20,39 +20,39 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define(['../SummaryWidgetViewPolicy'], function (SummaryWidgetViewPolicy) { - describe('SummaryWidgetViewPolicy', function () { - let policy; - let domainObject; - let view; - beforeEach(function () { - policy = new SummaryWidgetViewPolicy(); - domainObject = jasmine.createSpyObj('domainObject', ['getModel']); - domainObject.getModel.and.returnValue({}); - view = {}; - }); +import SummaryWidgetViewPolicy from '../SummaryWidgetViewPolicy'; + +describe('SummaryWidgetViewPolicy', function () { + let policy; + let domainObject; + let view; + beforeEach(function () { + policy = new SummaryWidgetViewPolicy(); + domainObject = jasmine.createSpyObj('domainObject', ['getModel']); + domainObject.getModel.and.returnValue({}); + view = {}; + }); - it('returns true for other object types', function () { - domainObject.getModel.and.returnValue({ - type: 'random' - }); - expect(policy.allow(view, domainObject)).toBe(true); + it('returns true for other object types', function () { + domainObject.getModel.and.returnValue({ + type: 'random' }); + expect(policy.allow(view, domainObject)).toBe(true); + }); - it('allows summary widget view for summary widgets', function () { - domainObject.getModel.and.returnValue({ - type: 'summary-widget' - }); - view.key = 'summary-widget-viewer'; - expect(policy.allow(view, domainObject)).toBe(true); + it('allows summary widget view for summary widgets', function () { + domainObject.getModel.and.returnValue({ + type: 'summary-widget' }); + view.key = 'summary-widget-viewer'; + expect(policy.allow(view, domainObject)).toBe(true); + }); - it('disallows other views for summary widgets', function () { - domainObject.getModel.and.returnValue({ - type: 'summary-widget' - }); - view.key = 'other view'; - expect(policy.allow(view, domainObject)).toBe(false); + it('disallows other views for summary widgets', function () { + domainObject.getModel.and.returnValue({ + type: 'summary-widget' }); + view.key = 'other view'; + expect(policy.allow(view, domainObject)).toBe(false); }); }); diff --git a/src/plugins/summaryWidget/test/TestDataItemSpec.js b/src/plugins/summaryWidget/test/TestDataItemSpec.js index 3e9ac3d31fa..be11730e4fb 100644 --- a/src/plugins/summaryWidget/test/TestDataItemSpec.js +++ b/src/plugins/summaryWidget/test/TestDataItemSpec.js @@ -1,167 +1,167 @@ -define(['../src/TestDataItem'], function (TestDataItem) { - describe('A summary widget test data item', function () { - let testDataItem; - let mockConfig; - let mockConditionManager; - let mockContainer; - let mockEvaluator; - let changeSpy; - let duplicateSpy; - let removeSpy; - let generateValueSpy; - - beforeEach(function () { - mockContainer = document.createElement('div'); - - mockConfig = { - object: 'object1', - key: 'property1', - value: 1 - }; - - mockEvaluator = {}; - mockEvaluator.getInputTypeById = jasmine.createSpy('inputType'); - - mockConditionManager = jasmine.createSpyObj('mockConditionManager', [ - 'on', - 'getComposition', - 'loadCompleted', - 'getEvaluator', - 'getTelemetryMetadata', - 'metadataLoadCompleted', - 'getObjectName', - 'getTelemetryPropertyName', - 'getTelemetryPropertyType' - ]); - mockConditionManager.loadCompleted.and.returnValue(false); - mockConditionManager.metadataLoadCompleted.and.returnValue(false); - mockConditionManager.getEvaluator.and.returnValue(mockEvaluator); - mockConditionManager.getComposition.and.returnValue({}); - mockConditionManager.getTelemetryMetadata.and.returnValue({}); - mockConditionManager.getObjectName.and.returnValue('Object Name'); - mockConditionManager.getTelemetryPropertyName.and.returnValue('Property Name'); - mockConditionManager.getTelemetryPropertyType.and.returnValue(''); - - duplicateSpy = jasmine.createSpy('duplicate'); - removeSpy = jasmine.createSpy('remove'); - changeSpy = jasmine.createSpy('change'); - generateValueSpy = jasmine.createSpy('generateValueInput'); - - testDataItem = new TestDataItem(mockConfig, 54, mockConditionManager); - - testDataItem.on('duplicate', duplicateSpy); - testDataItem.on('remove', removeSpy); - testDataItem.on('change', changeSpy); - }); +import TestDataItem from '../src/TestDataItem'; + +describe('A summary widget test data item', function () { + let testDataItem; + let mockConfig; + let mockConditionManager; + let mockContainer; + let mockEvaluator; + let changeSpy; + let duplicateSpy; + let removeSpy; + let generateValueSpy; + + beforeEach(function () { + mockContainer = document.createElement('div'); + + mockConfig = { + object: 'object1', + key: 'property1', + value: 1 + }; + + mockEvaluator = {}; + mockEvaluator.getInputTypeById = jasmine.createSpy('inputType'); + + mockConditionManager = jasmine.createSpyObj('mockConditionManager', [ + 'on', + 'getComposition', + 'loadCompleted', + 'getEvaluator', + 'getTelemetryMetadata', + 'metadataLoadCompleted', + 'getObjectName', + 'getTelemetryPropertyName', + 'getTelemetryPropertyType' + ]); + mockConditionManager.loadCompleted.and.returnValue(false); + mockConditionManager.metadataLoadCompleted.and.returnValue(false); + mockConditionManager.getEvaluator.and.returnValue(mockEvaluator); + mockConditionManager.getComposition.and.returnValue({}); + mockConditionManager.getTelemetryMetadata.and.returnValue({}); + mockConditionManager.getObjectName.and.returnValue('Object Name'); + mockConditionManager.getTelemetryPropertyName.and.returnValue('Property Name'); + mockConditionManager.getTelemetryPropertyType.and.returnValue(''); + + duplicateSpy = jasmine.createSpy('duplicate'); + removeSpy = jasmine.createSpy('remove'); + changeSpy = jasmine.createSpy('change'); + generateValueSpy = jasmine.createSpy('generateValueInput'); + + testDataItem = new TestDataItem(mockConfig, 54, mockConditionManager); + + testDataItem.on('duplicate', duplicateSpy); + testDataItem.on('remove', removeSpy); + testDataItem.on('change', changeSpy); + }); - it('exposes a DOM element to represent itself in the view', function () { - mockContainer.append(testDataItem.getDOM()); - expect(mockContainer.querySelectorAll('.t-test-data-item').length).toEqual(1); - }); + it('exposes a DOM element to represent itself in the view', function () { + mockContainer.append(testDataItem.getDOM()); + expect(mockContainer.querySelectorAll('.t-test-data-item').length).toEqual(1); + }); - it('responds to a change in its object select', function () { - testDataItem.selects.object.setSelected(''); - expect(changeSpy).toHaveBeenCalledWith({ - value: '', - property: 'object', - index: 54 - }); + it('responds to a change in its object select', function () { + testDataItem.selects.object.setSelected(''); + expect(changeSpy).toHaveBeenCalledWith({ + value: '', + property: 'object', + index: 54 }); + }); - it('responds to a change in its key select', function () { - testDataItem.generateValueInput = generateValueSpy; - testDataItem.selects.key.setSelected(''); - expect(changeSpy).toHaveBeenCalledWith({ - value: '', - property: 'key', - index: 54 - }); - expect(generateValueSpy).toHaveBeenCalledWith(''); + it('responds to a change in its key select', function () { + testDataItem.generateValueInput = generateValueSpy; + testDataItem.selects.key.setSelected(''); + expect(changeSpy).toHaveBeenCalledWith({ + value: '', + property: 'key', + index: 54 }); + expect(generateValueSpy).toHaveBeenCalledWith(''); + }); - it('generates a value input of the appropriate type', function () { - let inputs; + it('generates a value input of the appropriate type', function () { + let inputs; - mockContainer.append(testDataItem.getDOM()); - mockEvaluator.getInputTypeById.and.returnValue('number'); - testDataItem.generateValueInput(''); + mockContainer.append(testDataItem.getDOM()); + mockEvaluator.getInputTypeById.and.returnValue('number'); + testDataItem.generateValueInput(''); - inputs = mockContainer.querySelectorAll('input'); - const numberInputs = Array.from(inputs).filter((input) => input.type === 'number'); + inputs = mockContainer.querySelectorAll('input'); + const numberInputs = Array.from(inputs).filter((input) => input.type === 'number'); - expect(numberInputs.length).toEqual(1); - expect(inputs[0].valueAsNumber).toEqual(1); + expect(numberInputs.length).toEqual(1); + expect(inputs[0].valueAsNumber).toEqual(1); - mockEvaluator.getInputTypeById.and.returnValue('text'); - testDataItem.config.value = 'Text I Am'; - testDataItem.generateValueInput(''); + mockEvaluator.getInputTypeById.and.returnValue('text'); + testDataItem.config.value = 'Text I Am'; + testDataItem.generateValueInput(''); - inputs = mockContainer.querySelectorAll('input'); - const textInputs = Array.from(inputs).filter((input) => input.type === 'text'); + inputs = mockContainer.querySelectorAll('input'); + const textInputs = Array.from(inputs).filter((input) => input.type === 'text'); - expect(textInputs.length).toEqual(1); - expect(inputs[0].value).toEqual('Text I Am'); - }); + expect(textInputs.length).toEqual(1); + expect(inputs[0].value).toEqual('Text I Am'); + }); - it('ensures reasonable defaults on values if none are provided', function () { - let inputs; + it('ensures reasonable defaults on values if none are provided', function () { + let inputs; - mockContainer.append(testDataItem.getDOM()); + mockContainer.append(testDataItem.getDOM()); - mockEvaluator.getInputTypeById.and.returnValue('number'); - testDataItem.config.value = undefined; - testDataItem.generateValueInput(''); + mockEvaluator.getInputTypeById.and.returnValue('number'); + testDataItem.config.value = undefined; + testDataItem.generateValueInput(''); - inputs = mockContainer.querySelectorAll('input'); - const numberInputs = Array.from(inputs).filter((input) => input.type === 'number'); + inputs = mockContainer.querySelectorAll('input'); + const numberInputs = Array.from(inputs).filter((input) => input.type === 'number'); - expect(numberInputs.length).toEqual(1); - expect(inputs[0].valueAsNumber).toEqual(0); - expect(testDataItem.config.value).toEqual(0); + expect(numberInputs.length).toEqual(1); + expect(inputs[0].valueAsNumber).toEqual(0); + expect(testDataItem.config.value).toEqual(0); - mockEvaluator.getInputTypeById.and.returnValue('text'); - testDataItem.config.value = undefined; - testDataItem.generateValueInput(''); + mockEvaluator.getInputTypeById.and.returnValue('text'); + testDataItem.config.value = undefined; + testDataItem.generateValueInput(''); - inputs = mockContainer.querySelectorAll('input'); - const textInputs = Array.from(inputs).filter((input) => input.type === 'text'); + inputs = mockContainer.querySelectorAll('input'); + const textInputs = Array.from(inputs).filter((input) => input.type === 'text'); - expect(textInputs.length).toEqual(1); - expect(inputs[0].value).toEqual(''); - expect(testDataItem.config.value).toEqual(''); - }); + expect(textInputs.length).toEqual(1); + expect(inputs[0].value).toEqual(''); + expect(testDataItem.config.value).toEqual(''); + }); - it('responds to a change in its value inputs', function () { - mockContainer.append(testDataItem.getDOM()); - mockEvaluator.getInputTypeById.and.returnValue('number'); - testDataItem.generateValueInput(''); + it('responds to a change in its value inputs', function () { + mockContainer.append(testDataItem.getDOM()); + mockEvaluator.getInputTypeById.and.returnValue('number'); + testDataItem.generateValueInput(''); - const event = new Event('input', { - bubbles: true, - cancelable: true - }); + const event = new Event('input', { + bubbles: true, + cancelable: true + }); - mockContainer.querySelector('input').value = 9001; - mockContainer.querySelector('input').dispatchEvent(event); + mockContainer.querySelector('input').value = 9001; + mockContainer.querySelector('input').dispatchEvent(event); - expect(changeSpy).toHaveBeenCalledWith({ - value: 9001, - property: 'value', - index: 54 - }); + expect(changeSpy).toHaveBeenCalledWith({ + value: 9001, + property: 'value', + index: 54 }); + }); - it('can remove itself from the configuration', function () { - testDataItem.remove(); - expect(removeSpy).toHaveBeenCalledWith(54); - }); + it('can remove itself from the configuration', function () { + testDataItem.remove(); + expect(removeSpy).toHaveBeenCalledWith(54); + }); - it('can duplicate itself', function () { - testDataItem.duplicate(); - expect(duplicateSpy).toHaveBeenCalledWith({ - sourceItem: mockConfig, - index: 54 - }); + it('can duplicate itself', function () { + testDataItem.duplicate(); + expect(duplicateSpy).toHaveBeenCalledWith({ + sourceItem: mockConfig, + index: 54 }); }); }); diff --git a/src/plugins/summaryWidget/test/TestDataManagerSpec.js b/src/plugins/summaryWidget/test/TestDataManagerSpec.js index 3cf488ca093..517adbe9101 100644 --- a/src/plugins/summaryWidget/test/TestDataManagerSpec.js +++ b/src/plugins/summaryWidget/test/TestDataManagerSpec.js @@ -1,250 +1,244 @@ -define(['../src/TestDataManager'], function (TestDataManager) { - describe('A Summary Widget Rule', function () { - let mockDomainObject; - let mockOpenMCT; - let mockConditionManager; - let mockEvaluator; - let mockContainer; - let mockTelemetryMetadata; - let testDataManager; - let mockCompObject1; - let mockCompObject2; - - beforeEach(function () { - mockDomainObject = { - configuration: { - testDataConfig: [ - { - object: '', - key: '', - value: '' - }, - { - object: 'object1', - key: 'property1', - value: 66 - }, - { - object: 'object2', - key: 'property4', - value: 'Text It Is' - } - ] - }, - composition: [ +import TestDataManager from '../src/TestDataManager'; + +describe('A Summary Widget Rule', function () { + let mockDomainObject; + let mockOpenMCT; + let mockConditionManager; + let mockEvaluator; + let mockContainer; + let mockTelemetryMetadata; + let testDataManager; + let mockCompObject1; + let mockCompObject2; + + beforeEach(function () { + mockDomainObject = { + configuration: { + testDataConfig: [ { - object1: { - key: 'object1', - name: 'Object 1' - }, - object2: { - key: 'object2', - name: 'Object 2' - } - } - ] - }; - - mockTelemetryMetadata = { - object1: { - property1: { - key: 'property1' + object: '', + key: '', + value: '' }, - property2: { - key: 'property2' + { + object: 'object1', + key: 'property1', + value: 66 + }, + { + object: 'object2', + key: 'property4', + value: 'Text It Is' } - }, - object2: { - property3: { - key: 'property3' + ] + }, + composition: [ + { + object1: { + key: 'object1', + name: 'Object 1' }, - property4: { - key: 'property4' + object2: { + key: 'object2', + name: 'Object 2' } } - }; + ] + }; - mockCompObject1 = { - identifier: { - key: 'object1' + mockTelemetryMetadata = { + object1: { + property1: { + key: 'property1' }, - name: 'Object 1' - }; - mockCompObject2 = { - identifier: { - key: 'object2' + property2: { + key: 'property2' + } + }, + object2: { + property3: { + key: 'property3' }, - name: 'Object 2' - }; - - mockOpenMCT = {}; - mockOpenMCT.objects = {}; - mockOpenMCT.objects.mutate = jasmine.createSpy('mutate'); - - mockEvaluator = {}; - mockEvaluator.setTestDataCache = jasmine.createSpy('testDataCache'); - mockEvaluator.useTestData = jasmine.createSpy('useTestData'); - - mockConditionManager = jasmine.createSpyObj('mockConditionManager', [ - 'on', - 'getComposition', - 'loadCompleted', - 'getEvaluator', - 'getTelemetryMetadata', - 'metadataLoadCompleted', - 'getObjectName', - 'getTelemetryPropertyName', - 'triggerTelemetryCallback' - ]); - mockConditionManager.loadCompleted.and.returnValue(false); - mockConditionManager.metadataLoadCompleted.and.returnValue(false); - mockConditionManager.getEvaluator.and.returnValue(mockEvaluator); - mockConditionManager.getComposition.and.returnValue({ - object1: mockCompObject1, - object2: mockCompObject2 - }); - mockConditionManager.getTelemetryMetadata.and.callFake(function (id) { - return mockTelemetryMetadata[id]; - }); - mockConditionManager.getObjectName.and.returnValue('Object Name'); - mockConditionManager.getTelemetryPropertyName.and.returnValue('Property Name'); - - mockContainer = document.createElement('div'); - - testDataManager = new TestDataManager(mockDomainObject, mockConditionManager, mockOpenMCT); + property4: { + key: 'property4' + } + } + }; + + mockCompObject1 = { + identifier: { + key: 'object1' + }, + name: 'Object 1' + }; + mockCompObject2 = { + identifier: { + key: 'object2' + }, + name: 'Object 2' + }; + + mockOpenMCT = {}; + mockOpenMCT.objects = {}; + mockOpenMCT.objects.mutate = jasmine.createSpy('mutate'); + + mockEvaluator = {}; + mockEvaluator.setTestDataCache = jasmine.createSpy('testDataCache'); + mockEvaluator.useTestData = jasmine.createSpy('useTestData'); + + mockConditionManager = jasmine.createSpyObj('mockConditionManager', [ + 'on', + 'getComposition', + 'loadCompleted', + 'getEvaluator', + 'getTelemetryMetadata', + 'metadataLoadCompleted', + 'getObjectName', + 'getTelemetryPropertyName', + 'triggerTelemetryCallback' + ]); + mockConditionManager.loadCompleted.and.returnValue(false); + mockConditionManager.metadataLoadCompleted.and.returnValue(false); + mockConditionManager.getEvaluator.and.returnValue(mockEvaluator); + mockConditionManager.getComposition.and.returnValue({ + object1: mockCompObject1, + object2: mockCompObject2 + }); + mockConditionManager.getTelemetryMetadata.and.callFake(function (id) { + return mockTelemetryMetadata[id]; }); + mockConditionManager.getObjectName.and.returnValue('Object Name'); + mockConditionManager.getTelemetryPropertyName.and.returnValue('Property Name'); - it('closes its configuration panel on initial load', function () {}); + mockContainer = document.createElement('div'); - it('exposes a DOM element to represent itself in the view', function () { - mockContainer.append(testDataManager.getDOM()); - expect(mockContainer.querySelectorAll('.t-widget-test-data-content').length).toBeGreaterThan( - 0 - ); - }); + testDataManager = new TestDataManager(mockDomainObject, mockConditionManager, mockOpenMCT); + }); - it('generates a test cache in the format expected by a condition evaluator', function () { - testDataManager.updateTestCache(); - expect(mockEvaluator.setTestDataCache).toHaveBeenCalledWith({ - object1: { - property1: 66, - property2: '' - }, - object2: { - property3: '', - property4: 'Text It Is' - } - }); - }); + it('closes its configuration panel on initial load', function () {}); - it( - 'updates its configuration on a item change and provides an updated' + - 'cache to the evaluator', - function () { - testDataManager.onItemChange({ - value: 26, - property: 'value', - index: 1 - }); - expect(testDataManager.config[1].value).toEqual(26); - expect(mockEvaluator.setTestDataCache).toHaveBeenCalledWith({ - object1: { - property1: 26, - property2: '' - }, - object2: { - property3: '', - property4: 'Text It Is' - } - }); - } - ); + it('exposes a DOM element to represent itself in the view', function () { + mockContainer.append(testDataManager.getDOM()); + expect(mockContainer.querySelectorAll('.t-widget-test-data-content').length).toBeGreaterThan(0); + }); - it('allows initializing a new item with a default configuration', function () { - testDataManager.initItem(); - expect(mockDomainObject.configuration.testDataConfig).toEqual([ - { - object: '', - key: '', - value: '' - }, - { - object: 'object1', - key: 'property1', - value: 66 - }, - { - object: 'object2', - key: 'property4', - value: 'Text It Is' - }, - { - object: '', - key: '', - value: '' - } - ]); + it('generates a test cache in the format expected by a condition evaluator', function () { + testDataManager.updateTestCache(); + expect(mockEvaluator.setTestDataCache).toHaveBeenCalledWith({ + object1: { + property1: 66, + property2: '' + }, + object2: { + property3: '', + property4: 'Text It Is' + } }); + }); - it('allows initializing a new item from a given configuration', function () { - testDataManager.initItem({ - sourceItem: { - object: 'object2', - key: 'property3', - value: 1 - }, - index: 0 - }); - expect(mockDomainObject.configuration.testDataConfig).toEqual([ - { - object: '', - key: '', - value: '' - }, - { - object: 'object2', - key: 'property3', - value: 1 - }, - { - object: 'object1', - key: 'property1', - value: 66 - }, - { - object: 'object2', - key: 'property4', - value: 'Text It Is' - } - ]); + it('updates its configuration on a item change and provides an updated cache to the evaluator', function () { + testDataManager.onItemChange({ + value: 26, + property: 'value', + index: 1 }); - - it('invokes mutate when updating the domain object', function () { - testDataManager.updateDomainObject(); - expect(mockOpenMCT.objects.mutate).toHaveBeenCalled(); + expect(testDataManager.config[1].value).toEqual(26); + expect(mockEvaluator.setTestDataCache).toHaveBeenCalledWith({ + object1: { + property1: 26, + property2: '' + }, + object2: { + property3: '', + property4: 'Text It Is' + } }); + }); - it('builds item view from item configuration', function () { - mockContainer.append(testDataManager.getDOM()); - expect(mockContainer.querySelectorAll('.t-test-data-item').length).toEqual(3); - }); + it('allows initializing a new item with a default configuration', function () { + testDataManager.initItem(); + expect(mockDomainObject.configuration.testDataConfig).toEqual([ + { + object: '', + key: '', + value: '' + }, + { + object: 'object1', + key: 'property1', + value: 66 + }, + { + object: 'object2', + key: 'property4', + value: 'Text It Is' + }, + { + object: '', + key: '', + value: '' + } + ]); + }); - it('can remove a item from its configuration', function () { - testDataManager.removeItem(0); - expect(mockDomainObject.configuration.testDataConfig).toEqual([ - { - object: 'object1', - key: 'property1', - value: 66 - }, - { - object: 'object2', - key: 'property4', - value: 'Text It Is' - } - ]); + it('allows initializing a new item from a given configuration', function () { + testDataManager.initItem({ + sourceItem: { + object: 'object2', + key: 'property3', + value: 1 + }, + index: 0 }); + expect(mockDomainObject.configuration.testDataConfig).toEqual([ + { + object: '', + key: '', + value: '' + }, + { + object: 'object2', + key: 'property3', + value: 1 + }, + { + object: 'object1', + key: 'property1', + value: 66 + }, + { + object: 'object2', + key: 'property4', + value: 'Text It Is' + } + ]); + }); + + it('invokes mutate when updating the domain object', function () { + testDataManager.updateDomainObject(); + expect(mockOpenMCT.objects.mutate).toHaveBeenCalled(); + }); - it('exposes a UI element to toggle test data on and off', function () {}); + it('builds item view from item configuration', function () { + mockContainer.append(testDataManager.getDOM()); + expect(mockContainer.querySelectorAll('.t-test-data-item').length).toEqual(3); }); + + it('can remove a item from its configuration', function () { + testDataManager.removeItem(0); + expect(mockDomainObject.configuration.testDataConfig).toEqual([ + { + object: 'object1', + key: 'property1', + value: 66 + }, + { + object: 'object2', + key: 'property4', + value: 'Text It Is' + } + ]); + }); + + it('exposes a UI element to toggle test data on and off', function () {}); }); diff --git a/src/plugins/summaryWidget/test/input/ColorPaletteSpec.js b/src/plugins/summaryWidget/test/input/ColorPaletteSpec.js index 0470c0f0f3b..80f157d039d 100644 --- a/src/plugins/summaryWidget/test/input/ColorPaletteSpec.js +++ b/src/plugins/summaryWidget/test/input/ColorPaletteSpec.js @@ -1,24 +1,24 @@ -define(['../../src/input/ColorPalette'], function (ColorPalette) { - describe('An Open MCT color palette', function () { - let colorPalette; - let changeCallback; +import ColorPalette from '../../src/input/ColorPalette'; - beforeEach(function () { - changeCallback = jasmine.createSpy('changeCallback'); - }); +describe('An Open MCT color palette', function () { + let colorPalette; + let changeCallback; - it('allows defining a custom color set', function () { - colorPalette = new ColorPalette('someClass', 'someContainer', ['color1', 'color2', 'color3']); - expect(colorPalette.getCurrent()).toEqual('color1'); - colorPalette.on('change', changeCallback); - colorPalette.set('color2'); - expect(colorPalette.getCurrent()).toEqual('color2'); - expect(changeCallback).toHaveBeenCalledWith('color2'); - }); + beforeEach(function () { + changeCallback = jasmine.createSpy('changeCallback'); + }); + + it('allows defining a custom color set', function () { + colorPalette = new ColorPalette('someClass', 'someContainer', ['color1', 'color2', 'color3']); + expect(colorPalette.getCurrent()).toEqual('color1'); + colorPalette.on('change', changeCallback); + colorPalette.set('color2'); + expect(colorPalette.getCurrent()).toEqual('color2'); + expect(changeCallback).toHaveBeenCalledWith('color2'); + }); - it('loads with a default color set if one is not provided', function () { - colorPalette = new ColorPalette('someClass', 'someContainer'); - expect(colorPalette.getCurrent()).toBeDefined(); - }); + it('loads with a default color set if one is not provided', function () { + colorPalette = new ColorPalette('someClass', 'someContainer'); + expect(colorPalette.getCurrent()).toBeDefined(); }); }); diff --git a/src/plugins/summaryWidget/test/input/IconPaletteSpec.js b/src/plugins/summaryWidget/test/input/IconPaletteSpec.js index 3a9128c17d8..0bdb045b49b 100644 --- a/src/plugins/summaryWidget/test/input/IconPaletteSpec.js +++ b/src/plugins/summaryWidget/test/input/IconPaletteSpec.js @@ -1,24 +1,24 @@ -define(['../../src/input/IconPalette'], function (IconPalette) { - describe('An Open MCT icon palette', function () { - let iconPalette; - let changeCallback; +import IconPalette from '../../src/input/IconPalette'; - beforeEach(function () { - changeCallback = jasmine.createSpy('changeCallback'); - }); +describe('An Open MCT icon palette', function () { + let iconPalette; + let changeCallback; - it('allows defining a custom icon set', function () { - iconPalette = new IconPalette('', 'someContainer', ['icon1', 'icon2', 'icon3']); - expect(iconPalette.getCurrent()).toEqual('icon1'); - iconPalette.on('change', changeCallback); - iconPalette.set('icon2'); - expect(iconPalette.getCurrent()).toEqual('icon2'); - expect(changeCallback).toHaveBeenCalledWith('icon2'); - }); + beforeEach(function () { + changeCallback = jasmine.createSpy('changeCallback'); + }); + + it('allows defining a custom icon set', function () { + iconPalette = new IconPalette('', 'someContainer', ['icon1', 'icon2', 'icon3']); + expect(iconPalette.getCurrent()).toEqual('icon1'); + iconPalette.on('change', changeCallback); + iconPalette.set('icon2'); + expect(iconPalette.getCurrent()).toEqual('icon2'); + expect(changeCallback).toHaveBeenCalledWith('icon2'); + }); - it('loads with a default icon set if one is not provided', function () { - iconPalette = new IconPalette('someClass', 'someContainer'); - expect(iconPalette.getCurrent()).toBeDefined(); - }); + it('loads with a default icon set if one is not provided', function () { + iconPalette = new IconPalette('someClass', 'someContainer'); + expect(iconPalette.getCurrent()).toBeDefined(); }); }); diff --git a/src/plugins/summaryWidget/test/input/KeySelectSpec.js b/src/plugins/summaryWidget/test/input/KeySelectSpec.js index d5c22ce3b72..f6e992b02ff 100644 --- a/src/plugins/summaryWidget/test/input/KeySelectSpec.js +++ b/src/plugins/summaryWidget/test/input/KeySelectSpec.js @@ -1,123 +1,123 @@ -define(['../../src/input/KeySelect'], function (KeySelect) { - describe('A select for choosing composition object properties', function () { - let mockConfig; - let mockBadConfig; - let mockManager; - let keySelect; - let mockMetadata; - let mockObjectSelect; - beforeEach(function () { - mockConfig = { - object: 'object1', - key: 'a' - }; - - mockBadConfig = { - object: 'object1', - key: 'someNonexistentKey' - }; - - mockMetadata = { - object1: { - a: { - name: 'A' - }, - b: { - name: 'B' - } +import KeySelect from '../../src/input/KeySelect'; + +describe('A select for choosing composition object properties', function () { + let mockConfig; + let mockBadConfig; + let mockManager; + let keySelect; + let mockMetadata; + let mockObjectSelect; + beforeEach(function () { + mockConfig = { + object: 'object1', + key: 'a' + }; + + mockBadConfig = { + object: 'object1', + key: 'someNonexistentKey' + }; + + mockMetadata = { + object1: { + a: { + name: 'A' }, - object2: { - alpha: { - name: 'Alpha' - }, - beta: { - name: 'Beta' - } + b: { + name: 'B' + } + }, + object2: { + alpha: { + name: 'Alpha' }, - object3: { - a: { - name: 'A' - } + beta: { + name: 'Beta' } - }; - - mockManager = jasmine.createSpyObj('mockManager', [ - 'on', - 'metadataLoadCompleted', - 'triggerCallback', - 'getTelemetryMetadata' - ]); - - mockObjectSelect = jasmine.createSpyObj('mockObjectSelect', ['on', 'triggerCallback']); - - mockObjectSelect.on.and.callFake((event, callback) => { - mockObjectSelect.callbacks = mockObjectSelect.callbacks || {}; - mockObjectSelect.callbacks[event] = callback; - }); - - mockObjectSelect.triggerCallback.and.callFake((event, key) => { - mockObjectSelect.callbacks[event](key); - }); - - mockManager.on.and.callFake((event, callback) => { - mockManager.callbacks = mockManager.callbacks || {}; - mockManager.callbacks[event] = callback; - }); - - mockManager.triggerCallback.and.callFake((event) => { - mockManager.callbacks[event](); - }); - - mockManager.getTelemetryMetadata.and.callFake(function (key) { - return mockMetadata[key]; - }); - }); + }, + object3: { + a: { + name: 'A' + } + } + }; - it('waits until the metadata fully loads to populate itself', function () { - mockManager.metadataLoadCompleted.and.returnValue(false); - keySelect = new KeySelect(mockConfig, mockObjectSelect, mockManager); - expect(keySelect.getSelected()).toEqual(''); - }); + mockManager = jasmine.createSpyObj('mockManager', [ + 'on', + 'metadataLoadCompleted', + 'triggerCallback', + 'getTelemetryMetadata' + ]); - it('populates itself with metadata on a metadata load', function () { - mockManager.metadataLoadCompleted.and.returnValue(false); - keySelect = new KeySelect(mockConfig, mockObjectSelect, mockManager); - mockManager.triggerCallback('metadata'); - expect(keySelect.getSelected()).toEqual('a'); - }); + mockObjectSelect = jasmine.createSpyObj('mockObjectSelect', ['on', 'triggerCallback']); - it('populates itself with metadata if metadata load is already complete', function () { - mockManager.metadataLoadCompleted.and.returnValue(true); - keySelect = new KeySelect(mockConfig, mockObjectSelect, mockManager); - expect(keySelect.getSelected()).toEqual('a'); + mockObjectSelect.on.and.callFake((event, callback) => { + mockObjectSelect.callbacks = mockObjectSelect.callbacks || {}; + mockObjectSelect.callbacks[event] = callback; }); - it('clears its selection state if the property in its config is not in its object', function () { - mockManager.metadataLoadCompleted.and.returnValue(true); - keySelect = new KeySelect(mockBadConfig, mockObjectSelect, mockManager); - expect(keySelect.getSelected()).toEqual(''); + mockObjectSelect.triggerCallback.and.callFake((event, key) => { + mockObjectSelect.callbacks[event](key); }); - it('populates with the appropriate options when its linked object changes', function () { - mockManager.metadataLoadCompleted.and.returnValue(true); - keySelect = new KeySelect(mockConfig, mockObjectSelect, mockManager); - mockObjectSelect.triggerCallback('change', 'object2'); - keySelect.setSelected('alpha'); - expect(keySelect.getSelected()).toEqual('alpha'); + mockManager.on.and.callFake((event, callback) => { + mockManager.callbacks = mockManager.callbacks || {}; + mockManager.callbacks[event] = callback; }); - it('clears its selected state on change if the field is not present in the new object', function () { - mockManager.metadataLoadCompleted.and.returnValue(true); - keySelect = new KeySelect(mockConfig, mockObjectSelect, mockManager); - mockObjectSelect.triggerCallback('change', 'object2'); - expect(keySelect.getSelected()).toEqual(''); + mockManager.triggerCallback.and.callFake((event) => { + mockManager.callbacks[event](); }); - it('maintains its selected state on change if field is present in new object', function () { - mockManager.metadataLoadCompleted.and.returnValue(true); - keySelect = new KeySelect(mockConfig, mockObjectSelect, mockManager); - mockObjectSelect.triggerCallback('change', 'object3'); - expect(keySelect.getSelected()).toEqual('a'); + mockManager.getTelemetryMetadata.and.callFake(function (key) { + return mockMetadata[key]; }); }); + + it('waits until the metadata fully loads to populate itself', function () { + mockManager.metadataLoadCompleted.and.returnValue(false); + keySelect = new KeySelect(mockConfig, mockObjectSelect, mockManager); + expect(keySelect.getSelected()).toEqual(''); + }); + + it('populates itself with metadata on a metadata load', function () { + mockManager.metadataLoadCompleted.and.returnValue(false); + keySelect = new KeySelect(mockConfig, mockObjectSelect, mockManager); + mockManager.triggerCallback('metadata'); + expect(keySelect.getSelected()).toEqual('a'); + }); + + it('populates itself with metadata if metadata load is already complete', function () { + mockManager.metadataLoadCompleted.and.returnValue(true); + keySelect = new KeySelect(mockConfig, mockObjectSelect, mockManager); + expect(keySelect.getSelected()).toEqual('a'); + }); + + it('clears its selection state if the property in its config is not in its object', function () { + mockManager.metadataLoadCompleted.and.returnValue(true); + keySelect = new KeySelect(mockBadConfig, mockObjectSelect, mockManager); + expect(keySelect.getSelected()).toEqual(''); + }); + + it('populates with the appropriate options when its linked object changes', function () { + mockManager.metadataLoadCompleted.and.returnValue(true); + keySelect = new KeySelect(mockConfig, mockObjectSelect, mockManager); + mockObjectSelect.triggerCallback('change', 'object2'); + keySelect.setSelected('alpha'); + expect(keySelect.getSelected()).toEqual('alpha'); + }); + + it('clears its selected state on change if the field is not present in the new object', function () { + mockManager.metadataLoadCompleted.and.returnValue(true); + keySelect = new KeySelect(mockConfig, mockObjectSelect, mockManager); + mockObjectSelect.triggerCallback('change', 'object2'); + expect(keySelect.getSelected()).toEqual(''); + }); + + it('maintains its selected state on change if field is present in new object', function () { + mockManager.metadataLoadCompleted.and.returnValue(true); + keySelect = new KeySelect(mockConfig, mockObjectSelect, mockManager); + mockObjectSelect.triggerCallback('change', 'object3'); + expect(keySelect.getSelected()).toEqual('a'); + }); }); diff --git a/src/plugins/summaryWidget/test/input/ObjectSelectSpec.js b/src/plugins/summaryWidget/test/input/ObjectSelectSpec.js index 57173313767..3e4b296db1d 100644 --- a/src/plugins/summaryWidget/test/input/ObjectSelectSpec.js +++ b/src/plugins/summaryWidget/test/input/ObjectSelectSpec.js @@ -1,112 +1,134 @@ -define(['../../src/input/ObjectSelect'], function (ObjectSelect) { - describe('A select for choosing composition objects', function () { - let mockConfig; - let mockBadConfig; - let mockManager; - let objectSelect; - let mockComposition; - beforeEach(function () { - mockConfig = { - object: 'key1' - }; +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2023, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT is licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * Open MCT includes source code licensed under additional open source + * licenses. See the Open Source Licenses file (LICENSES.md) included with + * this source code distribution or the Licensing information page available + * at runtime from the About dialog for additional information. + *****************************************************************************/ - mockBadConfig = { - object: 'someNonexistentObject' - }; +import ObjectSelect from '../../src/input/ObjectSelect'; - mockComposition = { - key1: { - identifier: { - key: 'key1' - }, - name: 'Object 1' - }, - key2: { - identifier: { - key: 'key2' - }, - name: 'Object 2' - } - }; - mockManager = jasmine.createSpyObj('mockManager', [ - 'on', - 'loadCompleted', - 'triggerCallback', - 'getComposition' - ]); +describe('A select for choosing composition objects', function () { + let mockConfig; + let mockBadConfig; + let mockManager; + let objectSelect; + let mockComposition; + beforeEach(function () { + mockConfig = { + object: 'key1' + }; - mockManager.on.and.callFake((event, callback) => { - mockManager.callbacks = mockManager.callbacks || {}; - mockManager.callbacks[event] = callback; - }); + mockBadConfig = { + object: 'someNonexistentObject' + }; - mockManager.triggerCallback.and.callFake((event, newObj) => { - if (event === 'add') { - mockManager.callbacks.add(newObj); - } else { - mockManager.callbacks[event](); - } - }); + mockComposition = { + key1: { + identifier: { + key: 'key1' + }, + name: 'Object 1' + }, + key2: { + identifier: { + key: 'key2' + }, + name: 'Object 2' + } + }; + mockManager = jasmine.createSpyObj('mockManager', [ + 'on', + 'loadCompleted', + 'triggerCallback', + 'getComposition' + ]); - mockManager.getComposition.and.callFake(function () { - return mockComposition; - }); + mockManager.on.and.callFake((event, callback) => { + mockManager.callbacks = mockManager.callbacks || {}; + mockManager.callbacks[event] = callback; }); - it('allows setting special keyword options', function () { - mockManager.loadCompleted.and.returnValue(true); - objectSelect = new ObjectSelect(mockConfig, mockManager, [ - ['keyword1', 'A special option'], - ['keyword2', 'A special option'] - ]); - objectSelect.setSelected('keyword1'); - expect(objectSelect.getSelected()).toEqual('keyword1'); + mockManager.triggerCallback.and.callFake((event, newObj) => { + if (event === 'add') { + mockManager.callbacks.add(newObj); + } else { + mockManager.callbacks[event](); + } }); - it('waits until the composition fully loads to populate itself', function () { - mockManager.loadCompleted.and.returnValue(false); - objectSelect = new ObjectSelect(mockConfig, mockManager); - expect(objectSelect.getSelected()).toEqual(''); + mockManager.getComposition.and.callFake(function () { + return mockComposition; }); + }); - it('populates itself with composition objects on a composition load', function () { - mockManager.loadCompleted.and.returnValue(false); - objectSelect = new ObjectSelect(mockConfig, mockManager); - mockManager.triggerCallback('load'); - expect(objectSelect.getSelected()).toEqual('key1'); - }); + it('allows setting special keyword options', function () { + mockManager.loadCompleted.and.returnValue(true); + objectSelect = new ObjectSelect(mockConfig, mockManager, [ + ['keyword1', 'A special option'], + ['keyword2', 'A special option'] + ]); + objectSelect.setSelected('keyword1'); + expect(objectSelect.getSelected()).toEqual('keyword1'); + }); - it('populates itself with composition objects if load is already complete', function () { - mockManager.loadCompleted.and.returnValue(true); - objectSelect = new ObjectSelect(mockConfig, mockManager); - expect(objectSelect.getSelected()).toEqual('key1'); - }); + it('waits until the composition fully loads to populate itself', function () { + mockManager.loadCompleted.and.returnValue(false); + objectSelect = new ObjectSelect(mockConfig, mockManager); + expect(objectSelect.getSelected()).toEqual(''); + }); - it('clears its selection state if the object in its config is not in the composition', function () { - mockManager.loadCompleted.and.returnValue(true); - objectSelect = new ObjectSelect(mockBadConfig, mockManager); - expect(objectSelect.getSelected()).toEqual(''); - }); + it('populates itself with composition objects on a composition load', function () { + mockManager.loadCompleted.and.returnValue(false); + objectSelect = new ObjectSelect(mockConfig, mockManager); + mockManager.triggerCallback('load'); + expect(objectSelect.getSelected()).toEqual('key1'); + }); - it('adds a new option on a composition add', function () { - mockManager.loadCompleted.and.returnValue(true); - objectSelect = new ObjectSelect(mockConfig, mockManager); - mockManager.triggerCallback('add', { - identifier: { - key: 'key3' - }, - name: 'Object 3' - }); - objectSelect.setSelected('key3'); - expect(objectSelect.getSelected()).toEqual('key3'); - }); + it('populates itself with composition objects if load is already complete', function () { + mockManager.loadCompleted.and.returnValue(true); + objectSelect = new ObjectSelect(mockConfig, mockManager); + expect(objectSelect.getSelected()).toEqual('key1'); + }); + + it('clears its selection state if the object in its config is not in the composition', function () { + mockManager.loadCompleted.and.returnValue(true); + objectSelect = new ObjectSelect(mockBadConfig, mockManager); + expect(objectSelect.getSelected()).toEqual(''); + }); - it('removes an option on a composition remove', function () { - mockManager.loadCompleted.and.returnValue(true); - objectSelect = new ObjectSelect(mockConfig, mockManager); - delete mockComposition.key1; - mockManager.triggerCallback('remove'); - expect(objectSelect.getSelected()).not.toEqual('key1'); + it('adds a new option on a composition add', function () { + mockManager.loadCompleted.and.returnValue(true); + objectSelect = new ObjectSelect(mockConfig, mockManager); + mockManager.triggerCallback('add', { + identifier: { + key: 'key3' + }, + name: 'Object 3' }); + objectSelect.setSelected('key3'); + expect(objectSelect.getSelected()).toEqual('key3'); + }); + + it('removes an option on a composition remove', function () { + mockManager.loadCompleted.and.returnValue(true); + objectSelect = new ObjectSelect(mockConfig, mockManager); + delete mockComposition.key1; + mockManager.triggerCallback('remove'); + expect(objectSelect.getSelected()).not.toEqual('key1'); }); }); diff --git a/src/plugins/summaryWidget/test/input/OperationSelectSpec.js b/src/plugins/summaryWidget/test/input/OperationSelectSpec.js index c57bc36578e..1a6ff1e74ad 100644 --- a/src/plugins/summaryWidget/test/input/OperationSelectSpec.js +++ b/src/plugins/summaryWidget/test/input/OperationSelectSpec.js @@ -1,143 +1,165 @@ -define(['../../src/input/OperationSelect'], function (OperationSelect) { - describe('A select for choosing composition object properties', function () { - let mockConfig; - let mockBadConfig; - let mockManager; - let operationSelect; - let mockOperations; - let mockPropertyTypes; - let mockKeySelect; - let mockEvaluator; - beforeEach(function () { - mockConfig = { - object: 'object1', - key: 'a', - operation: 'operation1' - }; - - mockBadConfig = { - object: 'object1', - key: 'a', - operation: 'someNonexistentOperation' - }; - - mockOperations = { - operation1: { - text: 'An operation', - appliesTo: ['number'] - }, - operation2: { - text: 'Another operation', - appliesTo: ['string'] - } - }; - - mockPropertyTypes = { - object1: { - a: 'number', - b: 'string', - c: 'number' - } - }; - - mockManager = jasmine.createSpyObj('mockManager', [ - 'on', - 'metadataLoadCompleted', - 'triggerCallback', - 'getTelemetryPropertyType', - 'getEvaluator' - ]); - - mockKeySelect = jasmine.createSpyObj('mockKeySelect', ['on', 'triggerCallback']); - - mockEvaluator = jasmine.createSpyObj('mockEvaluator', [ - 'getOperationKeys', - 'operationAppliesTo', - 'getOperationText' - ]); - - mockEvaluator.getOperationKeys.and.returnValue(Object.keys(mockOperations)); - - mockEvaluator.getOperationText.and.callFake(function (key) { - return mockOperations[key].text; - }); - - mockEvaluator.operationAppliesTo.and.callFake(function (operation, type) { - return mockOperations[operation].appliesTo.includes(type); - }); - - mockKeySelect.on.and.callFake((event, callback) => { - mockKeySelect.callbacks = mockKeySelect.callbacks || {}; - mockKeySelect.callbacks[event] = callback; - }); - - mockKeySelect.triggerCallback.and.callFake((event, key) => { - mockKeySelect.callbacks[event](key); - }); - - mockManager.on.and.callFake((event, callback) => { - mockManager.callbacks = mockManager.callbacks || {}; - mockManager.callbacks[event] = callback; - }); - - mockManager.triggerCallback.and.callFake((event) => { - mockManager.callbacks[event](); - }); - - mockManager.getTelemetryPropertyType.and.callFake(function (object, key) { - return mockPropertyTypes[object][key]; - }); - - mockManager.getEvaluator.and.returnValue(mockEvaluator); +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2023, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT is licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * Open MCT includes source code licensed under additional open source + * licenses. See the Open Source Licenses file (LICENSES.md) included with + * this source code distribution or the Licensing information page available + * at runtime from the About dialog for additional information. + *****************************************************************************/ + +import OperationSelect from '../../src/input/OperationSelect'; + +describe('A select for choosing composition object properties', function () { + let mockConfig; + let mockBadConfig; + let mockManager; + let operationSelect; + let mockOperations; + let mockPropertyTypes; + let mockKeySelect; + let mockEvaluator; + beforeEach(function () { + mockConfig = { + object: 'object1', + key: 'a', + operation: 'operation1' + }; + + mockBadConfig = { + object: 'object1', + key: 'a', + operation: 'someNonexistentOperation' + }; + + mockOperations = { + operation1: { + text: 'An operation', + appliesTo: ['number'] + }, + operation2: { + text: 'Another operation', + appliesTo: ['string'] + } + }; + + mockPropertyTypes = { + object1: { + a: 'number', + b: 'string', + c: 'number' + } + }; + + mockManager = jasmine.createSpyObj('mockManager', [ + 'on', + 'metadataLoadCompleted', + 'triggerCallback', + 'getTelemetryPropertyType', + 'getEvaluator' + ]); + + mockKeySelect = jasmine.createSpyObj('mockKeySelect', ['on', 'triggerCallback']); + + mockEvaluator = jasmine.createSpyObj('mockEvaluator', [ + 'getOperationKeys', + 'operationAppliesTo', + 'getOperationText' + ]); + + mockEvaluator.getOperationKeys.and.returnValue(Object.keys(mockOperations)); + + mockEvaluator.getOperationText.and.callFake(function (key) { + return mockOperations[key].text; }); - it('waits until the metadata fully loads to populate itself', function () { - mockManager.metadataLoadCompleted.and.returnValue(false); - operationSelect = new OperationSelect(mockConfig, mockKeySelect, mockManager); - expect(operationSelect.getSelected()).toEqual(''); + mockEvaluator.operationAppliesTo.and.callFake(function (operation, type) { + return mockOperations[operation].appliesTo.includes(type); }); - it('populates itself with operations on a metadata load', function () { - mockManager.metadataLoadCompleted.and.returnValue(false); - operationSelect = new OperationSelect(mockConfig, mockKeySelect, mockManager); - mockManager.triggerCallback('metadata'); - expect(operationSelect.getSelected()).toEqual('operation1'); + mockKeySelect.on.and.callFake((event, callback) => { + mockKeySelect.callbacks = mockKeySelect.callbacks || {}; + mockKeySelect.callbacks[event] = callback; }); - it('populates itself with operations if metadata load is already complete', function () { - mockManager.metadataLoadCompleted.and.returnValue(true); - operationSelect = new OperationSelect(mockConfig, mockKeySelect, mockManager); - expect(operationSelect.getSelected()).toEqual('operation1'); + mockKeySelect.triggerCallback.and.callFake((event, key) => { + mockKeySelect.callbacks[event](key); }); - it('clears its selection state if the operation in its config does not apply', function () { - mockManager.metadataLoadCompleted.and.returnValue(true); - operationSelect = new OperationSelect(mockBadConfig, mockKeySelect, mockManager); - expect(operationSelect.getSelected()).toEqual(''); + mockManager.on.and.callFake((event, callback) => { + mockManager.callbacks = mockManager.callbacks || {}; + mockManager.callbacks[event] = callback; }); - it('populates with the appropriate options when its linked key changes', function () { - mockManager.metadataLoadCompleted.and.returnValue(true); - operationSelect = new OperationSelect(mockConfig, mockKeySelect, mockManager); - mockKeySelect.triggerCallback('change', 'b'); - operationSelect.setSelected('operation2'); - expect(operationSelect.getSelected()).toEqual('operation2'); - operationSelect.setSelected('operation1'); - expect(operationSelect.getSelected()).not.toEqual('operation1'); + mockManager.triggerCallback.and.callFake((event) => { + mockManager.callbacks[event](); }); - it('clears its selection on a change if the operation does not apply', function () { - mockManager.metadataLoadCompleted.and.returnValue(true); - operationSelect = new OperationSelect(mockConfig, mockKeySelect, mockManager); - mockKeySelect.triggerCallback('change', 'b'); - expect(operationSelect.getSelected()).toEqual(''); + mockManager.getTelemetryPropertyType.and.callFake(function (object, key) { + return mockPropertyTypes[object][key]; }); - it('maintains its selected state on change if the operation does apply', function () { - mockManager.metadataLoadCompleted.and.returnValue(true); - operationSelect = new OperationSelect(mockConfig, mockKeySelect, mockManager); - mockKeySelect.triggerCallback('change', 'c'); - expect(operationSelect.getSelected()).toEqual('operation1'); - }); + mockManager.getEvaluator.and.returnValue(mockEvaluator); + }); + + it('waits until the metadata fully loads to populate itself', function () { + mockManager.metadataLoadCompleted.and.returnValue(false); + operationSelect = new OperationSelect(mockConfig, mockKeySelect, mockManager); + expect(operationSelect.getSelected()).toEqual(''); + }); + + it('populates itself with operations on a metadata load', function () { + mockManager.metadataLoadCompleted.and.returnValue(false); + operationSelect = new OperationSelect(mockConfig, mockKeySelect, mockManager); + mockManager.triggerCallback('metadata'); + expect(operationSelect.getSelected()).toEqual('operation1'); + }); + + it('populates itself with operations if metadata load is already complete', function () { + mockManager.metadataLoadCompleted.and.returnValue(true); + operationSelect = new OperationSelect(mockConfig, mockKeySelect, mockManager); + expect(operationSelect.getSelected()).toEqual('operation1'); + }); + + it('clears its selection state if the operation in its config does not apply', function () { + mockManager.metadataLoadCompleted.and.returnValue(true); + operationSelect = new OperationSelect(mockBadConfig, mockKeySelect, mockManager); + expect(operationSelect.getSelected()).toEqual(''); + }); + + it('populates with the appropriate options when its linked key changes', function () { + mockManager.metadataLoadCompleted.and.returnValue(true); + operationSelect = new OperationSelect(mockConfig, mockKeySelect, mockManager); + mockKeySelect.triggerCallback('change', 'b'); + operationSelect.setSelected('operation2'); + expect(operationSelect.getSelected()).toEqual('operation2'); + operationSelect.setSelected('operation1'); + expect(operationSelect.getSelected()).not.toEqual('operation1'); + }); + + it('clears its selection on a change if the operation does not apply', function () { + mockManager.metadataLoadCompleted.and.returnValue(true); + operationSelect = new OperationSelect(mockConfig, mockKeySelect, mockManager); + mockKeySelect.triggerCallback('change', 'b'); + expect(operationSelect.getSelected()).toEqual(''); + }); + + it('maintains its selected state on change if the operation does apply', function () { + mockManager.metadataLoadCompleted.and.returnValue(true); + operationSelect = new OperationSelect(mockConfig, mockKeySelect, mockManager); + mockKeySelect.triggerCallback('change', 'c'); + expect(operationSelect.getSelected()).toEqual('operation1'); }); }); diff --git a/src/plugins/summaryWidget/test/input/PaletteSpec.js b/src/plugins/summaryWidget/test/input/PaletteSpec.js index 67678cb0495..3b7983eb99a 100644 --- a/src/plugins/summaryWidget/test/input/PaletteSpec.js +++ b/src/plugins/summaryWidget/test/input/PaletteSpec.js @@ -1,44 +1,44 @@ -define(['../../src/input/Palette'], function (Palette) { - describe('A generic Open MCT palette input', function () { - let palette; - let callbackSpy1; - let callbackSpy2; +import Palette from '../../src/input/Palette'; - beforeEach(function () { - palette = new Palette('someClass', 'someContainer', ['item1', 'item2', 'item3']); - callbackSpy1 = jasmine.createSpy('changeCallback1'); - callbackSpy2 = jasmine.createSpy('changeCallback2'); - }); +describe('A generic Open MCT palette input', function () { + let palette; + let callbackSpy1; + let callbackSpy2; - it('gets the current item', function () { - expect(palette.getCurrent()).toEqual('item1'); - }); + beforeEach(function () { + palette = new Palette('someClass', 'someContainer', ['item1', 'item2', 'item3']); + callbackSpy1 = jasmine.createSpy('changeCallback1'); + callbackSpy2 = jasmine.createSpy('changeCallback2'); + }); - it('allows setting the current item', function () { - palette.set('item2'); - expect(palette.getCurrent()).toEqual('item2'); - }); + it('gets the current item', function () { + expect(palette.getCurrent()).toEqual('item1'); + }); - it('allows registering change callbacks, and errors when an unsupported event is registered', function () { - expect(function () { - palette.on('change', callbackSpy1); - }).not.toThrow(); - expect(function () { - palette.on('someUnsupportedEvent', callbackSpy1); - }).toThrow(); - }); + it('allows setting the current item', function () { + palette.set('item2'); + expect(palette.getCurrent()).toEqual('item2'); + }); - it('injects its callbacks with the new selected item on change', function () { + it('allows registering change callbacks, and errors when an unsupported event is registered', function () { + expect(function () { palette.on('change', callbackSpy1); - palette.on('change', callbackSpy2); - palette.set('item2'); - expect(callbackSpy1).toHaveBeenCalledWith('item2'); - expect(callbackSpy2).toHaveBeenCalledWith('item2'); - }); + }).not.toThrow(); + expect(function () { + palette.on('someUnsupportedEvent', callbackSpy1); + }).toThrow(); + }); + + it('injects its callbacks with the new selected item on change', function () { + palette.on('change', callbackSpy1); + palette.on('change', callbackSpy2); + palette.set('item2'); + expect(callbackSpy1).toHaveBeenCalledWith('item2'); + expect(callbackSpy2).toHaveBeenCalledWith('item2'); + }); - it('gracefully handles being set to an item not included in its set', function () { - palette.set('foobar'); - expect(palette.getCurrent()).not.toEqual('foobar'); - }); + it('gracefully handles being set to an item not included in its set', function () { + palette.set('foobar'); + expect(palette.getCurrent()).not.toEqual('foobar'); }); }); diff --git a/src/plugins/summaryWidget/test/input/SelectSpec.js b/src/plugins/summaryWidget/test/input/SelectSpec.js index bdd0dac774a..cfa13f454c3 100644 --- a/src/plugins/summaryWidget/test/input/SelectSpec.js +++ b/src/plugins/summaryWidget/test/input/SelectSpec.js @@ -1,61 +1,61 @@ -define(['../../src/input/Select'], function (Select) { - describe('A select wrapper', function () { - let select; - let testOptions; - let callbackSpy1; - let callbackSpy2; - beforeEach(function () { - select = new Select(); - testOptions = [ - ['item1', 'Item 1'], - ['item2', 'Item 2'], - ['item3', 'Item 3'] - ]; - select.setOptions(testOptions); - callbackSpy1 = jasmine.createSpy('callbackSpy1'); - callbackSpy2 = jasmine.createSpy('callbackSpy2'); - }); +import Select from '../../src/input/Select'; - it('gets and sets the current item', function () { - select.setSelected('item1'); - expect(select.getSelected()).toEqual('item1'); - }); +describe('A select wrapper', function () { + let select; + let testOptions; + let callbackSpy1; + let callbackSpy2; + beforeEach(function () { + select = new Select(); + testOptions = [ + ['item1', 'Item 1'], + ['item2', 'Item 2'], + ['item3', 'Item 3'] + ]; + select.setOptions(testOptions); + callbackSpy1 = jasmine.createSpy('callbackSpy1'); + callbackSpy2 = jasmine.createSpy('callbackSpy2'); + }); - it('allows adding a single new option', function () { - select.addOption('newOption', 'A New Option'); - select.setSelected('newOption'); - expect(select.getSelected()).toEqual('newOption'); - }); + it('gets and sets the current item', function () { + select.setSelected('item1'); + expect(select.getSelected()).toEqual('item1'); + }); - it('allows populating with a new set of options', function () { - select.setOptions([ - ['newItem1', 'Item 1'], - ['newItem2', 'Item 2'] - ]); - select.setSelected('newItem1'); - expect(select.getSelected()).toEqual('newItem1'); - }); + it('allows adding a single new option', function () { + select.addOption('newOption', 'A New Option'); + select.setSelected('newOption'); + expect(select.getSelected()).toEqual('newOption'); + }); - it('allows registering change callbacks, and errors when an unsupported event is registered', function () { - expect(function () { - select.on('change', callbackSpy1); - }).not.toThrow(); - expect(function () { - select.on('someUnsupportedEvent', callbackSpy1); - }).toThrow(); - }); + it('allows populating with a new set of options', function () { + select.setOptions([ + ['newItem1', 'Item 1'], + ['newItem2', 'Item 2'] + ]); + select.setSelected('newItem1'); + expect(select.getSelected()).toEqual('newItem1'); + }); - it('injects its callbacks with its property and value on a change', function () { + it('allows registering change callbacks, and errors when an unsupported event is registered', function () { + expect(function () { select.on('change', callbackSpy1); - select.on('change', callbackSpy2); - select.setSelected('item2'); - expect(callbackSpy1).toHaveBeenCalledWith('item2'); - expect(callbackSpy2).toHaveBeenCalledWith('item2'); - }); + }).not.toThrow(); + expect(function () { + select.on('someUnsupportedEvent', callbackSpy1); + }).toThrow(); + }); + + it('injects its callbacks with its property and value on a change', function () { + select.on('change', callbackSpy1); + select.on('change', callbackSpy2); + select.setSelected('item2'); + expect(callbackSpy1).toHaveBeenCalledWith('item2'); + expect(callbackSpy2).toHaveBeenCalledWith('item2'); + }); - it('gracefully handles being set to an item not included in its set', function () { - select.setSelected('foobar'); - expect(select.getSelected()).not.toEqual('foobar'); - }); + it('gracefully handles being set to an item not included in its set', function () { + select.setSelected('foobar'); + expect(select.getSelected()).not.toEqual('foobar'); }); }); diff --git a/src/plugins/tabs/plugin.js b/src/plugins/tabs/plugin.js index e62ba670c1e..c7e116d921f 100644 --- a/src/plugins/tabs/plugin.js +++ b/src/plugins/tabs/plugin.js @@ -19,41 +19,40 @@ * this source code distribution or the Licensing information page available * at runtime from the About dialog for additional information. *****************************************************************************/ +import Tabs from './tabs'; -define(['./tabs'], function (Tabs) { - return function plugin() { - return function install(openmct) { - openmct.objectViews.addProvider(new Tabs.default(openmct)); +export default function plugin() { + return function install(openmct) { + openmct.objectViews.addProvider(new Tabs(openmct)); - openmct.types.addType('tabs', { - name: 'Tabs View', - description: 'Quickly navigate between multiple objects of any type using tabs.', - creatable: true, - cssClass: 'icon-tabs-view', - initialize(domainObject) { - domainObject.composition = []; - domainObject.keep_alive = true; - }, - form: [ - { - key: 'keep_alive', - name: 'Eager Load Tabs', - control: 'select', - options: [ - { - name: 'True', - value: true - }, - { - name: 'False', - value: false - } - ], - required: true, - cssClass: 'l-input' - } - ] - }); - }; + openmct.types.addType('tabs', { + name: 'Tabs View', + description: 'Quickly navigate between multiple objects of any type using tabs.', + creatable: true, + cssClass: 'icon-tabs-view', + initialize(domainObject) { + domainObject.composition = []; + domainObject.keep_alive = true; + }, + form: [ + { + key: 'keep_alive', + name: 'Eager Load Tabs', + control: 'select', + options: [ + { + name: 'True', + value: true + }, + { + name: 'False', + value: false + } + ], + required: true, + cssClass: 'l-input' + } + ] + }); }; -}); +} diff --git a/src/plugins/telemetryMean/plugin.js b/src/plugins/telemetryMean/plugin.js index 3920d811e70..f75eddfbdd2 100755 --- a/src/plugins/telemetryMean/plugin.js +++ b/src/plugins/telemetryMean/plugin.js @@ -20,59 +20,57 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define(['./src/MeanTelemetryProvider'], function (MeanTelemetryProvider) { - const DEFAULT_SAMPLES = 10; +import MeanTelemetryProvider from './src/MeanTelemetryProvider'; - function plugin() { - return function install(openmct) { - openmct.types.addType('telemetry-mean', { - name: 'Telemetry Filter', - description: - 'Provides telemetry values that represent the mean of the last N values of a telemetry stream', - creatable: true, - cssClass: 'icon-telemetry', - initialize: function (domainObject) { - domainObject.samples = DEFAULT_SAMPLES; - domainObject.telemetry = {}; - domainObject.telemetry.values = openmct.time - .getAllTimeSystems() - .map(function (timeSystem, index) { - return { - key: timeSystem.key, - name: timeSystem.name, - hints: { - domain: index + 1 - } - }; - }); - domainObject.telemetry.values.push({ - key: 'value', - name: 'Value', - hints: { - range: 1 - } +const DEFAULT_SAMPLES = 10; + +export default function plugin() { + return function install(openmct) { + openmct.types.addType('telemetry-mean', { + name: 'Telemetry Filter', + description: + 'Provides telemetry values that represent the mean of the last N values of a telemetry stream', + creatable: true, + cssClass: 'icon-telemetry', + initialize: function (domainObject) { + domainObject.samples = DEFAULT_SAMPLES; + domainObject.telemetry = {}; + domainObject.telemetry.values = openmct.time + .getAllTimeSystems() + .map(function (timeSystem, index) { + return { + key: timeSystem.key, + name: timeSystem.name, + hints: { + domain: index + 1 + } + }; }); - }, - form: [ - { - key: 'telemetryPoint', - name: 'Telemetry Point', - control: 'textfield', - required: true, - cssClass: 'l-input-lg' - }, - { - key: 'samples', - name: 'Samples to Average', - control: 'textfield', - required: true, - cssClass: 'l-input-sm' + domainObject.telemetry.values.push({ + key: 'value', + name: 'Value', + hints: { + range: 1 } - ] - }); - openmct.telemetry.addProvider(new MeanTelemetryProvider(openmct)); - }; - } - - return plugin; -}); + }); + }, + form: [ + { + key: 'telemetryPoint', + name: 'Telemetry Point', + control: 'textfield', + required: true, + cssClass: 'l-input-lg' + }, + { + key: 'samples', + name: 'Samples to Average', + control: 'textfield', + required: true, + cssClass: 'l-input-sm' + } + ] + }); + openmct.telemetry.addProvider(new MeanTelemetryProvider(openmct)); + }; +} diff --git a/src/plugins/telemetryMean/src/MeanTelemetryProvider.js b/src/plugins/telemetryMean/src/MeanTelemetryProvider.js index 65dd435d0bc..00192da02fe 100644 --- a/src/plugins/telemetryMean/src/MeanTelemetryProvider.js +++ b/src/plugins/telemetryMean/src/MeanTelemetryProvider.js @@ -20,114 +20,114 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define(['objectUtils', './TelemetryAverager'], function (objectUtils, TelemetryAverager) { - function MeanTelemetryProvider(openmct) { - this.openmct = openmct; - this.telemetryAPI = openmct.telemetry; - this.timeAPI = openmct.time; - this.objectAPI = openmct.objects; - this.perObjectProviders = {}; - } - - MeanTelemetryProvider.prototype.canProvideTelemetry = function (domainObject) { - return domainObject.type === 'telemetry-mean'; - }; - - MeanTelemetryProvider.prototype.supportsRequest = - MeanTelemetryProvider.prototype.supportsSubscribe = - MeanTelemetryProvider.prototype.canProvideTelemetry; - - MeanTelemetryProvider.prototype.subscribe = function (domainObject, callback) { - let wrappedUnsubscribe; - let unsubscribeCalled = false; - const objectId = objectUtils.parseKeyString(domainObject.telemetryPoint); - const samples = domainObject.samples; - - this.objectAPI - .get(objectId) - .then( - function (linkedDomainObject) { - if (!unsubscribeCalled) { - wrappedUnsubscribe = this.subscribeToAverage(linkedDomainObject, samples, callback); - } - }.bind(this) - ) - .catch(logError); - - return function unsubscribe() { - unsubscribeCalled = true; - if (wrappedUnsubscribe !== undefined) { - wrappedUnsubscribe(); - } - }; - }; - - MeanTelemetryProvider.prototype.subscribeToAverage = function (domainObject, samples, callback) { - const telemetryAverager = new TelemetryAverager( - this.telemetryAPI, - this.timeAPI, - domainObject, - samples, - callback - ); - const createAverageDatum = telemetryAverager.createAverageDatum.bind(telemetryAverager); - - return this.telemetryAPI.subscribe(domainObject, createAverageDatum); - }; - - MeanTelemetryProvider.prototype.request = function (domainObject, request) { - const objectId = objectUtils.parseKeyString(domainObject.telemetryPoint); - const samples = domainObject.samples; - - return this.objectAPI.get(objectId).then( +import objectUtils from 'objectUtils'; + +import TelemetryAverager from './TelemetryAverager'; + +export default function MeanTelemetryProvider(openmct) { + this.openmct = openmct; + this.telemetryAPI = openmct.telemetry; + this.timeAPI = openmct.time; + this.objectAPI = openmct.objects; + this.perObjectProviders = {}; +} + +MeanTelemetryProvider.prototype.canProvideTelemetry = function (domainObject) { + return domainObject.type === 'telemetry-mean'; +}; + +MeanTelemetryProvider.prototype.supportsRequest = + MeanTelemetryProvider.prototype.supportsSubscribe = + MeanTelemetryProvider.prototype.canProvideTelemetry; + +MeanTelemetryProvider.prototype.subscribe = function (domainObject, callback) { + let wrappedUnsubscribe; + let unsubscribeCalled = false; + const objectId = objectUtils.parseKeyString(domainObject.telemetryPoint); + const samples = domainObject.samples; + + this.objectAPI + .get(objectId) + .then( function (linkedDomainObject) { - return this.requestAverageTelemetry(linkedDomainObject, request, samples); + if (!unsubscribeCalled) { + wrappedUnsubscribe = this.subscribeToAverage(linkedDomainObject, samples, callback); + } }.bind(this) - ); - }; + ) + .catch(logError); - /** - * @private - */ - MeanTelemetryProvider.prototype.requestAverageTelemetry = function ( - domainObject, - request, - samples - ) { - const averageData = []; - const addToAverageData = averageData.push.bind(averageData); - const telemetryAverager = new TelemetryAverager( - this.telemetryAPI, - this.timeAPI, - domainObject, - samples, - addToAverageData - ); - const createAverageDatum = telemetryAverager.createAverageDatum.bind(telemetryAverager); - - return this.telemetryAPI.request(domainObject, request).then(function (telemetryData) { - telemetryData.forEach(createAverageDatum); - - return averageData; - }); - }; - - /** - * @private - */ - MeanTelemetryProvider.prototype.getLinkedObject = function (domainObject) { - const objectId = objectUtils.parseKeyString(domainObject.telemetryPoint); - - return this.objectAPI.get(objectId); + return function unsubscribe() { + unsubscribeCalled = true; + if (wrappedUnsubscribe !== undefined) { + wrappedUnsubscribe(); + } }; +}; - function logError(error) { - if (error.stack) { - console.error(error.stack); - } else { - console.error(error); - } +MeanTelemetryProvider.prototype.subscribeToAverage = function (domainObject, samples, callback) { + const telemetryAverager = new TelemetryAverager( + this.telemetryAPI, + this.timeAPI, + domainObject, + samples, + callback + ); + const createAverageDatum = telemetryAverager.createAverageDatum.bind(telemetryAverager); + + return this.telemetryAPI.subscribe(domainObject, createAverageDatum); +}; + +MeanTelemetryProvider.prototype.request = function (domainObject, request) { + const objectId = objectUtils.parseKeyString(domainObject.telemetryPoint); + const samples = domainObject.samples; + + return this.objectAPI.get(objectId).then( + function (linkedDomainObject) { + return this.requestAverageTelemetry(linkedDomainObject, request, samples); + }.bind(this) + ); +}; + +/** + * @private + */ +MeanTelemetryProvider.prototype.requestAverageTelemetry = function ( + domainObject, + request, + samples +) { + const averageData = []; + const addToAverageData = averageData.push.bind(averageData); + const telemetryAverager = new TelemetryAverager( + this.telemetryAPI, + this.timeAPI, + domainObject, + samples, + addToAverageData + ); + const createAverageDatum = telemetryAverager.createAverageDatum.bind(telemetryAverager); + + return this.telemetryAPI.request(domainObject, request).then(function (telemetryData) { + telemetryData.forEach(createAverageDatum); + + return averageData; + }); +}; + +/** + * @private + */ +MeanTelemetryProvider.prototype.getLinkedObject = function (domainObject) { + const objectId = objectUtils.parseKeyString(domainObject.telemetryPoint); + + return this.objectAPI.get(objectId); +}; + +function logError(error) { + if (error.stack) { + console.error(error.stack); + } else { + console.error(error); } - - return MeanTelemetryProvider; -}); +} diff --git a/src/plugins/telemetryMean/src/MeanTelemetryProviderSpec.js b/src/plugins/telemetryMean/src/MeanTelemetryProviderSpec.js index 53015cce7e6..df7fd51a06d 100644 --- a/src/plugins/telemetryMean/src/MeanTelemetryProviderSpec.js +++ b/src/plugins/telemetryMean/src/MeanTelemetryProviderSpec.js @@ -20,604 +20,603 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ /* eslint-disable no-invalid-this */ -define(['./MeanTelemetryProvider', './MockTelemetryApi'], function ( - MeanTelemetryProvider, - MockTelemetryApi -) { - const RANGE_KEY = 'value'; - - describe('The Mean Telemetry Provider', function () { - let mockApi; - let meanTelemetryProvider; - let mockDomainObject; - let associatedObject; - let allPromises; + +import MeanTelemetryProvider from './MeanTelemetryProvider'; +import MockTelemetryApi from './MockTelemetryApi'; + +const RANGE_KEY = 'value'; + +describe('The Mean Telemetry Provider', function () { + let mockApi; + let meanTelemetryProvider; + let mockDomainObject; + let associatedObject; + let allPromises; + + beforeEach(function () { + allPromises = []; + createMockApi(); + setTimeSystemTo('utc'); + createMockObjects(); + meanTelemetryProvider = new MeanTelemetryProvider(mockApi); + }); + + it('supports telemetry-mean objects only', function () { + const mockTelemetryMeanObject = mockObjectWithType('telemetry-mean'); + const mockOtherObject = mockObjectWithType('other'); + + expect(meanTelemetryProvider.canProvideTelemetry(mockTelemetryMeanObject)).toBe(true); + expect(meanTelemetryProvider.canProvideTelemetry(mockOtherObject)).toBe(false); + }); + + describe('the subscribe function', function () { + let subscriptionCallback; beforeEach(function () { - allPromises = []; - createMockApi(); - setTimeSystemTo('utc'); - createMockObjects(); - meanTelemetryProvider = new MeanTelemetryProvider(mockApi); + subscriptionCallback = jasmine.createSpy('subscriptionCallback'); }); - it('supports telemetry-mean objects only', function () { - const mockTelemetryMeanObject = mockObjectWithType('telemetry-mean'); - const mockOtherObject = mockObjectWithType('other'); + it('subscribes to telemetry for the associated object', function () { + meanTelemetryProvider.subscribe(mockDomainObject); - expect(meanTelemetryProvider.canProvideTelemetry(mockTelemetryMeanObject)).toBe(true); - expect(meanTelemetryProvider.canProvideTelemetry(mockOtherObject)).toBe(false); + return expectObjectWasSubscribedTo(associatedObject); }); - describe('the subscribe function', function () { - let subscriptionCallback; + it('returns a function that unsubscribes from the associated object', function () { + const unsubscribe = meanTelemetryProvider.subscribe(mockDomainObject); - beforeEach(function () { - subscriptionCallback = jasmine.createSpy('subscriptionCallback'); - }); + return waitForPromises() + .then(unsubscribe) + .then(waitForPromises) + .then(function () { + expect(mockApi.telemetry.unsubscribe).toHaveBeenCalled(); + }); + }); - it('subscribes to telemetry for the associated object', function () { - meanTelemetryProvider.subscribe(mockDomainObject); + it('returns an average only when the sample size is reached', function () { + const inputTelemetry = [ + { + utc: 1, + defaultRange: 123.1231 + }, + { + utc: 2, + defaultRange: 321.3223 + }, + { + utc: 3, + defaultRange: 111.4446 + }, + { + utc: 4, + defaultRange: 555.2313 + } + ]; + + setSampleSize(5); + meanTelemetryProvider.subscribe(mockDomainObject, subscriptionCallback); + + return waitForPromises() + .then(feedInputTelemetry.bind(this, inputTelemetry)) + .then(function () { + expect(subscriptionCallback).not.toHaveBeenCalled(); + }); + }); - return expectObjectWasSubscribedTo(associatedObject); - }); + it('correctly averages a sample of five values', function () { + const inputTelemetry = [ + { + utc: 1, + defaultRange: 123.1231 + }, + { + utc: 2, + defaultRange: 321.3223 + }, + { + utc: 3, + defaultRange: 111.4446 + }, + { + utc: 4, + defaultRange: 555.2313 + }, + { + utc: 5, + defaultRange: 1.1231 + } + ]; + const expectedAverages = [ + { + utc: 5, + value: 222.44888 + } + ]; + + setSampleSize(5); + meanTelemetryProvider.subscribe(mockDomainObject, subscriptionCallback); + + return waitForPromises() + .then(feedInputTelemetry.bind(this, inputTelemetry)) + .then(expectAveragesForTelemetry.bind(this, expectedAverages)); + }); - it('returns a function that unsubscribes from the associated object', function () { - const unsubscribe = meanTelemetryProvider.subscribe(mockDomainObject); + it('correctly averages a sample of ten values', function () { + const inputTelemetry = [ + { + utc: 1, + defaultRange: 123.1231 + }, + { + utc: 2, + defaultRange: 321.3223 + }, + { + utc: 3, + defaultRange: 111.4446 + }, + { + utc: 4, + defaultRange: 555.2313 + }, + { + utc: 5, + defaultRange: 1.1231 + }, + { + utc: 6, + defaultRange: 2323.12 + }, + { + utc: 7, + defaultRange: 532.12 + }, + { + utc: 8, + defaultRange: 453.543 + }, + { + utc: 9, + defaultRange: 89.2111 + }, + { + utc: 10, + defaultRange: 0.543 + } + ]; + const expectedAverages = [ + { + utc: 10, + value: 451.07815 + } + ]; + + setSampleSize(10); + meanTelemetryProvider.subscribe(mockDomainObject, subscriptionCallback); + + return waitForPromises() + .then(feedInputTelemetry.bind(this, inputTelemetry)) + .then(expectAveragesForTelemetry.bind(this, expectedAverages)); + }); - return waitForPromises() - .then(unsubscribe) - .then(waitForPromises) - .then(function () { - expect(mockApi.telemetry.unsubscribe).toHaveBeenCalled(); - }); - }); + it('only averages values within its sample window', function () { + const inputTelemetry = [ + { + utc: 1, + defaultRange: 123.1231 + }, + { + utc: 2, + defaultRange: 321.3223 + }, + { + utc: 3, + defaultRange: 111.4446 + }, + { + utc: 4, + defaultRange: 555.2313 + }, + { + utc: 5, + defaultRange: 1.1231 + }, + { + utc: 6, + defaultRange: 2323.12 + }, + { + utc: 7, + defaultRange: 532.12 + }, + { + utc: 8, + defaultRange: 453.543 + }, + { + utc: 9, + defaultRange: 89.2111 + }, + { + utc: 10, + defaultRange: 0.543 + } + ]; + const expectedAverages = [ + { + utc: 5, + value: 222.44888 + }, + { + utc: 6, + value: 662.4482599999999 + }, + { + utc: 7, + value: 704.6078 + }, + { + utc: 8, + value: 773.02748 + }, + { + utc: 9, + value: 679.8234399999999 + }, + { + utc: 10, + value: 679.70742 + } + ]; + + setSampleSize(5); + meanTelemetryProvider.subscribe(mockDomainObject, subscriptionCallback); + + return waitForPromises() + .then(feedInputTelemetry.bind(this, inputTelemetry)) + .then(expectAveragesForTelemetry.bind(this, expectedAverages)); + }); + describe('given telemetry input with range values', function () { + let inputTelemetry; - it('returns an average only when the sample size is reached', function () { - const inputTelemetry = [ + beforeEach(function () { + inputTelemetry = [ { utc: 1, - defaultRange: 123.1231 - }, - { - utc: 2, - defaultRange: 321.3223 - }, - { - utc: 3, - defaultRange: 111.4446 - }, + rangeKey: 5678, + otherKey: 9999 + } + ]; + setSampleSize(1); + }); + it("uses the 'rangeKey' input range, when it is the default, to calculate the average", function () { + const averageTelemetryForRangeKey = [ { - utc: 4, - defaultRange: 555.2313 + utc: 1, + value: 5678 } ]; - setSampleSize(5); meanTelemetryProvider.subscribe(mockDomainObject, subscriptionCallback); + mockApi.telemetry.setDefaultRangeTo('rangeKey'); return waitForPromises() .then(feedInputTelemetry.bind(this, inputTelemetry)) - .then(function () { - expect(subscriptionCallback).not.toHaveBeenCalled(); - }); + .then(expectAveragesForTelemetry.bind(this, averageTelemetryForRangeKey)); }); - it('correctly averages a sample of five values', function () { - const inputTelemetry = [ + it("uses the 'otherKey' input range, when it is the default, to calculate the average", function () { + const averageTelemetryForOtherKey = [ { utc: 1, - defaultRange: 123.1231 - }, - { - utc: 2, - defaultRange: 321.3223 - }, - { - utc: 3, - defaultRange: 111.4446 - }, - { - utc: 4, - defaultRange: 555.2313 - }, - { - utc: 5, - defaultRange: 1.1231 - } - ]; - const expectedAverages = [ - { - utc: 5, - value: 222.44888 + value: 9999 } ]; - setSampleSize(5); meanTelemetryProvider.subscribe(mockDomainObject, subscriptionCallback); + mockApi.telemetry.setDefaultRangeTo('otherKey'); return waitForPromises() .then(feedInputTelemetry.bind(this, inputTelemetry)) - .then(expectAveragesForTelemetry.bind(this, expectedAverages)); + .then(expectAveragesForTelemetry.bind(this, averageTelemetryForOtherKey)); }); + }); + describe('given telemetry input with range values', function () { + let inputTelemetry; - it('correctly averages a sample of ten values', function () { - const inputTelemetry = [ + beforeEach(function () { + inputTelemetry = [ { utc: 1, - defaultRange: 123.1231 - }, - { - utc: 2, - defaultRange: 321.3223 - }, - { - utc: 3, - defaultRange: 111.4446 - }, - { - utc: 4, - defaultRange: 555.2313 - }, - { - utc: 5, - defaultRange: 1.1231 - }, - { - utc: 6, - defaultRange: 2323.12 - }, - { - utc: 7, - defaultRange: 532.12 - }, - { - utc: 8, - defaultRange: 453.543 - }, - { - utc: 9, - defaultRange: 89.2111 - }, - { - utc: 10, - defaultRange: 0.543 + rangeKey: 5678, + otherKey: 9999 } ]; - const expectedAverages = [ + setSampleSize(1); + }); + it("uses the 'rangeKey' input range, when it is the default, to calculate the average", function () { + const averageTelemetryForRangeKey = [ { - utc: 10, - value: 451.07815 + utc: 1, + value: 5678 } ]; - setSampleSize(10); meanTelemetryProvider.subscribe(mockDomainObject, subscriptionCallback); + mockApi.telemetry.setDefaultRangeTo('rangeKey'); return waitForPromises() .then(feedInputTelemetry.bind(this, inputTelemetry)) - .then(expectAveragesForTelemetry.bind(this, expectedAverages)); + .then(expectAveragesForTelemetry.bind(this, averageTelemetryForRangeKey)); }); - it('only averages values within its sample window', function () { - const inputTelemetry = [ + it("uses the 'otherKey' input range, when it is the default, to calculate the average", function () { + const averageTelemetryForOtherKey = [ { utc: 1, - defaultRange: 123.1231 - }, - { - utc: 2, - defaultRange: 321.3223 - }, - { - utc: 3, - defaultRange: 111.4446 - }, - { - utc: 4, - defaultRange: 555.2313 - }, - { - utc: 5, - defaultRange: 1.1231 - }, - { - utc: 6, - defaultRange: 2323.12 - }, - { - utc: 7, - defaultRange: 532.12 - }, - { - utc: 8, - defaultRange: 453.543 - }, - { - utc: 9, - defaultRange: 89.2111 - }, - { - utc: 10, - defaultRange: 0.543 - } - ]; - const expectedAverages = [ - { - utc: 5, - value: 222.44888 - }, - { - utc: 6, - value: 662.4482599999999 - }, - { - utc: 7, - value: 704.6078 - }, - { - utc: 8, - value: 773.02748 - }, - { - utc: 9, - value: 679.8234399999999 - }, - { - utc: 10, - value: 679.70742 + value: 9999 } ]; - setSampleSize(5); meanTelemetryProvider.subscribe(mockDomainObject, subscriptionCallback); + mockApi.telemetry.setDefaultRangeTo('otherKey'); return waitForPromises() .then(feedInputTelemetry.bind(this, inputTelemetry)) - .then(expectAveragesForTelemetry.bind(this, expectedAverages)); + .then(expectAveragesForTelemetry.bind(this, averageTelemetryForOtherKey)); }); - describe('given telemetry input with range values', function () { - let inputTelemetry; - - beforeEach(function () { - inputTelemetry = [ - { - utc: 1, - rangeKey: 5678, - otherKey: 9999 - } - ]; - setSampleSize(1); - }); - it("uses the 'rangeKey' input range, when it is the default, to calculate the average", function () { - const averageTelemetryForRangeKey = [ - { - utc: 1, - value: 5678 - } - ]; - - meanTelemetryProvider.subscribe(mockDomainObject, subscriptionCallback); - mockApi.telemetry.setDefaultRangeTo('rangeKey'); - - return waitForPromises() - .then(feedInputTelemetry.bind(this, inputTelemetry)) - .then(expectAveragesForTelemetry.bind(this, averageTelemetryForRangeKey)); - }); - - it("uses the 'otherKey' input range, when it is the default, to calculate the average", function () { - const averageTelemetryForOtherKey = [ - { - utc: 1, - value: 9999 - } - ]; + }); - meanTelemetryProvider.subscribe(mockDomainObject, subscriptionCallback); - mockApi.telemetry.setDefaultRangeTo('otherKey'); + function feedInputTelemetry(inputTelemetry) { + inputTelemetry.forEach(mockApi.telemetry.mockReceiveTelemetry); + } - return waitForPromises() - .then(feedInputTelemetry.bind(this, inputTelemetry)) - .then(expectAveragesForTelemetry.bind(this, averageTelemetryForOtherKey)); + function expectAveragesForTelemetry(expectedAverages) { + return waitForPromises().then(function () { + expectedAverages.forEach(function (averageDatum) { + expect(subscriptionCallback).toHaveBeenCalledWith(averageDatum); }); }); - describe('given telemetry input with range values', function () { - let inputTelemetry; - - beforeEach(function () { - inputTelemetry = [ - { - utc: 1, - rangeKey: 5678, - otherKey: 9999 - } - ]; - setSampleSize(1); - }); - it("uses the 'rangeKey' input range, when it is the default, to calculate the average", function () { - const averageTelemetryForRangeKey = [ - { - utc: 1, - value: 5678 - } - ]; - - meanTelemetryProvider.subscribe(mockDomainObject, subscriptionCallback); - mockApi.telemetry.setDefaultRangeTo('rangeKey'); - - return waitForPromises() - .then(feedInputTelemetry.bind(this, inputTelemetry)) - .then(expectAveragesForTelemetry.bind(this, averageTelemetryForRangeKey)); - }); - - it("uses the 'otherKey' input range, when it is the default, to calculate the average", function () { - const averageTelemetryForOtherKey = [ - { - utc: 1, - value: 9999 - } - ]; - - meanTelemetryProvider.subscribe(mockDomainObject, subscriptionCallback); - mockApi.telemetry.setDefaultRangeTo('otherKey'); + } - return waitForPromises() - .then(feedInputTelemetry.bind(this, inputTelemetry)) - .then(expectAveragesForTelemetry.bind(this, averageTelemetryForOtherKey)); - }); + function expectObjectWasSubscribedTo(object) { + return waitForPromises().then(function () { + expect(mockApi.telemetry.subscribe).toHaveBeenCalledWith(object, jasmine.any(Function)); }); + } + }); - function feedInputTelemetry(inputTelemetry) { - inputTelemetry.forEach(mockApi.telemetry.mockReceiveTelemetry); - } - - function expectAveragesForTelemetry(expectedAverages) { - return waitForPromises().then(function () { - expectedAverages.forEach(function (averageDatum) { - expect(subscriptionCallback).toHaveBeenCalledWith(averageDatum); - }); - }); - } - - function expectObjectWasSubscribedTo(object) { - return waitForPromises().then(function () { - expect(mockApi.telemetry.subscribe).toHaveBeenCalledWith(object, jasmine.any(Function)); - }); - } - }); - - describe('the request function', function () { - it('requests telemetry for the associated object', function () { - whenTelemetryRequestedReturn([]); + describe('the request function', function () { + it('requests telemetry for the associated object', function () { + whenTelemetryRequestedReturn([]); - return meanTelemetryProvider.request(mockDomainObject).then(function () { - expect(mockApi.telemetry.request).toHaveBeenCalledWith(associatedObject, undefined); - }); + return meanTelemetryProvider.request(mockDomainObject).then(function () { + expect(mockApi.telemetry.request).toHaveBeenCalledWith(associatedObject, undefined); }); + }); - it('returns an average only when the sample size is reached', function () { - const inputTelemetry = [ - { - utc: 1, - defaultRange: 123.1231 - }, - { - utc: 2, - defaultRange: 321.3223 - }, - { - utc: 3, - defaultRange: 111.4446 - }, - { - utc: 4, - defaultRange: 555.2313 - } - ]; - - setSampleSize(5); - whenTelemetryRequestedReturn(inputTelemetry); - - return meanTelemetryProvider.request(mockDomainObject).then(function (averageData) { - expect(averageData.length).toBe(0); - }); + it('returns an average only when the sample size is reached', function () { + const inputTelemetry = [ + { + utc: 1, + defaultRange: 123.1231 + }, + { + utc: 2, + defaultRange: 321.3223 + }, + { + utc: 3, + defaultRange: 111.4446 + }, + { + utc: 4, + defaultRange: 555.2313 + } + ]; + + setSampleSize(5); + whenTelemetryRequestedReturn(inputTelemetry); + + return meanTelemetryProvider.request(mockDomainObject).then(function (averageData) { + expect(averageData.length).toBe(0); }); + }); - it('correctly averages a sample of five values', function () { - const inputTelemetry = [ - { - utc: 1, - defaultRange: 123.1231 - }, - { - utc: 2, - defaultRange: 321.3223 - }, - { - utc: 3, - defaultRange: 111.4446 - }, - { - utc: 4, - defaultRange: 555.2313 - }, - { - utc: 5, - defaultRange: 1.1231 - } - ]; - - setSampleSize(5); - whenTelemetryRequestedReturn(inputTelemetry); - - return meanTelemetryProvider.request(mockDomainObject).then(function (averageData) { - expectAverageToBe(222.44888, averageData); - }); + it('correctly averages a sample of five values', function () { + const inputTelemetry = [ + { + utc: 1, + defaultRange: 123.1231 + }, + { + utc: 2, + defaultRange: 321.3223 + }, + { + utc: 3, + defaultRange: 111.4446 + }, + { + utc: 4, + defaultRange: 555.2313 + }, + { + utc: 5, + defaultRange: 1.1231 + } + ]; + + setSampleSize(5); + whenTelemetryRequestedReturn(inputTelemetry); + + return meanTelemetryProvider.request(mockDomainObject).then(function (averageData) { + expectAverageToBe(222.44888, averageData); }); + }); - it('correctly averages a sample of ten values', function () { - const inputTelemetry = [ - { - utc: 1, - defaultRange: 123.1231 - }, - { - utc: 2, - defaultRange: 321.3223 - }, - { - utc: 3, - defaultRange: 111.4446 - }, - { - utc: 4, - defaultRange: 555.2313 - }, - { - utc: 5, - defaultRange: 1.1231 - }, - { - utc: 6, - defaultRange: 2323.12 - }, - { - utc: 7, - defaultRange: 532.12 - }, - { - utc: 8, - defaultRange: 453.543 - }, - { - utc: 9, - defaultRange: 89.2111 - }, - { - utc: 10, - defaultRange: 0.543 - } - ]; - - setSampleSize(10); - whenTelemetryRequestedReturn(inputTelemetry); - - return meanTelemetryProvider.request(mockDomainObject).then(function (averageData) { - expectAverageToBe(451.07815, averageData); - }); + it('correctly averages a sample of ten values', function () { + const inputTelemetry = [ + { + utc: 1, + defaultRange: 123.1231 + }, + { + utc: 2, + defaultRange: 321.3223 + }, + { + utc: 3, + defaultRange: 111.4446 + }, + { + utc: 4, + defaultRange: 555.2313 + }, + { + utc: 5, + defaultRange: 1.1231 + }, + { + utc: 6, + defaultRange: 2323.12 + }, + { + utc: 7, + defaultRange: 532.12 + }, + { + utc: 8, + defaultRange: 453.543 + }, + { + utc: 9, + defaultRange: 89.2111 + }, + { + utc: 10, + defaultRange: 0.543 + } + ]; + + setSampleSize(10); + whenTelemetryRequestedReturn(inputTelemetry); + + return meanTelemetryProvider.request(mockDomainObject).then(function (averageData) { + expectAverageToBe(451.07815, averageData); }); + }); - it('only averages values within its sample window', function () { - const inputTelemetry = [ - { - utc: 1, - defaultRange: 123.1231 - }, - { - utc: 2, - defaultRange: 321.3223 - }, - { - utc: 3, - defaultRange: 111.4446 - }, - { - utc: 4, - defaultRange: 555.2313 - }, - { - utc: 5, - defaultRange: 1.1231 - }, - { - utc: 6, - defaultRange: 2323.12 - }, - { - utc: 7, - defaultRange: 532.12 - }, - { - utc: 8, - defaultRange: 453.543 - }, - { - utc: 9, - defaultRange: 89.2111 - }, - { - utc: 10, - defaultRange: 0.543 - } - ]; - - setSampleSize(5); - whenTelemetryRequestedReturn(inputTelemetry); - - return meanTelemetryProvider.request(mockDomainObject).then(function (averageData) { - expectAverageToBe(679.70742, averageData); - }); + it('only averages values within its sample window', function () { + const inputTelemetry = [ + { + utc: 1, + defaultRange: 123.1231 + }, + { + utc: 2, + defaultRange: 321.3223 + }, + { + utc: 3, + defaultRange: 111.4446 + }, + { + utc: 4, + defaultRange: 555.2313 + }, + { + utc: 5, + defaultRange: 1.1231 + }, + { + utc: 6, + defaultRange: 2323.12 + }, + { + utc: 7, + defaultRange: 532.12 + }, + { + utc: 8, + defaultRange: 453.543 + }, + { + utc: 9, + defaultRange: 89.2111 + }, + { + utc: 10, + defaultRange: 0.543 + } + ]; + + setSampleSize(5); + whenTelemetryRequestedReturn(inputTelemetry); + + return meanTelemetryProvider.request(mockDomainObject).then(function (averageData) { + expectAverageToBe(679.70742, averageData); }); - - function expectAverageToBe(expectedValue, averageData) { - const averageDatum = averageData[averageData.length - 1]; - expect(averageDatum[RANGE_KEY]).toBe(expectedValue); - } - - function whenTelemetryRequestedReturn(telemetry) { - mockApi.telemetry.request.and.returnValue(resolvePromiseWith(telemetry)); - } }); - function createMockObjects() { - mockDomainObject = { - telemetryPoint: 'someTelemetryPoint' - }; - associatedObject = {}; - mockApi.objects.get.and.returnValue(resolvePromiseWith(associatedObject)); - } - - function setSampleSize(sampleSize) { - mockDomainObject.samples = sampleSize; - } - - function createMockApi() { - mockApi = { - telemetry: new MockTelemetryApi(), - objects: createMockObjectApi(), - time: createMockTimeApi() - }; - } - - function createMockObjectApi() { - return jasmine.createSpyObj('ObjectAPI', ['get']); - } - - function mockObjectWithType(type) { - return { - type: type - }; + function expectAverageToBe(expectedValue, averageData) { + const averageDatum = averageData[averageData.length - 1]; + expect(averageDatum[RANGE_KEY]).toBe(expectedValue); } - function resolvePromiseWith(value) { - const promise = Promise.resolve(value); - allPromises.push(promise); - - return promise; - } - - function waitForPromises() { - return Promise.all(allPromises); - } - - function createMockTimeApi() { - return jasmine.createSpyObj('timeApi', ['timeSystem']); - } - - function setTimeSystemTo(timeSystemKey) { - mockApi.time.timeSystem.and.returnValue({ - key: timeSystemKey - }); + function whenTelemetryRequestedReturn(telemetry) { + mockApi.telemetry.request.and.returnValue(resolvePromiseWith(telemetry)); } }); + + function createMockObjects() { + mockDomainObject = { + telemetryPoint: 'someTelemetryPoint' + }; + associatedObject = {}; + mockApi.objects.get.and.returnValue(resolvePromiseWith(associatedObject)); + } + + function setSampleSize(sampleSize) { + mockDomainObject.samples = sampleSize; + } + + function createMockApi() { + mockApi = { + telemetry: new MockTelemetryApi(), + objects: createMockObjectApi(), + time: createMockTimeApi() + }; + } + + function createMockObjectApi() { + return jasmine.createSpyObj('ObjectAPI', ['get']); + } + + function mockObjectWithType(type) { + return { + type: type + }; + } + + function resolvePromiseWith(value) { + const promise = Promise.resolve(value); + allPromises.push(promise); + + return promise; + } + + function waitForPromises() { + return Promise.all(allPromises); + } + + function createMockTimeApi() { + return jasmine.createSpyObj('timeApi', ['timeSystem']); + } + + function setTimeSystemTo(timeSystemKey) { + mockApi.time.timeSystem.and.returnValue({ + key: timeSystemKey + }); + } }); diff --git a/src/plugins/telemetryMean/src/MockTelemetryApi.js b/src/plugins/telemetryMean/src/MockTelemetryApi.js index 85cef975712..3a6b9d89c28 100644 --- a/src/plugins/telemetryMean/src/MockTelemetryApi.js +++ b/src/plugins/telemetryMean/src/MockTelemetryApi.js @@ -20,82 +20,78 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define([], function () { - function MockTelemetryApi() { - this.createSpy('subscribe'); - this.createSpy('getMetadata'); - - this.metadata = this.createMockMetadata(); - this.setDefaultRangeTo('defaultRange'); - this.unsubscribe = jasmine.createSpy('unsubscribe'); - this.mockReceiveTelemetry = this.mockReceiveTelemetry.bind(this); - } - - MockTelemetryApi.prototype.subscribe = function () { - return this.unsubscribe; - }; - - MockTelemetryApi.prototype.getMetadata = function (object) { - return this.metadata; - }; - - MockTelemetryApi.prototype.request = jasmine.createSpy('request'); - - MockTelemetryApi.prototype.getValueFormatter = function (valueMetadata) { - const mockValueFormatter = jasmine.createSpyObj('valueFormatter', ['parse']); - - mockValueFormatter.parse.and.callFake(function (value) { - return value[valueMetadata.key]; - }); - - return mockValueFormatter; - }; - - MockTelemetryApi.prototype.mockReceiveTelemetry = function (newTelemetryDatum) { - const subscriptionCallback = this.subscribe.calls.mostRecent().args[1]; - subscriptionCallback(newTelemetryDatum); +export default function MockTelemetryApi() { + this.createSpy('subscribe'); + this.createSpy('getMetadata'); + + this.metadata = this.createMockMetadata(); + this.setDefaultRangeTo('defaultRange'); + this.unsubscribe = jasmine.createSpy('unsubscribe'); + this.mockReceiveTelemetry = this.mockReceiveTelemetry.bind(this); +} + +MockTelemetryApi.prototype.subscribe = function () { + return this.unsubscribe; +}; + +MockTelemetryApi.prototype.getMetadata = function (object) { + return this.metadata; +}; + +MockTelemetryApi.prototype.request = jasmine.createSpy('request'); + +MockTelemetryApi.prototype.getValueFormatter = function (valueMetadata) { + const mockValueFormatter = jasmine.createSpyObj('valueFormatter', ['parse']); + + mockValueFormatter.parse.and.callFake(function (value) { + return value[valueMetadata.key]; + }); + + return mockValueFormatter; +}; + +MockTelemetryApi.prototype.mockReceiveTelemetry = function (newTelemetryDatum) { + const subscriptionCallback = this.subscribe.calls.mostRecent().args[1]; + subscriptionCallback(newTelemetryDatum); +}; + +/** + * @private + */ +MockTelemetryApi.prototype.onRequestReturn = function (telemetryData) { + this.requestTelemetry = telemetryData; +}; + +/** + * @private + */ +MockTelemetryApi.prototype.setDefaultRangeTo = function (rangeKey) { + const mockMetadataValue = { + key: rangeKey }; - - /** - * @private - */ - MockTelemetryApi.prototype.onRequestReturn = function (telemetryData) { - this.requestTelemetry = telemetryData; - }; - - /** - * @private - */ - MockTelemetryApi.prototype.setDefaultRangeTo = function (rangeKey) { - const mockMetadataValue = { - key: rangeKey + this.metadata.valuesForHints.and.returnValue([mockMetadataValue]); +}; + +/** + * @private + */ +MockTelemetryApi.prototype.createMockMetadata = function () { + const mockMetadata = jasmine.createSpyObj('metadata', ['value', 'valuesForHints']); + + mockMetadata.value.and.callFake(function (key) { + return { + key: key }; - this.metadata.valuesForHints.and.returnValue([mockMetadataValue]); - }; - - /** - * @private - */ - MockTelemetryApi.prototype.createMockMetadata = function () { - const mockMetadata = jasmine.createSpyObj('metadata', ['value', 'valuesForHints']); - - mockMetadata.value.and.callFake(function (key) { - return { - key: key - }; - }); - - return mockMetadata; - }; - - /** - * @private - */ - MockTelemetryApi.prototype.createSpy = function (functionName) { - this[functionName] = this[functionName].bind(this); - spyOn(this, functionName); - this[functionName].and.callThrough(); - }; - - return MockTelemetryApi; -}); + }); + + return mockMetadata; +}; + +/** + * @private + */ +MockTelemetryApi.prototype.createSpy = function (functionName) { + this[functionName] = this[functionName].bind(this); + spyOn(this, functionName); + this[functionName].and.callThrough(); +}; diff --git a/src/plugins/telemetryMean/src/TelemetryAverager.js b/src/plugins/telemetryMean/src/TelemetryAverager.js index 3d40deee448..95194f3f586 100644 --- a/src/plugins/telemetryMean/src/TelemetryAverager.js +++ b/src/plugins/telemetryMean/src/TelemetryAverager.js @@ -20,100 +20,102 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define([], function () { - function TelemetryAverager(telemetryAPI, timeAPI, domainObject, samples, averageDatumCallback) { - this.telemetryAPI = telemetryAPI; - this.timeAPI = timeAPI; +export default function TelemetryAverager( + telemetryAPI, + timeAPI, + domainObject, + samples, + averageDatumCallback +) { + this.telemetryAPI = telemetryAPI; + this.timeAPI = timeAPI; + + this.domainObject = domainObject; + this.samples = samples; + this.averagingWindow = []; + + this.rangeKey = undefined; + this.rangeFormatter = undefined; + this.setRangeKeyAndFormatter(); + + // Defined dynamically based on current time system + this.domainKey = undefined; + this.domainFormatter = undefined; + + this.averageDatumCallback = averageDatumCallback; +} + +TelemetryAverager.prototype.createAverageDatum = function (telemetryDatum) { + this.setDomainKeyAndFormatter(); + + const timeValue = this.domainFormatter.parse(telemetryDatum); + const rangeValue = this.rangeFormatter.parse(telemetryDatum); + + this.averagingWindow.push(rangeValue); + + if (this.averagingWindow.length < this.samples) { + // We do not have enough data to produce an average + return; + } else if (this.averagingWindow.length > this.samples) { + //Do not let averaging window grow beyond defined sample size + this.averagingWindow.shift(); + } + + const averageValue = this.calculateMean(); - this.domainObject = domainObject; - this.samples = samples; - this.averagingWindow = []; + const meanDatum = {}; + meanDatum[this.domainKey] = timeValue; + meanDatum.value = averageValue; - this.rangeKey = undefined; - this.rangeFormatter = undefined; - this.setRangeKeyAndFormatter(); + this.averageDatumCallback(meanDatum); +}; - // Defined dynamically based on current time system - this.domainKey = undefined; - this.domainFormatter = undefined; +/** + * @private + */ +TelemetryAverager.prototype.calculateMean = function () { + let sum = 0; + let i = 0; - this.averageDatumCallback = averageDatumCallback; + for (; i < this.averagingWindow.length; i++) { + sum += this.averagingWindow[i]; } - TelemetryAverager.prototype.createAverageDatum = function (telemetryDatum) { - this.setDomainKeyAndFormatter(); - - const timeValue = this.domainFormatter.parse(telemetryDatum); - const rangeValue = this.rangeFormatter.parse(telemetryDatum); - - this.averagingWindow.push(rangeValue); - - if (this.averagingWindow.length < this.samples) { - // We do not have enough data to produce an average - return; - } else if (this.averagingWindow.length > this.samples) { - //Do not let averaging window grow beyond defined sample size - this.averagingWindow.shift(); - } - - const averageValue = this.calculateMean(); - - const meanDatum = {}; - meanDatum[this.domainKey] = timeValue; - meanDatum.value = averageValue; - - this.averageDatumCallback(meanDatum); - }; - - /** - * @private - */ - TelemetryAverager.prototype.calculateMean = function () { - let sum = 0; - let i = 0; - - for (; i < this.averagingWindow.length; i++) { - sum += this.averagingWindow[i]; - } - - return sum / this.averagingWindow.length; - }; - - /** - * The mean telemetry filter produces domain values in whatever time - * system is currently selected from the conductor. Because this can - * change dynamically, the averager needs to be updated regularly with - * the current domain. - * @private - */ - TelemetryAverager.prototype.setDomainKeyAndFormatter = function () { - const domainKey = this.timeAPI.timeSystem().key; - if (domainKey !== this.domainKey) { - this.domainKey = domainKey; - this.domainFormatter = this.getFormatter(domainKey); - } - }; - - /** - * @private - */ - TelemetryAverager.prototype.setRangeKeyAndFormatter = function () { - const metadatas = this.telemetryAPI.getMetadata(this.domainObject); - const rangeValues = metadatas.valuesForHints(['range']); - - this.rangeKey = rangeValues[0].key; - this.rangeFormatter = this.getFormatter(this.rangeKey); - }; - - /** - * @private - */ - TelemetryAverager.prototype.getFormatter = function (key) { - const objectMetadata = this.telemetryAPI.getMetadata(this.domainObject); - const valueMetadata = objectMetadata.value(key); - - return this.telemetryAPI.getValueFormatter(valueMetadata); - }; - - return TelemetryAverager; -}); + return sum / this.averagingWindow.length; +}; + +/** + * The mean telemetry filter produces domain values in whatever time + * system is currently selected from the conductor. Because this can + * change dynamically, the averager needs to be updated regularly with + * the current domain. + * @private + */ +TelemetryAverager.prototype.setDomainKeyAndFormatter = function () { + const domainKey = this.timeAPI.timeSystem().key; + if (domainKey !== this.domainKey) { + this.domainKey = domainKey; + this.domainFormatter = this.getFormatter(domainKey); + } +}; + +/** + * @private + */ +TelemetryAverager.prototype.setRangeKeyAndFormatter = function () { + const metadatas = this.telemetryAPI.getMetadata(this.domainObject); + const rangeValues = metadatas.valuesForHints(['range']); + + this.rangeKey = rangeValues[0].key; + this.rangeFormatter = this.getFormatter(this.rangeKey); +}; + +/** + * @private + */ +TelemetryAverager.prototype.getFormatter = function (key) { + const objectMetadata = this.telemetryAPI.getMetadata(this.domainObject); + const valueMetadata = objectMetadata.value(key); + + return this.telemetryAPI.getValueFormatter(valueMetadata); +}; diff --git a/src/plugins/telemetryTable/TelemetryTable.js b/src/plugins/telemetryTable/TelemetryTable.js index a6d98788ebe..56afb7503fc 100644 --- a/src/plugins/telemetryTable/TelemetryTable.js +++ b/src/plugins/telemetryTable/TelemetryTable.js @@ -20,428 +20,415 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define([ - 'EventEmitter', - 'lodash', - './collections/TableRowCollection', - './TelemetryTableRow', - './TelemetryTableNameColumn', - './TelemetryTableColumn', - './TelemetryTableUnitColumn', - './TelemetryTableConfiguration', - '../../utils/staleness' -], function ( - EventEmitter, - _, - TableRowCollection, - TelemetryTableRow, - TelemetryTableNameColumn, - TelemetryTableColumn, - TelemetryTableUnitColumn, - TelemetryTableConfiguration, - StalenessUtils -) { - class TelemetryTable extends EventEmitter { - constructor(domainObject, openmct) { - super(); - - this.domainObject = domainObject; - this.openmct = openmct; - this.rowCount = 100; - this.tableComposition = undefined; - this.datumCache = []; - this.configuration = new TelemetryTableConfiguration(domainObject, openmct); - this.paused = false; - this.keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier); - - this.telemetryObjects = {}; - this.telemetryCollections = {}; - this.delayedActions = []; - this.outstandingRequests = 0; - this.stalenessSubscription = {}; - - this.addTelemetryObject = this.addTelemetryObject.bind(this); - this.removeTelemetryObject = this.removeTelemetryObject.bind(this); - this.removeTelemetryCollection = this.removeTelemetryCollection.bind(this); - this.incrementOutstandingRequests = this.incrementOutstandingRequests.bind(this); - this.decrementOutstandingRequests = this.decrementOutstandingRequests.bind(this); - this.resetRowsFromAllData = this.resetRowsFromAllData.bind(this); - this.isTelemetryObject = this.isTelemetryObject.bind(this); - this.updateFilters = this.updateFilters.bind(this); - this.clearData = this.clearData.bind(this); - this.buildOptionsFromConfiguration = this.buildOptionsFromConfiguration.bind(this); - - this.filterObserver = undefined; - - this.createTableRowCollections(); +import EventEmitter from 'EventEmitter'; +import _ from 'lodash'; + +import StalenessUtils from '../../utils/staleness'; +import TableRowCollection from './collections/TableRowCollection'; +import TelemetryTableColumn from './TelemetryTableColumn'; +import TelemetryTableConfiguration from './TelemetryTableConfiguration'; +import TelemetryTableNameColumn from './TelemetryTableNameColumn'; +import TelemetryTableRow from './TelemetryTableRow'; +import TelemetryTableUnitColumn from './TelemetryTableUnitColumn'; + +export default class TelemetryTable extends EventEmitter { + constructor(domainObject, openmct) { + super(); + + this.domainObject = domainObject; + this.openmct = openmct; + this.rowCount = 100; + this.tableComposition = undefined; + this.datumCache = []; + this.configuration = new TelemetryTableConfiguration(domainObject, openmct); + this.paused = false; + this.keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier); + + this.telemetryObjects = {}; + this.telemetryCollections = {}; + this.delayedActions = []; + this.outstandingRequests = 0; + this.stalenessSubscription = {}; + + this.addTelemetryObject = this.addTelemetryObject.bind(this); + this.removeTelemetryObject = this.removeTelemetryObject.bind(this); + this.removeTelemetryCollection = this.removeTelemetryCollection.bind(this); + this.incrementOutstandingRequests = this.incrementOutstandingRequests.bind(this); + this.decrementOutstandingRequests = this.decrementOutstandingRequests.bind(this); + this.resetRowsFromAllData = this.resetRowsFromAllData.bind(this); + this.isTelemetryObject = this.isTelemetryObject.bind(this); + this.updateFilters = this.updateFilters.bind(this); + this.clearData = this.clearData.bind(this); + this.buildOptionsFromConfiguration = this.buildOptionsFromConfiguration.bind(this); + + this.filterObserver = undefined; + + this.createTableRowCollections(); + } + + /** + * @private + */ + addNameColumn(telemetryObject, metadataValues) { + let metadatum = metadataValues.find((m) => m.key === 'name'); + if (!metadatum) { + metadatum = { + format: 'string', + key: 'name', + name: 'Name' + }; } - /** - * @private - */ - addNameColumn(telemetryObject, metadataValues) { - let metadatum = metadataValues.find((m) => m.key === 'name'); - if (!metadatum) { - metadatum = { - format: 'string', - key: 'name', - name: 'Name' - }; - } + const column = new TelemetryTableNameColumn(this.openmct, telemetryObject, metadatum); - const column = new TelemetryTableNameColumn(this.openmct, telemetryObject, metadatum); + this.configuration.addSingleColumnForObject(telemetryObject, column); + } - this.configuration.addSingleColumnForObject(telemetryObject, column); + initialize() { + if (this.domainObject.type === 'table') { + this.filterObserver = this.openmct.objects.observe( + this.domainObject, + 'configuration.filters', + this.updateFilters + ); + this.filters = this.domainObject.configuration.filters; + this.loadComposition(); + } else { + this.addTelemetryObject(this.domainObject); } + } - initialize() { - if (this.domainObject.type === 'table') { - this.filterObserver = this.openmct.objects.observe( - this.domainObject, - 'configuration.filters', - this.updateFilters - ); - this.filters = this.domainObject.configuration.filters; - this.loadComposition(); - } else { - this.addTelemetryObject(this.domainObject); - } - } + createTableRowCollections() { + this.tableRows = new TableRowCollection(); - createTableRowCollections() { - this.tableRows = new TableRowCollection(); + //Fetch any persisted default sort + let sortOptions = this.configuration.getConfiguration().sortOptions; - //Fetch any persisted default sort - let sortOptions = this.configuration.getConfiguration().sortOptions; + //If no persisted sort order, default to sorting by time system, ascending. + sortOptions = sortOptions || { + key: this.openmct.time.timeSystem().key, + direction: 'asc' + }; - //If no persisted sort order, default to sorting by time system, ascending. - sortOptions = sortOptions || { - key: this.openmct.time.timeSystem().key, - direction: 'asc' - }; - - this.tableRows.sortBy(sortOptions); - this.tableRows.on('resetRowsFromAllData', this.resetRowsFromAllData); - } + this.tableRows.sortBy(sortOptions); + this.tableRows.on('resetRowsFromAllData', this.resetRowsFromAllData); + } - loadComposition() { - this.tableComposition = this.openmct.composition.get(this.domainObject); + loadComposition() { + this.tableComposition = this.openmct.composition.get(this.domainObject); - if (this.tableComposition !== undefined) { - this.tableComposition.load().then((composition) => { - composition = composition.filter(this.isTelemetryObject); - composition.forEach(this.addTelemetryObject); + if (this.tableComposition !== undefined) { + this.tableComposition.load().then((composition) => { + composition = composition.filter(this.isTelemetryObject); + composition.forEach(this.addTelemetryObject); - this.tableComposition.on('add', this.addTelemetryObject); - this.tableComposition.on('remove', this.removeTelemetryObject); - }); - } + this.tableComposition.on('add', this.addTelemetryObject); + this.tableComposition.on('remove', this.removeTelemetryObject); + }); } + } - addTelemetryObject(telemetryObject) { - this.addColumnsForObject(telemetryObject, true); - - const keyString = this.openmct.objects.makeKeyString(telemetryObject.identifier); - let requestOptions = this.buildOptionsFromConfiguration(telemetryObject); - let columnMap = this.getColumnMapForObject(keyString); - let limitEvaluator = this.openmct.telemetry.limitEvaluator(telemetryObject); - - const telemetryProcessor = this.getTelemetryProcessor(keyString, columnMap, limitEvaluator); - const telemetryRemover = this.getTelemetryRemover(); - - this.removeTelemetryCollection(keyString); + addTelemetryObject(telemetryObject) { + this.addColumnsForObject(telemetryObject, true); + + const keyString = this.openmct.objects.makeKeyString(telemetryObject.identifier); + let requestOptions = this.buildOptionsFromConfiguration(telemetryObject); + let columnMap = this.getColumnMapForObject(keyString); + let limitEvaluator = this.openmct.telemetry.limitEvaluator(telemetryObject); + + const telemetryProcessor = this.getTelemetryProcessor(keyString, columnMap, limitEvaluator); + const telemetryRemover = this.getTelemetryRemover(); + + this.removeTelemetryCollection(keyString); + + this.telemetryCollections[keyString] = this.openmct.telemetry.requestCollection( + telemetryObject, + requestOptions + ); + + this.telemetryCollections[keyString].on('requestStarted', this.incrementOutstandingRequests); + this.telemetryCollections[keyString].on('requestEnded', this.decrementOutstandingRequests); + this.telemetryCollections[keyString].on('remove', telemetryRemover); + this.telemetryCollections[keyString].on('add', telemetryProcessor); + this.telemetryCollections[keyString].on('clear', this.clearData); + this.telemetryCollections[keyString].load(); + + this.stalenessSubscription[keyString] = {}; + this.stalenessSubscription[keyString].stalenessUtils = new StalenessUtils.default( + this.openmct, + telemetryObject + ); + this.openmct.telemetry.isStale(telemetryObject).then((stalenessResponse) => { + if (stalenessResponse !== undefined) { + this.handleStaleness(keyString, stalenessResponse); + } + }); + const stalenessSubscription = this.openmct.telemetry.subscribeToStaleness( + telemetryObject, + (stalenessResponse) => { + this.handleStaleness(keyString, stalenessResponse); + } + ); - this.telemetryCollections[keyString] = this.openmct.telemetry.requestCollection( - telemetryObject, - requestOptions - ); + this.stalenessSubscription[keyString].unsubscribe = stalenessSubscription; - this.telemetryCollections[keyString].on('requestStarted', this.incrementOutstandingRequests); - this.telemetryCollections[keyString].on('requestEnded', this.decrementOutstandingRequests); - this.telemetryCollections[keyString].on('remove', telemetryRemover); - this.telemetryCollections[keyString].on('add', telemetryProcessor); - this.telemetryCollections[keyString].on('clear', this.clearData); - this.telemetryCollections[keyString].load(); - - this.stalenessSubscription[keyString] = {}; - this.stalenessSubscription[keyString].stalenessUtils = new StalenessUtils.default( - this.openmct, - telemetryObject - ); - this.openmct.telemetry.isStale(telemetryObject).then((stalenessResponse) => { - if (stalenessResponse !== undefined) { - this.handleStaleness(keyString, stalenessResponse); - } - }); - const stalenessSubscription = this.openmct.telemetry.subscribeToStaleness( - telemetryObject, - (stalenessResponse) => { - this.handleStaleness(keyString, stalenessResponse); - } - ); + this.telemetryObjects[keyString] = { + telemetryObject, + keyString, + requestOptions, + columnMap, + limitEvaluator + }; - this.stalenessSubscription[keyString].unsubscribe = stalenessSubscription; + this.emit('object-added', telemetryObject); + } - this.telemetryObjects[keyString] = { - telemetryObject, + handleStaleness(keyString, stalenessResponse, skipCheck = false) { + if ( + skipCheck || + this.stalenessSubscription[keyString].stalenessUtils.shouldUpdateStaleness( + stalenessResponse, + keyString + ) + ) { + this.emit('telemetry-staleness', { keyString, - requestOptions, - columnMap, - limitEvaluator - }; - - this.emit('object-added', telemetryObject); + isStale: stalenessResponse.isStale + }); } + } - handleStaleness(keyString, stalenessResponse, skipCheck = false) { - if ( - skipCheck || - this.stalenessSubscription[keyString].stalenessUtils.shouldUpdateStaleness( - stalenessResponse, - keyString - ) - ) { - this.emit('telemetry-staleness', { - keyString, - isStale: stalenessResponse.isStale - }); + getTelemetryProcessor(keyString, columnMap, limitEvaluator) { + return (telemetry) => { + //Check that telemetry object has not been removed since telemetry was requested. + if (!this.telemetryObjects[keyString]) { + return; } - } - - getTelemetryProcessor(keyString, columnMap, limitEvaluator) { - return (telemetry) => { - //Check that telemetry object has not been removed since telemetry was requested. - if (!this.telemetryObjects[keyString]) { - return; - } - const metadataValue = this.openmct.telemetry - .getMetadata(this.telemetryObjects[keyString].telemetryObject) - .getUseToUpdateInPlaceValue(); + const metadataValue = this.openmct.telemetry + .getMetadata(this.telemetryObjects[keyString].telemetryObject) + .getUseToUpdateInPlaceValue(); - let telemetryRows = telemetry.map( - (datum) => - new TelemetryTableRow(datum, columnMap, keyString, limitEvaluator, metadataValue?.key) - ); - - if (this.paused) { - this.delayedActions.push(this.tableRows.addRows.bind(this, telemetryRows, 'add')); - } else { - this.tableRows.addRows(telemetryRows); - } - }; - } + let telemetryRows = telemetry.map( + (datum) => + new TelemetryTableRow(datum, columnMap, keyString, limitEvaluator, metadataValue?.key) + ); - getTelemetryRemover() { - return (telemetry) => { - if (this.paused) { - this.delayedActions.push(this.tableRows.removeRowsByData.bind(this, telemetry)); - } else { - this.tableRows.removeRowsByData(telemetry); - } - }; - } + if (this.paused) { + this.delayedActions.push(this.tableRows.addRows.bind(this, telemetryRows, 'add')); + } else { + this.tableRows.addRows(telemetryRows); + } + }; + } - /** - * @private - */ - incrementOutstandingRequests() { - if (this.outstandingRequests === 0) { - this.emit('outstanding-requests', true); + getTelemetryRemover() { + return (telemetry) => { + if (this.paused) { + this.delayedActions.push(this.tableRows.removeRowsByData.bind(this, telemetry)); + } else { + this.tableRows.removeRowsByData(telemetry); } + }; + } - this.outstandingRequests++; + /** + * @private + */ + incrementOutstandingRequests() { + if (this.outstandingRequests === 0) { + this.emit('outstanding-requests', true); } - /** - * @private - */ - decrementOutstandingRequests() { - this.outstandingRequests--; + this.outstandingRequests++; + } - if (this.outstandingRequests === 0) { - this.emit('outstanding-requests', false); - } + /** + * @private + */ + decrementOutstandingRequests() { + this.outstandingRequests--; + + if (this.outstandingRequests === 0) { + this.emit('outstanding-requests', false); } + } - // will pull all necessary information for all existing bounded telemetry - // and pass to table row collection to reset without making any new requests - // triggered by filtering - resetRowsFromAllData() { - let allRows = []; + // will pull all necessary information for all existing bounded telemetry + // and pass to table row collection to reset without making any new requests + // triggered by filtering + resetRowsFromAllData() { + let allRows = []; - Object.keys(this.telemetryCollections).forEach((keyString) => { - let { columnMap, limitEvaluator } = this.telemetryObjects[keyString]; + Object.keys(this.telemetryCollections).forEach((keyString) => { + let { columnMap, limitEvaluator } = this.telemetryObjects[keyString]; - const metadataValue = this.openmct.telemetry - .getMetadata(this.telemetryObjects[keyString].telemetryObject) - .getUseToUpdateInPlaceValue(); + const metadataValue = this.openmct.telemetry + .getMetadata(this.telemetryObjects[keyString].telemetryObject) + .getUseToUpdateInPlaceValue(); - this.telemetryCollections[keyString].getAll().forEach((datum) => { - allRows.push( - new TelemetryTableRow(datum, columnMap, keyString, limitEvaluator, metadataValue?.key) - ); - }); + this.telemetryCollections[keyString].getAll().forEach((datum) => { + allRows.push( + new TelemetryTableRow(datum, columnMap, keyString, limitEvaluator, metadataValue?.key) + ); }); + }); - this.tableRows.clearRowsFromTableAndFilter(allRows); - } - - updateFilters(updatedFilters) { - let deepCopiedFilters = JSON.parse(JSON.stringify(updatedFilters)); - - if (this.filters && !_.isEqual(this.filters, deepCopiedFilters)) { - this.filters = deepCopiedFilters; - this.tableRows.clear(); - this.clearAndResubscribe(); - } else { - this.filters = deepCopiedFilters; - } - } + this.tableRows.clearRowsFromTableAndFilter(allRows); + } - clearAndResubscribe() { - let objectKeys = Object.keys(this.telemetryObjects); + updateFilters(updatedFilters) { + let deepCopiedFilters = JSON.parse(JSON.stringify(updatedFilters)); + if (this.filters && !_.isEqual(this.filters, deepCopiedFilters)) { + this.filters = deepCopiedFilters; this.tableRows.clear(); - objectKeys.forEach((keyString) => { - this.addTelemetryObject(this.telemetryObjects[keyString].telemetryObject); - }); + this.clearAndResubscribe(); + } else { + this.filters = deepCopiedFilters; } + } - removeTelemetryObject(objectIdentifier) { - const keyString = this.openmct.objects.makeKeyString(objectIdentifier); - const SKIP_CHECK = true; + clearAndResubscribe() { + let objectKeys = Object.keys(this.telemetryObjects); - this.configuration.removeColumnsForObject(objectIdentifier, true); - this.tableRows.removeRowsByObject(keyString); + this.tableRows.clear(); + objectKeys.forEach((keyString) => { + this.addTelemetryObject(this.telemetryObjects[keyString].telemetryObject); + }); + } - this.removeTelemetryCollection(keyString); - delete this.telemetryObjects[keyString]; + removeTelemetryObject(objectIdentifier) { + const keyString = this.openmct.objects.makeKeyString(objectIdentifier); + const SKIP_CHECK = true; - this.emit('object-removed', objectIdentifier); + this.configuration.removeColumnsForObject(objectIdentifier, true); + this.tableRows.removeRowsByObject(keyString); - this.stalenessSubscription[keyString].unsubscribe(); - this.stalenessSubscription[keyString].stalenessUtils.destroy(); - this.handleStaleness(keyString, { isStale: false }, SKIP_CHECK); - delete this.stalenessSubscription[keyString]; - } + this.removeTelemetryCollection(keyString); + delete this.telemetryObjects[keyString]; - clearData() { - this.tableRows.clear(); - this.emit('refresh'); - } + this.emit('object-removed', objectIdentifier); - addColumnsForObject(telemetryObject) { - const metadata = this.openmct.telemetry.getMetadata(telemetryObject); - let metadataValues = metadata.values(); - - this.addNameColumn(telemetryObject, metadataValues); - metadataValues.forEach((metadatum) => { - if (metadatum.key === 'name' || metadata.isInPlaceUpdateValue(metadatum)) { - return; - } - - let column = this.createColumn(metadatum); - this.configuration.addSingleColumnForObject(telemetryObject, column); - // add units column if available - if (metadatum.unit !== undefined) { - let unitColumn = this.createUnitColumn(metadatum); - this.configuration.addSingleColumnForObject(telemetryObject, unitColumn); - } - }); - } + this.stalenessSubscription[keyString].unsubscribe(); + this.stalenessSubscription[keyString].stalenessUtils.destroy(); + this.handleStaleness(keyString, { isStale: false }, SKIP_CHECK); + delete this.stalenessSubscription[keyString]; + } - getColumnMapForObject(objectKeyString) { - let columns = this.configuration.getColumns(); + clearData() { + this.tableRows.clear(); + this.emit('refresh'); + } - if (columns[objectKeyString]) { - return columns[objectKeyString].reduce((map, column) => { - map[column.getKey()] = column; + addColumnsForObject(telemetryObject) { + const metadata = this.openmct.telemetry.getMetadata(telemetryObject); + let metadataValues = metadata.values(); - return map; - }, {}); + this.addNameColumn(telemetryObject, metadataValues); + metadataValues.forEach((metadatum) => { + if (metadatum.key === 'name' || metadata.isInPlaceUpdateValue(metadatum)) { + return; } - return {}; - } + let column = this.createColumn(metadatum); + this.configuration.addSingleColumnForObject(telemetryObject, column); + // add units column if available + if (metadatum.unit !== undefined) { + let unitColumn = this.createUnitColumn(metadatum); + this.configuration.addSingleColumnForObject(telemetryObject, unitColumn); + } + }); + } - buildOptionsFromConfiguration(telemetryObject) { - let keyString = this.openmct.objects.makeKeyString(telemetryObject.identifier); - let filters = - this.domainObject.configuration && - this.domainObject.configuration.filters && - this.domainObject.configuration.filters[keyString]; + getColumnMapForObject(objectKeyString) { + let columns = this.configuration.getColumns(); - return { filters } || {}; - } + if (columns[objectKeyString]) { + return columns[objectKeyString].reduce((map, column) => { + map[column.getKey()] = column; - createColumn(metadatum) { - return new TelemetryTableColumn(this.openmct, metadatum); + return map; + }, {}); } - createUnitColumn(metadatum) { - return new TelemetryTableUnitColumn(this.openmct, metadatum); - } + return {}; + } - isTelemetryObject(domainObject) { - return Object.prototype.hasOwnProperty.call(domainObject, 'telemetry'); - } + buildOptionsFromConfiguration(telemetryObject) { + let keyString = this.openmct.objects.makeKeyString(telemetryObject.identifier); + let filters = + this.domainObject.configuration && + this.domainObject.configuration.filters && + this.domainObject.configuration.filters[keyString]; - sortBy(sortOptions) { - this.tableRows.sortBy(sortOptions); + return { filters } || {}; + } - if (this.openmct.editor.isEditing()) { - let configuration = this.configuration.getConfiguration(); - configuration.sortOptions = sortOptions; - this.configuration.updateConfiguration(configuration); - } - } + createColumn(metadatum) { + return new TelemetryTableColumn(this.openmct, metadatum); + } - runDelayedActions() { - this.delayedActions.forEach((action) => action()); - this.delayedActions = []; - } + createUnitColumn(metadatum) { + return new TelemetryTableUnitColumn(this.openmct, metadatum); + } - removeTelemetryCollection(keyString) { - if (this.telemetryCollections[keyString]) { - this.telemetryCollections[keyString].destroy(); - this.telemetryCollections[keyString] = undefined; - delete this.telemetryCollections[keyString]; - } - } + isTelemetryObject(domainObject) { + return Object.prototype.hasOwnProperty.call(domainObject, 'telemetry'); + } + + sortBy(sortOptions) { + this.tableRows.sortBy(sortOptions); - pause() { - this.paused = true; + if (this.openmct.editor.isEditing()) { + let configuration = this.configuration.getConfiguration(); + configuration.sortOptions = sortOptions; + this.configuration.updateConfiguration(configuration); } + } - unpause() { - this.paused = false; - this.runDelayedActions(); + runDelayedActions() { + this.delayedActions.forEach((action) => action()); + this.delayedActions = []; + } + + removeTelemetryCollection(keyString) { + if (this.telemetryCollections[keyString]) { + this.telemetryCollections[keyString].destroy(); + this.telemetryCollections[keyString] = undefined; + delete this.telemetryCollections[keyString]; } + } - destroy() { - this.tableRows.destroy(); + pause() { + this.paused = true; + } - this.tableRows.off('resetRowsFromAllData', this.resetRowsFromAllData); + unpause() { + this.paused = false; + this.runDelayedActions(); + } - let keystrings = Object.keys(this.telemetryCollections); - keystrings.forEach(this.removeTelemetryCollection); + destroy() { + this.tableRows.destroy(); - if (this.filterObserver) { - this.filterObserver(); - } + this.tableRows.off('resetRowsFromAllData', this.resetRowsFromAllData); - Object.values(this.stalenessSubscription).forEach((stalenessSubscription) => { - stalenessSubscription.unsubscribe(); - stalenessSubscription.stalenessUtils.destroy(); - }); + let keystrings = Object.keys(this.telemetryCollections); + keystrings.forEach(this.removeTelemetryCollection); - if (this.tableComposition !== undefined) { - this.tableComposition.off('add', this.addTelemetryObject); - this.tableComposition.off('remove', this.removeTelemetryObject); - } + if (this.filterObserver) { + this.filterObserver(); } - } - return TelemetryTable; -}); + Object.values(this.stalenessSubscription).forEach((stalenessSubscription) => { + stalenessSubscription.unsubscribe(); + stalenessSubscription.stalenessUtils.destroy(); + }); + + if (this.tableComposition !== undefined) { + this.tableComposition.off('add', this.addTelemetryObject); + this.tableComposition.off('remove', this.removeTelemetryObject); + } + } +} diff --git a/src/plugins/telemetryTable/TelemetryTableColumn.js b/src/plugins/telemetryTable/TelemetryTableColumn.js index dc72225e9ab..d374c45bdd8 100644 --- a/src/plugins/telemetryTable/TelemetryTableColumn.js +++ b/src/plugins/telemetryTable/TelemetryTableColumn.js @@ -19,48 +19,44 @@ * this source code distribution or the Licensing information page available * at runtime from the About dialog for additional information. *****************************************************************************/ -define(function () { - class TelemetryTableColumn { - constructor(openmct, metadatum, options = { selectable: false }) { - this.metadatum = metadatum; - this.formatter = openmct.telemetry.getValueFormatter(metadatum); - this.titleValue = this.metadatum.name; - this.selectable = options.selectable; - } - - getKey() { - return this.metadatum.key; - } +export default class TelemetryTableColumn { + constructor(openmct, metadatum, options = { selectable: false }) { + this.metadatum = metadatum; + this.formatter = openmct.telemetry.getValueFormatter(metadatum); + this.titleValue = this.metadatum.name; + this.selectable = options.selectable; + } - getTitle() { - return this.metadatum.name; - } + getKey() { + return this.metadatum.key; + } - getMetadatum() { - return this.metadatum; - } + getTitle() { + return this.metadatum.name; + } - hasValueForDatum(telemetryDatum) { - return Object.prototype.hasOwnProperty.call(telemetryDatum, this.metadatum.source); - } + getMetadatum() { + return this.metadatum; + } - getRawValue(telemetryDatum) { - return telemetryDatum[this.metadatum.source]; - } + hasValueForDatum(telemetryDatum) { + return Object.prototype.hasOwnProperty.call(telemetryDatum, this.metadatum.source); + } - getFormattedValue(telemetryDatum) { - let formattedValue = this.formatter.format(telemetryDatum); - if (formattedValue !== undefined && typeof formattedValue !== 'string') { - return formattedValue.toString(); - } else { - return formattedValue; - } - } + getRawValue(telemetryDatum) { + return telemetryDatum[this.metadatum.source]; + } - getParsedValue(telemetryDatum) { - return this.formatter.parse(telemetryDatum); + getFormattedValue(telemetryDatum) { + let formattedValue = this.formatter.format(telemetryDatum); + if (formattedValue !== undefined && typeof formattedValue !== 'string') { + return formattedValue.toString(); + } else { + return formattedValue; } } - return TelemetryTableColumn; -}); + getParsedValue(telemetryDatum) { + return this.formatter.parse(telemetryDatum); + } +} diff --git a/src/plugins/telemetryTable/TelemetryTableConfiguration.js b/src/plugins/telemetryTable/TelemetryTableConfiguration.js index d79af9fec59..043f71e45ce 100644 --- a/src/plugins/telemetryTable/TelemetryTableConfiguration.js +++ b/src/plugins/telemetryTable/TelemetryTableConfiguration.js @@ -20,149 +20,148 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define(['lodash', 'EventEmitter'], function (_, EventEmitter) { - class TelemetryTableConfiguration extends EventEmitter { - constructor(domainObject, openmct) { - super(); - - this.domainObject = domainObject; - this.openmct = openmct; - this.columns = {}; - - this.removeColumnsForObject = this.removeColumnsForObject.bind(this); - this.objectMutated = this.objectMutated.bind(this); - - this.unlistenFromMutation = openmct.objects.observe( - domainObject, - 'configuration', - this.objectMutated - ); - } +import EventEmitter from 'EventEmitter'; +import _ from 'lodash'; - getConfiguration() { - let configuration = this.domainObject.configuration || {}; - configuration.hiddenColumns = configuration.hiddenColumns || {}; - configuration.columnWidths = configuration.columnWidths || {}; - configuration.columnOrder = configuration.columnOrder || []; - configuration.cellFormat = configuration.cellFormat || {}; - configuration.autosize = configuration.autosize === undefined ? true : configuration.autosize; +export default class TelemetryTableConfiguration extends EventEmitter { + constructor(domainObject, openmct) { + super(); - return configuration; - } + this.domainObject = domainObject; + this.openmct = openmct; + this.columns = {}; - updateConfiguration(configuration) { - this.openmct.objects.mutate(this.domainObject, 'configuration', configuration); - } + this.removeColumnsForObject = this.removeColumnsForObject.bind(this); + this.objectMutated = this.objectMutated.bind(this); - /** - * @private - * @param {*} object - */ - objectMutated(configuration) { - if (configuration !== undefined) { - this.emit('change', configuration); - } - } + this.unlistenFromMutation = openmct.objects.observe( + domainObject, + 'configuration', + this.objectMutated + ); + } - addSingleColumnForObject(telemetryObject, column, position) { - let objectKeyString = this.openmct.objects.makeKeyString(telemetryObject.identifier); - this.columns[objectKeyString] = this.columns[objectKeyString] || []; - position = position || this.columns[objectKeyString].length; - this.columns[objectKeyString].splice(position, 0, column); - } + getConfiguration() { + let configuration = this.domainObject.configuration || {}; + configuration.hiddenColumns = configuration.hiddenColumns || {}; + configuration.columnWidths = configuration.columnWidths || {}; + configuration.columnOrder = configuration.columnOrder || []; + configuration.cellFormat = configuration.cellFormat || {}; + configuration.autosize = configuration.autosize === undefined ? true : configuration.autosize; - removeColumnsForObject(objectIdentifier) { - let objectKeyString = this.openmct.objects.makeKeyString(objectIdentifier); - let columnsToRemove = this.columns[objectKeyString]; - - delete this.columns[objectKeyString]; - - let configuration = this.domainObject.configuration; - let configurationChanged = false; - columnsToRemove.forEach((column) => { - //There may be more than one column with the same key (eg. time system columns) - if (!this.hasColumnWithKey(column.getKey())) { - delete configuration.hiddenColumns[column.getKey()]; - configurationChanged = true; - } - }); - if (configurationChanged) { - this.updateConfiguration(configuration); - } - } + return configuration; + } - hasColumnWithKey(columnKey) { - return _.flatten(Object.values(this.columns)).some((column) => column.getKey() === columnKey); - } + updateConfiguration(configuration) { + this.openmct.objects.mutate(this.domainObject, 'configuration', configuration); + } - getColumns() { - return this.columns; + /** + * @private + * @param {*} object + */ + objectMutated(configuration) { + if (configuration !== undefined) { + this.emit('change', configuration); } + } + + addSingleColumnForObject(telemetryObject, column, position) { + let objectKeyString = this.openmct.objects.makeKeyString(telemetryObject.identifier); + this.columns[objectKeyString] = this.columns[objectKeyString] || []; + position = position || this.columns[objectKeyString].length; + this.columns[objectKeyString].splice(position, 0, column); + } - getAllHeaders() { - let flattenedColumns = _.flatten(Object.values(this.columns)); - /* eslint-disable you-dont-need-lodash-underscore/uniq */ - let headers = _.uniq(flattenedColumns, false, (column) => column.getKey()).reduce( - fromColumnsToHeadersMap, - {} - ); - /* eslint-enable you-dont-need-lodash-underscore/uniq */ - function fromColumnsToHeadersMap(headersMap, column) { - headersMap[column.getKey()] = column.getTitle(); - - return headersMap; + removeColumnsForObject(objectIdentifier) { + let objectKeyString = this.openmct.objects.makeKeyString(objectIdentifier); + let columnsToRemove = this.columns[objectKeyString]; + + delete this.columns[objectKeyString]; + + let configuration = this.domainObject.configuration; + let configurationChanged = false; + columnsToRemove.forEach((column) => { + //There may be more than one column with the same key (eg. time system columns) + if (!this.hasColumnWithKey(column.getKey())) { + delete configuration.hiddenColumns[column.getKey()]; + configurationChanged = true; } + }); + if (configurationChanged) { + this.updateConfiguration(configuration); + } + } + + hasColumnWithKey(columnKey) { + return _.flatten(Object.values(this.columns)).some((column) => column.getKey() === columnKey); + } + + getColumns() { + return this.columns; + } - return headers; + getAllHeaders() { + let flattenedColumns = _.flatten(Object.values(this.columns)); + /* eslint-disable you-dont-need-lodash-underscore/uniq */ + let headers = _.uniq(flattenedColumns, false, (column) => column.getKey()).reduce( + fromColumnsToHeadersMap, + {} + ); + /* eslint-enable you-dont-need-lodash-underscore/uniq */ + function fromColumnsToHeadersMap(headersMap, column) { + headersMap[column.getKey()] = column.getTitle(); + + return headersMap; } - getVisibleHeaders() { - let allHeaders = this.getAllHeaders(); - let configuration = this.getConfiguration(); + return headers; + } - let orderedColumns = this.getColumnOrder(); - let unorderedColumns = _.difference(Object.keys(allHeaders), orderedColumns); + getVisibleHeaders() { + let allHeaders = this.getAllHeaders(); + let configuration = this.getConfiguration(); - return orderedColumns - .concat(unorderedColumns) - .filter((headerKey) => { - return configuration.hiddenColumns[headerKey] !== true; - }) - .reduce((headers, headerKey) => { - headers[headerKey] = allHeaders[headerKey]; + let orderedColumns = this.getColumnOrder(); + let unorderedColumns = _.difference(Object.keys(allHeaders), orderedColumns); - return headers; - }, {}); - } + return orderedColumns + .concat(unorderedColumns) + .filter((headerKey) => { + return configuration.hiddenColumns[headerKey] !== true; + }) + .reduce((headers, headerKey) => { + headers[headerKey] = allHeaders[headerKey]; - getColumnWidths() { - let configuration = this.getConfiguration(); + return headers; + }, {}); + } - return configuration.columnWidths; - } + getColumnWidths() { + let configuration = this.getConfiguration(); - setColumnWidths(columnWidths) { - let configuration = this.getConfiguration(); - configuration.columnWidths = columnWidths; - this.updateConfiguration(configuration); - } + return configuration.columnWidths; + } - getColumnOrder() { - let configuration = this.getConfiguration(); + setColumnWidths(columnWidths) { + let configuration = this.getConfiguration(); + configuration.columnWidths = columnWidths; + this.updateConfiguration(configuration); + } - return configuration.columnOrder; - } + getColumnOrder() { + let configuration = this.getConfiguration(); - setColumnOrder(columnOrder) { - let configuration = this.getConfiguration(); - configuration.columnOrder = columnOrder; - this.updateConfiguration(configuration); - } + return configuration.columnOrder; + } - destroy() { - this.unlistenFromMutation(); - } + setColumnOrder(columnOrder) { + let configuration = this.getConfiguration(); + configuration.columnOrder = columnOrder; + this.updateConfiguration(configuration); } - return TelemetryTableConfiguration; -}); + destroy() { + this.unlistenFromMutation(); + } +} diff --git a/src/plugins/telemetryTable/TelemetryTableNameColumn.js b/src/plugins/telemetryTable/TelemetryTableNameColumn.js index 0ae1ba352db..2c3bdf0473e 100644 --- a/src/plugins/telemetryTable/TelemetryTableNameColumn.js +++ b/src/plugins/telemetryTable/TelemetryTableNameColumn.js @@ -19,22 +19,21 @@ * this source code distribution or the Licensing information page available * at runtime from the About dialog for additional information. *****************************************************************************/ -define(['./TelemetryTableColumn.js'], function (TelemetryTableColumn) { - class TelemetryTableNameColumn extends TelemetryTableColumn { - constructor(openmct, telemetryObject, metadatum) { - super(openmct, metadatum); - this.telemetryObject = telemetryObject; - } +import TelemetryTableColumn from './TelemetryTableColumn'; - getRawValue() { - return this.telemetryObject.name; - } +export default class TelemetryTableNameColumn extends TelemetryTableColumn { + constructor(openmct, telemetryObject, metadatum) { + super(openmct, metadatum); - getFormattedValue() { - return this.telemetryObject.name; - } + this.telemetryObject = telemetryObject; } - return TelemetryTableNameColumn; -}); + getRawValue() { + return this.telemetryObject.name; + } + + getFormattedValue() { + return this.telemetryObject.name; + } +} diff --git a/src/plugins/telemetryTable/TelemetryTableRow.js b/src/plugins/telemetryTable/TelemetryTableRow.js index 1f25f178a09..fc31621585a 100644 --- a/src/plugins/telemetryTable/TelemetryTableRow.js +++ b/src/plugins/telemetryTable/TelemetryTableRow.js @@ -20,108 +20,104 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define([], function () { - class TelemetryTableRow { - constructor(datum, columns, objectKeyString, limitEvaluator, inPlaceUpdateKey) { - this.columns = columns; - - this.datum = createNormalizedDatum(datum, columns); - this.fullDatum = datum; - this.limitEvaluator = limitEvaluator; - this.objectKeyString = objectKeyString; - this.inPlaceUpdateKey = inPlaceUpdateKey; - } - - getFormattedDatum(headers) { - return Object.keys(headers).reduce((formattedDatum, columnKey) => { - formattedDatum[columnKey] = this.getFormattedValue(columnKey); +export default class TelemetryTableRow { + constructor(datum, columns, objectKeyString, limitEvaluator, inPlaceUpdateKey) { + this.columns = columns; + + this.datum = createNormalizedDatum(datum, columns); + this.fullDatum = datum; + this.limitEvaluator = limitEvaluator; + this.objectKeyString = objectKeyString; + this.inPlaceUpdateKey = inPlaceUpdateKey; + } - return formattedDatum; - }, {}); - } + getFormattedDatum(headers) { + return Object.keys(headers).reduce((formattedDatum, columnKey) => { + formattedDatum[columnKey] = this.getFormattedValue(columnKey); - getFormattedValue(key) { - let column = this.columns[key]; + return formattedDatum; + }, {}); + } - return column && column.getFormattedValue(this.datum[key]); - } + getFormattedValue(key) { + let column = this.columns[key]; - getParsedValue(key) { - let column = this.columns[key]; + return column && column.getFormattedValue(this.datum[key]); + } - return column && column.getParsedValue(this.datum[key]); - } + getParsedValue(key) { + let column = this.columns[key]; - getCellComponentName(key) { - let column = this.columns[key]; + return column && column.getParsedValue(this.datum[key]); + } - return column && column.getCellComponentName && column.getCellComponentName(); - } + getCellComponentName(key) { + let column = this.columns[key]; - getRowClass() { - if (!this.rowClass) { - let limitEvaluation = this.limitEvaluator.evaluate(this.datum); - this.rowClass = limitEvaluation && limitEvaluation.cssClass; - } + return column && column.getCellComponentName && column.getCellComponentName(); + } - return this.rowClass; + getRowClass() { + if (!this.rowClass) { + let limitEvaluation = this.limitEvaluator.evaluate(this.datum); + this.rowClass = limitEvaluation && limitEvaluation.cssClass; } - getCellLimitClasses() { - if (!this.cellLimitClasses) { - this.cellLimitClasses = Object.values(this.columns).reduce((alarmStateMap, column) => { - if (!column.isUnit) { - let limitEvaluation = this.limitEvaluator.evaluate(this.datum, column.getMetadatum()); - alarmStateMap[column.getKey()] = limitEvaluation && limitEvaluation.cssClass; - } + return this.rowClass; + } - return alarmStateMap; - }, {}); - } + getCellLimitClasses() { + if (!this.cellLimitClasses) { + this.cellLimitClasses = Object.values(this.columns).reduce((alarmStateMap, column) => { + if (!column.isUnit) { + let limitEvaluation = this.limitEvaluator.evaluate(this.datum, column.getMetadatum()); + alarmStateMap[column.getKey()] = limitEvaluation && limitEvaluation.cssClass; + } - return this.cellLimitClasses; + return alarmStateMap; + }, {}); } - getContextualDomainObject(openmct, objectKeyString) { - return openmct.objects.get(objectKeyString); - } + return this.cellLimitClasses; + } - getContextMenuActions() { - return ['viewDatumAction', 'viewHistoricalData']; - } + getContextualDomainObject(openmct, objectKeyString) { + return openmct.objects.get(objectKeyString); + } - updateWithDatum(updatesToDatum) { - const normalizedUpdatesToDatum = createNormalizedDatum(updatesToDatum, this.columns); - this.datum = { - ...this.datum, - ...normalizedUpdatesToDatum - }; - this.fullDatum = { - ...this.fullDatum, - ...updatesToDatum - }; - } + getContextMenuActions() { + return ['viewDatumAction', 'viewHistoricalData']; } - /** - * Normalize the structure of datums to assist sorting and merging of columns. - * Maps all sources to keys. - * @private - * @param {*} telemetryDatum - * @param {*} metadataValues - */ - function createNormalizedDatum(datum, columns) { - const normalizedDatum = JSON.parse(JSON.stringify(datum)); - - Object.values(columns).forEach((column) => { - const rawValue = column.getRawValue(datum); - if (rawValue !== undefined) { - normalizedDatum[column.getKey()] = rawValue; - } - }); - - return normalizedDatum; + updateWithDatum(updatesToDatum) { + const normalizedUpdatesToDatum = createNormalizedDatum(updatesToDatum, this.columns); + this.datum = { + ...this.datum, + ...normalizedUpdatesToDatum + }; + this.fullDatum = { + ...this.fullDatum, + ...updatesToDatum + }; } +} + +/** + * Normalize the structure of datums to assist sorting and merging of columns. + * Maps all sources to keys. + * @private + * @param {*} telemetryDatum + * @param {*} metadataValues + */ +function createNormalizedDatum(datum, columns) { + const normalizedDatum = JSON.parse(JSON.stringify(datum)); + + Object.values(columns).forEach((column) => { + const rawValue = column.getRawValue(datum); + if (rawValue !== undefined) { + normalizedDatum[column.getKey()] = rawValue; + } + }); - return TelemetryTableRow; -}); + return normalizedDatum; +} diff --git a/src/plugins/telemetryTable/TelemetryTableType.js b/src/plugins/telemetryTable/TelemetryTableType.js index 0087db26058..6ad6df6791b 100644 --- a/src/plugins/telemetryTable/TelemetryTableType.js +++ b/src/plugins/telemetryTable/TelemetryTableType.js @@ -20,19 +20,17 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define(function () { - return { - name: 'Telemetry Table', - description: - 'Display values for one or more telemetry end points in a scrolling table. Each row is a time-stamped value.', - creatable: true, - cssClass: 'icon-tabular-scrolling', - initialize(domainObject) { - domainObject.composition = []; - domainObject.configuration = { - columnWidths: {}, - hiddenColumns: {} - }; - } - }; -}); +export default { + name: 'Telemetry Table', + description: + 'Display values for one or more telemetry end points in a scrolling table. Each row is a time-stamped value.', + creatable: true, + cssClass: 'icon-tabular-scrolling', + initialize(domainObject) { + domainObject.composition = []; + domainObject.configuration = { + columnWidths: {}, + hiddenColumns: {} + }; + } +}; diff --git a/src/plugins/telemetryTable/TelemetryTableUnitColumn.js b/src/plugins/telemetryTable/TelemetryTableUnitColumn.js index 2f89b6b9d83..d5aa461b9fd 100644 --- a/src/plugins/telemetryTable/TelemetryTableUnitColumn.js +++ b/src/plugins/telemetryTable/TelemetryTableUnitColumn.js @@ -19,38 +19,38 @@ * this source code distribution or the Licensing information page available * at runtime from the About dialog for additional information. *****************************************************************************/ -define(['./TelemetryTableColumn.js'], function (TelemetryTableColumn) { - class TelemetryTableUnitColumn extends TelemetryTableColumn { - constructor(openmct, metadatum) { - super(openmct, metadatum); - this.isUnit = true; - this.titleValue += ' Unit'; - this.formatter = { - format: (datum) => { - return this.metadatum.unit; - }, - parse: (datum) => { - return this.metadatum.unit; - } - }; - } +import TelemetryTableColumn from './TelemetryTableColumn.js'; - getKey() { - return this.metadatum.key + '-unit'; - } +class TelemetryTableUnitColumn extends TelemetryTableColumn { + constructor(openmct, metadatum) { + super(openmct, metadatum); + this.isUnit = true; + this.titleValue += ' Unit'; + this.formatter = { + format: (datum) => { + return this.metadatum.unit; + }, + parse: (datum) => { + return this.metadatum.unit; + } + }; + } - getTitle() { - return this.metadatum.name + ' Unit'; - } + getKey() { + return this.metadatum.key + '-unit'; + } - getRawValue(telemetryDatum) { - return this.metadatum.unit; - } + getTitle() { + return this.metadatum.name + ' Unit'; + } + + getRawValue(telemetryDatum) { + return this.metadatum.unit; + } - getFormattedValue(telemetryDatum) { - return this.formatter.format(telemetryDatum); - } + getFormattedValue(telemetryDatum) { + return this.formatter.format(telemetryDatum); } +} - return TelemetryTableUnitColumn; -}); +export default TelemetryTableUnitColumn; diff --git a/src/plugins/telemetryTable/collections/TableRowCollection.js b/src/plugins/telemetryTable/collections/TableRowCollection.js index a803bb1f1e3..8e465447cff 100644 --- a/src/plugins/telemetryTable/collections/TableRowCollection.js +++ b/src/plugins/telemetryTable/collections/TableRowCollection.js @@ -19,370 +19,368 @@ * this source code distribution or the Licensing information page available * at runtime from the About dialog for additional information. *****************************************************************************/ +import EventEmitter from 'EventEmitter'; +import _ from 'lodash'; + +/** + * @constructor + */ +export default class TableRowCollection extends EventEmitter { + constructor() { + super(); + + this.rows = []; + this.columnFilters = {}; + this.addRows = this.addRows.bind(this); + this.removeRowsByObject = this.removeRowsByObject.bind(this); + this.removeRowsByData = this.removeRowsByData.bind(this); + + this.clear = this.clear.bind(this); + } -define(['lodash', 'EventEmitter'], function (_, EventEmitter) { - /** - * @constructor - */ - class TableRowCollection extends EventEmitter { - constructor() { - super(); - - this.rows = []; - this.columnFilters = {}; - this.addRows = this.addRows.bind(this); - this.removeRowsByObject = this.removeRowsByObject.bind(this); - this.removeRowsByData = this.removeRowsByData.bind(this); - - this.clear = this.clear.bind(this); - } - - removeRowsByObject(keyString) { - let removed = []; + removeRowsByObject(keyString) { + let removed = []; - this.rows = this.rows.filter((row) => { - if (row.objectKeyString === keyString) { - removed.push(row); + this.rows = this.rows.filter((row) => { + if (row.objectKeyString === keyString) { + removed.push(row); - return false; - } else { - return true; - } - }); + return false; + } else { + return true; + } + }); - this.emit('remove', removed); - } + this.emit('remove', removed); + } - addRows(rows) { - let rowsToAdd = this.filterRows(rows); + addRows(rows) { + let rowsToAdd = this.filterRows(rows); - this.sortAndMergeRows(rowsToAdd); + this.sortAndMergeRows(rowsToAdd); - // we emit filter no matter what to trigger - // an update of visible rows - if (rowsToAdd.length > 0) { - this.emit('add', rowsToAdd); - } + // we emit filter no matter what to trigger + // an update of visible rows + if (rowsToAdd.length > 0) { + this.emit('add', rowsToAdd); } + } - clearRowsFromTableAndFilter(rows) { - let rowsToAdd = this.filterRows(rows); - // Reset of all rows, need to wipe current rows - this.rows = []; + clearRowsFromTableAndFilter(rows) { + let rowsToAdd = this.filterRows(rows); + // Reset of all rows, need to wipe current rows + this.rows = []; - this.sortAndMergeRows(rowsToAdd); + this.sortAndMergeRows(rowsToAdd); - // We emit filter and update of visible rows - this.emit('filter', rowsToAdd); - } - - filterRows(rows) { - if (Object.keys(this.columnFilters).length > 0) { - return rows.filter(this.matchesFilters, this); - } + // We emit filter and update of visible rows + this.emit('filter', rowsToAdd); + } - return rows; + filterRows(rows) { + if (Object.keys(this.columnFilters).length > 0) { + return rows.filter(this.matchesFilters, this); } - sortAndMergeRows(rows) { - const sortedRows = this.sortCollection(rows); - - if (this.rows.length === 0) { - this.rows = sortedRows; + return rows; + } - return; - } + sortAndMergeRows(rows) { + const sortedRows = this.sortCollection(rows); - const firstIncomingRow = sortedRows[0]; - const lastIncomingRow = sortedRows[sortedRows.length - 1]; - const firstExistingRow = this.rows[0]; - const lastExistingRow = this.rows[this.rows.length - 1]; + if (this.rows.length === 0) { + this.rows = sortedRows; - if (this.firstRowInSortOrder(lastIncomingRow, firstExistingRow) === lastIncomingRow) { - this.insertOrUpdateRows(sortedRows, true); - } else if (this.firstRowInSortOrder(lastExistingRow, firstIncomingRow) === lastExistingRow) { - this.insertOrUpdateRows(sortedRows, false); - } else { - this.mergeSortedRows(sortedRows); - } + return; } - getInPlaceUpdateIndex(row) { - const inPlaceUpdateKey = row.inPlaceUpdateKey; - if (!inPlaceUpdateKey) { - return -1; - } - - const foundIndex = this.rows.findIndex( - (existingRow) => - existingRow.datum[inPlaceUpdateKey] && - existingRow.datum[inPlaceUpdateKey] === row.datum[inPlaceUpdateKey] - ); - - return foundIndex; + const firstIncomingRow = sortedRows[0]; + const lastIncomingRow = sortedRows[sortedRows.length - 1]; + const firstExistingRow = this.rows[0]; + const lastExistingRow = this.rows[this.rows.length - 1]; + + if (this.firstRowInSortOrder(lastIncomingRow, firstExistingRow) === lastIncomingRow) { + this.insertOrUpdateRows(sortedRows, true); + } else if (this.firstRowInSortOrder(lastExistingRow, firstIncomingRow) === lastExistingRow) { + this.insertOrUpdateRows(sortedRows, false); + } else { + this.mergeSortedRows(sortedRows); } + } - updateRowInPlace(row, index) { - const foundRow = this.rows[index]; - foundRow.updateWithDatum(row.datum); - this.rows[index] = foundRow; + getInPlaceUpdateIndex(row) { + const inPlaceUpdateKey = row.inPlaceUpdateKey; + if (!inPlaceUpdateKey) { + return -1; } - sortCollection(rows) { - const sortedRows = _.orderBy( - rows, - (row) => row.getParsedValue(this.sortOptions.key), - this.sortOptions.direction - ); + const foundIndex = this.rows.findIndex( + (existingRow) => + existingRow.datum[inPlaceUpdateKey] && + existingRow.datum[inPlaceUpdateKey] === row.datum[inPlaceUpdateKey] + ); - return sortedRows; - } + return foundIndex; + } - insertOrUpdateRows(rowsToAdd, addToBeginning) { - rowsToAdd.forEach((row) => { - const index = this.getInPlaceUpdateIndex(row); - if (index > -1) { - this.updateRowInPlace(row, index); - } else { - if (addToBeginning) { - this.rows.unshift(row); - } else { - this.rows.push(row); - } - } - }); - } + updateRowInPlace(row, index) { + const foundRow = this.rows[index]; + foundRow.updateWithDatum(row.datum); + this.rows[index] = foundRow; + } - mergeSortedRows(rows) { - const mergedRows = []; - let i = 0; - let j = 0; + sortCollection(rows) { + const sortedRows = _.orderBy( + rows, + (row) => row.getParsedValue(this.sortOptions.key), + this.sortOptions.direction + ); - while (i < this.rows.length && j < rows.length) { - const existingRow = this.rows[i]; - const incomingRow = rows[j]; + return sortedRows; + } - const index = this.getInPlaceUpdateIndex(incomingRow); - if (index > -1) { - this.updateRowInPlace(incomingRow, index); + insertOrUpdateRows(rowsToAdd, addToBeginning) { + rowsToAdd.forEach((row) => { + const index = this.getInPlaceUpdateIndex(row); + if (index > -1) { + this.updateRowInPlace(row, index); + } else { + if (addToBeginning) { + this.rows.unshift(row); } else { - if (this.firstRowInSortOrder(existingRow, incomingRow) === existingRow) { - mergedRows.push(existingRow); - i++; - } else { - mergedRows.push(incomingRow); - j++; - } + this.rows.push(row); } } + }); + } - // tail of existing rows is all that is left to merge - if (i < this.rows.length) { - for (i; i < this.rows.length; i++) { - mergedRows.push(this.rows[i]); - } - } + mergeSortedRows(rows) { + const mergedRows = []; + let i = 0; + let j = 0; + + while (i < this.rows.length && j < rows.length) { + const existingRow = this.rows[i]; + const incomingRow = rows[j]; - // tail of incoming rows is all that is left to merge - if (j < rows.length) { - for (j; j < rows.length; j++) { - mergedRows.push(rows[j]); + const index = this.getInPlaceUpdateIndex(incomingRow); + if (index > -1) { + this.updateRowInPlace(incomingRow, index); + } else { + if (this.firstRowInSortOrder(existingRow, incomingRow) === existingRow) { + mergedRows.push(existingRow); + i++; + } else { + mergedRows.push(incomingRow); + j++; } } - - this.rows = mergedRows; } - firstRowInSortOrder(row1, row2) { - const val1 = this.getValueForSortColumn(row1); - const val2 = this.getValueForSortColumn(row2); + // tail of existing rows is all that is left to merge + if (i < this.rows.length) { + for (i; i < this.rows.length; i++) { + mergedRows.push(this.rows[i]); + } + } - if (this.sortOptions.direction === 'asc') { - return val1 <= val2 ? row1 : row2; - } else { - return val1 >= val2 ? row1 : row2; + // tail of incoming rows is all that is left to merge + if (j < rows.length) { + for (j; j < rows.length; j++) { + mergedRows.push(rows[j]); } } - removeRowsByData(data) { - let removed = []; + this.rows = mergedRows; + } - this.rows = this.rows.filter((row) => { - if (data.includes(row.fullDatum)) { - removed.push(row); + firstRowInSortOrder(row1, row2) { + const val1 = this.getValueForSortColumn(row1); + const val2 = this.getValueForSortColumn(row2); - return false; - } else { - return true; - } - }); - - this.emit('remove', removed); + if (this.sortOptions.direction === 'asc') { + return val1 <= val2 ? row1 : row2; + } else { + return val1 >= val2 ? row1 : row2; } + } - /** - * Sorts the telemetry collection based on the provided sort field - * specifier. Subsequent inserts are sorted to maintain specified sport - * order. - * - * @example - * // First build some mock telemetry for the purpose of an example - * let now = Date.now(); - * let telemetry = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(function (value) { - * return { - * // define an object property to demonstrate nested paths - * timestamp: { - * ms: now - value * 1000, - * text: - * }, - * value: value - * } - * }); - * let collection = new TelemetryCollection(); - * - * collection.add(telemetry); - * - * // Sort by telemetry value - * collection.sortBy({ - * key: 'value', direction: 'asc' - * }); - * - * // Sort by ms since epoch - * collection.sort({ - * key: 'timestamp.ms', - * direction: 'asc' - * }); - * - * // Sort by 'text' attribute, descending - * collection.sort("timestamp.text"); - * - * - * @param {object} sortOptions An object specifying a sort key, and direction. - */ - sortBy(sortOptions) { - if (arguments.length > 0) { - this.sortOptions = sortOptions; - this.rows = _.orderBy( - this.rows, - (row) => row.getParsedValue(sortOptions.key), - sortOptions.direction - ); - this.emit('sort'); - } + removeRowsByData(data) { + let removed = []; - // Return duplicate to avoid direct modification of underlying object - return Object.assign({}, this.sortOptions); - } + this.rows = this.rows.filter((row) => { + if (data.includes(row.fullDatum)) { + removed.push(row); - setColumnFilter(columnKey, filter) { - filter = filter.trim().toLowerCase(); - let wasBlank = this.columnFilters[columnKey] === undefined; - let isSubset = this.isSubsetOfCurrentFilter(columnKey, filter); - - if (filter.length === 0) { - delete this.columnFilters[columnKey]; + return false; } else { - this.columnFilters[columnKey] = filter; + return true; } + }); - if (isSubset || wasBlank) { - this.rows = this.rows.filter(this.matchesFilters, this); - this.emit('filter'); - } else { - this.emit('resetRowsFromAllData'); - } + this.emit('remove', removed); + } + + /** + * Sorts the telemetry collection based on the provided sort field + * specifier. Subsequent inserts are sorted to maintain specified sport + * order. + * + * @example + * // First build some mock telemetry for the purpose of an example + * let now = Date.now(); + * let telemetry = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(function (value) { + * return { + * // define an object property to demonstrate nested paths + * timestamp: { + * ms: now - value * 1000, + * text: + * }, + * value: value + * } + * }); + * let collection = new TelemetryCollection(); + * + * collection.add(telemetry); + * + * // Sort by telemetry value + * collection.sortBy({ + * key: 'value', direction: 'asc' + * }); + * + * // Sort by ms since epoch + * collection.sort({ + * key: 'timestamp.ms', + * direction: 'asc' + * }); + * + * // Sort by 'text' attribute, descending + * collection.sort("timestamp.text"); + * + * + * @param {object} sortOptions An object specifying a sort key, and direction. + */ + sortBy(sortOptions) { + if (arguments.length > 0) { + this.sortOptions = sortOptions; + this.rows = _.orderBy( + this.rows, + (row) => row.getParsedValue(sortOptions.key), + sortOptions.direction + ); + this.emit('sort'); } - setColumnRegexFilter(columnKey, filter) { - filter = filter.trim(); - this.columnFilters[columnKey] = new RegExp(filter); + // Return duplicate to avoid direct modification of underlying object + return Object.assign({}, this.sortOptions); + } + + setColumnFilter(columnKey, filter) { + filter = filter.trim().toLowerCase(); + let wasBlank = this.columnFilters[columnKey] === undefined; + let isSubset = this.isSubsetOfCurrentFilter(columnKey, filter); + + if (filter.length === 0) { + delete this.columnFilters[columnKey]; + } else { + this.columnFilters[columnKey] = filter; + } + if (isSubset || wasBlank) { + this.rows = this.rows.filter(this.matchesFilters, this); + this.emit('filter'); + } else { this.emit('resetRowsFromAllData'); } + } - getColumnMapForObject(objectKeyString) { - let columns = this.configuration.getColumns(); + setColumnRegexFilter(columnKey, filter) { + filter = filter.trim(); + this.columnFilters[columnKey] = new RegExp(filter); - if (columns[objectKeyString]) { - return columns[objectKeyString].reduce((map, column) => { - map[column.getKey()] = column; + this.emit('resetRowsFromAllData'); + } - return map; - }, {}); - } + getColumnMapForObject(objectKeyString) { + let columns = this.configuration.getColumns(); - return {}; + if (columns[objectKeyString]) { + return columns[objectKeyString].reduce((map, column) => { + map[column.getKey()] = column; + + return map; + }, {}); } - // /** - // * @private - // */ - isSubsetOfCurrentFilter(columnKey, filter) { - if (this.columnFilters[columnKey] instanceof RegExp) { - return false; - } + return {}; + } - return ( - this.columnFilters[columnKey] && - filter.startsWith(this.columnFilters[columnKey]) && - // startsWith check will otherwise fail when filter cleared - // because anyString.startsWith('') === true - filter !== '' - ); + // /** + // * @private + // */ + isSubsetOfCurrentFilter(columnKey, filter) { + if (this.columnFilters[columnKey] instanceof RegExp) { + return false; } - /** - * @private - */ - matchesFilters(row) { - let doesMatchFilters = true; - Object.keys(this.columnFilters).forEach((key) => { - if (!doesMatchFilters || !this.rowHasColumn(row, key)) { - return false; - } + return ( + this.columnFilters[columnKey] && + filter.startsWith(this.columnFilters[columnKey]) && + // startsWith check will otherwise fail when filter cleared + // because anyString.startsWith('') === true + filter !== '' + ); + } - let formattedValue = row.getFormattedValue(key); - if (formattedValue === undefined) { - return false; - } + /** + * @private + */ + matchesFilters(row) { + let doesMatchFilters = true; + Object.keys(this.columnFilters).forEach((key) => { + if (!doesMatchFilters || !this.rowHasColumn(row, key)) { + return false; + } - if (this.columnFilters[key] instanceof RegExp) { - doesMatchFilters = this.columnFilters[key].test(formattedValue); - } else { - doesMatchFilters = formattedValue.toLowerCase().indexOf(this.columnFilters[key]) !== -1; - } - }); + let formattedValue = row.getFormattedValue(key); + if (formattedValue === undefined) { + return false; + } - return doesMatchFilters; - } + if (this.columnFilters[key] instanceof RegExp) { + doesMatchFilters = this.columnFilters[key].test(formattedValue); + } else { + doesMatchFilters = formattedValue.toLowerCase().indexOf(this.columnFilters[key]) !== -1; + } + }); - rowHasColumn(row, key) { - return Object.prototype.hasOwnProperty.call(row.columns, key); - } + return doesMatchFilters; + } - getRows() { - return this.rows; - } + rowHasColumn(row, key) { + return Object.prototype.hasOwnProperty.call(row.columns, key); + } - getRowsLength() { - return this.rows.length; - } + getRows() { + return this.rows; + } - getValueForSortColumn(row) { - return row.getParsedValue(this.sortOptions.key); - } + getRowsLength() { + return this.rows.length; + } - clear() { - let removedRows = this.rows; - this.rows = []; + getValueForSortColumn(row) { + return row.getParsedValue(this.sortOptions.key); + } - this.emit('remove', removedRows); - } + clear() { + let removedRows = this.rows; + this.rows = []; - destroy() { - this.removeAllListeners(); - } + this.emit('remove', removedRows); } - return TableRowCollection; -}); + destroy() { + this.removeAllListeners(); + } +} diff --git a/src/plugins/utcTimeSystem/UTCTimeSystem.js b/src/plugins/utcTimeSystem/UTCTimeSystem.js index 50029481525..ca2a317c2f5 100644 --- a/src/plugins/utcTimeSystem/UTCTimeSystem.js +++ b/src/plugins/utcTimeSystem/UTCTimeSystem.js @@ -20,17 +20,17 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define([], function () { +/** + * This time system supports UTC dates. + * @implements TimeSystem + * @constructor + */ +class UTCTimeSystem { /** - * This time system supports UTC dates. - * @implements TimeSystem - * @constructor + * Metadata used to identify the time system in + * the UI */ - function UTCTimeSystem() { - /** - * Metadata used to identify the time system in - * the UI - */ + constructor() { this.key = 'utc'; this.name = 'UTC'; this.cssClass = 'icon-clock'; @@ -38,6 +38,6 @@ define([], function () { this.durationFormat = 'duration'; this.isUTCBased = true; } +} - return UTCTimeSystem; -}); +export default UTCTimeSystem; diff --git a/src/ui/registries/ToolbarRegistry.js b/src/ui/registries/ToolbarRegistry.js index aab0080f517..30a2a7c80eb 100644 --- a/src/ui/registries/ToolbarRegistry.js +++ b/src/ui/registries/ToolbarRegistry.js @@ -20,102 +20,68 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define([], function () { - /** - * A ToolbarRegistry maintains the definitions for toolbars. - * - * @interface ToolbarRegistry - * @memberof module:openmct - */ - function ToolbarRegistry() { - this.providers = {}; - } - - /** - * Gets toolbar controls from providers which can provide a toolbar for this selection. - * - * @param {object} selection the selection object - * @returns {Object[]} an array of objects defining controls for the toolbar - * @private for platform-internal use - */ - ToolbarRegistry.prototype.get = function (selection) { - const providers = this.getAllProviders().filter(function (provider) { - return provider.forSelection(selection); - }); - - const structure = []; - - providers.forEach((provider) => { - provider.toolbar(selection).forEach((item) => structure.push(item)); - }); - - return structure; - }; +/** + * A ToolbarRegistry maintains the definitions for toolbars. + * + * @interface ToolbarRegistry + * @memberof module:openmct + */ +export default function ToolbarRegistry() { + this.providers = {}; +} - /** - * @private - */ - ToolbarRegistry.prototype.getAllProviders = function () { - return Object.values(this.providers); - }; +/** + * Gets toolbar controls from providers which can provide a toolbar for this selection. + * + * @param {object} selection the selection object + * @returns {Object[]} an array of objects defining controls for the toolbar + * @private for platform-internal use + */ +ToolbarRegistry.prototype.get = function (selection) { + const providers = this.getAllProviders().filter(function (provider) { + return provider.forSelection(selection); + }); - /** - * @private - */ - ToolbarRegistry.prototype.getByProviderKey = function (key) { - return this.providers[key]; - }; + const structure = []; - /** - * Registers a new type of toolbar. - * - * @param {module:openmct.ToolbarRegistry} provider the provider for this toolbar - * @method addProvider - * @memberof module:openmct.ToolbarRegistry# - */ - ToolbarRegistry.prototype.addProvider = function (provider) { - const key = provider.key; + providers.forEach((provider) => { + provider.toolbar(selection).forEach((item) => structure.push(item)); + }); - if (key === undefined) { - throw "Toolbar providers must have a unique 'key' property defined."; - } + return structure; +}; - if (this.providers[key] !== undefined) { - console.warn("Provider already defined for key '%s'. Provider keys must be unique.", key); - } +/** + * @private + */ +ToolbarRegistry.prototype.getAllProviders = function () { + return Object.values(this.providers); +}; - this.providers[key] = provider; - }; +/** + * @private + */ +ToolbarRegistry.prototype.getByProviderKey = function (key) { + return this.providers[key]; +}; - /** - * Exposes types of toolbars in Open MCT. - * - * @interface ToolbarProvider - * @property {string} key a unique identifier for this toolbar - * @property {string} name the human-readable name of this toolbar - * @property {string} [description] a longer-form description (typically - * a single sentence or short paragraph) of this kind of toolbar - * @memberof module:openmct - */ +/** + * Registers a new type of toolbar. + * + * @param {module:openmct.ToolbarRegistry} provider the provider for this toolbar + * @method addProvider + * @memberof module:openmct.ToolbarRegistry# + */ +ToolbarRegistry.prototype.addProvider = function (provider) { + const key = provider.key; - /** - * Checks if this provider can supply toolbar for a selection. - * - * @method forSelection - * @memberof module:openmct.ToolbarProvider# - * @param {module:openmct.selection} selection - * @returns {boolean} 'true' if the toolbar applies to the provided selection, - * otherwise 'false'. - */ + if (key === undefined) { + throw "Toolbar providers must have a unique 'key' property defined."; + } - /** - * Provides controls that comprise a toolbar. - * - * @method toolbar - * @memberof module:openmct.ToolbarProvider# - * @param {object} selection the selection object - * @returns {Object[]} an array of objects defining controls for the toolbar. - */ + if (this.providers[key] !== undefined) { + console.warn("Provider already defined for key '%s'. Provider keys must be unique.", key); + } - return ToolbarRegistry; -}); + this.providers[key] = provider; +}; diff --git a/src/ui/registries/ViewRegistry.js b/src/ui/registries/ViewRegistry.js index e649fc3ac4d..9cbfa17a418 100644 --- a/src/ui/registries/ViewRegistry.js +++ b/src/ui/registries/ViewRegistry.js @@ -20,255 +20,104 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define(['EventEmitter'], function (EventEmitter) { - const DEFAULT_VIEW_PRIORITY = 100; - - /** - * A ViewRegistry maintains the definitions for different kinds of views - * that may occur in different places in the user interface. - * @interface ViewRegistry - * @memberof module:openmct - */ - function ViewRegistry() { - EventEmitter.apply(this); - this.providers = {}; +import EventEmitter from 'EventEmitter'; + +const DEFAULT_VIEW_PRIORITY = 100; + +/** + * A ViewRegistry maintains the definitions for different kinds of views + * that may occur in different places in the user interface. + * @interface ViewRegistry + * @memberof module:openmct + */ +export default function ViewRegistry() { + EventEmitter.apply(this); + this.providers = {}; +} + +ViewRegistry.prototype = Object.create(EventEmitter.prototype); + +/** + * @private for platform-internal use + * @param {*} item the object to be viewed + * @param {array} objectPath - The current contextual object path of the view object + * eg current domainObject is located under MyItems which is under Root + * @returns {module:openmct.ViewProvider[]} any providers + * which can provide views of this object + */ +ViewRegistry.prototype.get = function (item, objectPath) { + if (objectPath === undefined) { + throw 'objectPath must be provided to get applicable views for an object'; } - ViewRegistry.prototype = Object.create(EventEmitter.prototype); - - /** - * @private for platform-internal use - * @param {*} item the object to be viewed - * @param {array} objectPath - The current contextual object path of the view object - * eg current domainObject is located under MyItems which is under Root - * @returns {module:openmct.ViewProvider[]} any providers - * which can provide views of this object - */ - ViewRegistry.prototype.get = function (item, objectPath) { - if (objectPath === undefined) { - throw 'objectPath must be provided to get applicable views for an object'; - } - - function byPriority(providerA, providerB) { - let priorityA = providerA.priority ? providerA.priority(item) : DEFAULT_VIEW_PRIORITY; - let priorityB = providerB.priority ? providerB.priority(item) : DEFAULT_VIEW_PRIORITY; - - return priorityB - priorityA; - } - - return this.getAllProviders() - .filter(function (provider) { - return provider.canView(item, objectPath); - }) - .sort(byPriority); - }; - - /** - * @private - */ - ViewRegistry.prototype.getAllProviders = function () { - return Object.values(this.providers); - }; + function byPriority(providerA, providerB) { + let priorityA = providerA.priority ? providerA.priority(item) : DEFAULT_VIEW_PRIORITY; + let priorityB = providerB.priority ? providerB.priority(item) : DEFAULT_VIEW_PRIORITY; - /** - * Register a new type of view. - * - * @param {module:openmct.ViewProvider} provider the provider for this view - * @method addProvider - * @memberof module:openmct.ViewRegistry# - */ - ViewRegistry.prototype.addProvider = function (provider) { - const key = provider.key; - if (key === undefined) { - throw "View providers must have a unique 'key' property defined"; - } + return priorityB - priorityA; + } - if (this.providers[key] !== undefined) { - console.warn("Provider already defined for key '%s'. Provider keys must be unique.", key); - } + return this.getAllProviders() + .filter(function (provider) { + return provider.canView(item, objectPath); + }) + .sort(byPriority); +}; + +/** + * @private + */ +ViewRegistry.prototype.getAllProviders = function () { + return Object.values(this.providers); +}; + +/** + * Register a new type of view. + * + * @param {module:openmct.ViewProvider} provider the provider for this view + * @method addProvider + * @memberof module:openmct.ViewRegistry# + */ +ViewRegistry.prototype.addProvider = function (provider) { + const key = provider.key; + if (key === undefined) { + throw "View providers must have a unique 'key' property defined"; + } - const wrappedView = provider.view.bind(provider); - provider.view = (domainObject, objectPath) => { - const viewObject = wrappedView(domainObject, objectPath); - const wrappedShow = viewObject.show.bind(viewObject); - viewObject.key = key; // provide access to provider key on view object - viewObject.show = (element, isEditing, viewOptions) => { - viewObject.parentElement = element.parentElement; - wrappedShow(element, isEditing, viewOptions); - }; + if (this.providers[key] !== undefined) { + console.warn("Provider already defined for key '%s'. Provider keys must be unique.", key); + } - return viewObject; + const wrappedView = provider.view.bind(provider); + provider.view = (domainObject, objectPath) => { + const viewObject = wrappedView(domainObject, objectPath); + const wrappedShow = viewObject.show.bind(viewObject); + viewObject.key = key; // provide access to provider key on view object + viewObject.show = (element, isEditing, viewOptions) => { + viewObject.parentElement = element.parentElement; + wrappedShow(element, isEditing, viewOptions); }; - this.providers[key] = provider; - }; - - /** - * @private - */ - ViewRegistry.prototype.getByProviderKey = function (key) { - return this.providers[key]; + return viewObject; }; - /** - * Used internally to support seamless usage of new views with old - * views. - * @private - */ - ViewRegistry.prototype.getByVPID = function (vpid) { - return this.providers.filter(function (p) { - return p.vpid === vpid; - })[0]; - }; - - /** - * A View is used to provide displayable content, and to react to - * associated life cycle events. - * - * @name View - * @interface - * @memberof module:openmct - */ - - /** - * Populate the supplied DOM element with the contents of this view. - * - * View implementations should use this method to attach any - * listeners or acquire other resources that are necessary to keep - * the contents of this view up-to-date. - * - * @param {HTMLElement} container the DOM element to populate - * @method show - * @memberof module:openmct.View# - */ - - /** - * Indicates whether or not the application is in edit mode. This supports - * views that have distinct visual and behavioral elements when the - * navigated object is being edited. - * - * For cases where a completely separate view is desired for editing purposes, - * see {@link openmct.ViewProvider#edit} - * - * @param {boolean} isEditing - * @method show - * @memberof module:openmct.View# - */ - - /** - * Release any resources associated with this view. - * - * View implementations should use this method to detach any - * listeners or release other resources that are no longer necessary - * once a view is no longer used. - * - * @method destroy - * @memberof module:openmct.View# - */ - - /** - * Returns the selection context. - * - * View implementations should use this method to customize - * the selection context. - * - * @method getSelectionContext - * @memberof module:openmct.View# - */ - - /** - * Exposes types of views in Open MCT. - * - * @interface ViewProvider - * @property {string} key a unique identifier for this view - * @property {string} name the human-readable name of this view - * @property {string} [description] a longer-form description (typically - * a single sentence or short paragraph) of this kind of view - * @property {string} [cssClass] the CSS class to apply to labels for this - * view (to add icons, for instance) - * @memberof module:openmct - */ - - /** - * Check if this provider can supply views for a domain object. - * - * When called by Open MCT, this may include additional arguments - * which are on the path to the object to be viewed; for instance, - * when viewing "A Folder" within "My Items", this method will be - * invoked with "A Folder" (as a domain object) as the first argument - * - * @method canView - * @memberof module:openmct.ViewProvider# - * @param {module:openmct.DomainObject} domainObject the domain object - * to be viewed - * @param {array} objectPath - The current contextual object path of the view object - * eg current domainObject is located under MyItems which is under Root - * @returns {boolean} 'true' if the view applies to the provided object, - * otherwise 'false'. - */ - - /** - * An optional function that defines whether or not this view can be used to edit a given object. - * If not provided, will default to `false` and the view will not support editing. To support editing, - * return true from this function and then - - * * Return a {@link openmct.View} from the `view` function, using the `onEditModeChange` callback to - * add and remove editing elements from the view - * OR - * * Return a {@link openmct.View} from the `view` function defining a read-only view. - * AND - * * Define an {@link openmct.ViewProvider#Edit} function on the view provider that returns an - * editing-specific view. - * - * @method canEdit - * @memberof module:openmct.ViewProvider# - * @param {module:openmct.DomainObject} domainObject the domain object - * to be edited - * @param {array} objectPath - The current contextual object path of the view object - * eg current domainObject is located under MyItems which is under Root - * @returns {boolean} 'true' if the view can be used to edit the provided object, - * otherwise 'false'. - */ - - /** - * Optional method determining the priority of a given view. If this - * function is not defined on a view provider, then a default priority - * of 100 will be applicable for all objects supported by this view. - * - * @method priority - * @memberof module:openmct.ViewProvider# - * @param {module:openmct.DomainObject} domainObject the domain object - * to be viewed - * @returns {number} The priority of the view. If multiple views could apply - * to an object, the view that returns the lowest number will be - * the default view. - */ - - /** - * Provide a view of this object. - * - * When called by Open MCT, the following arguments will be passed to it: - * @param {object} domainObject - the domainObject that the view is provided for - * @param {array} objectPath - The current contextual object path of the view object - * eg current domainObject is located under MyItems which is under Root - * - * @method view - * @memberof module:openmct.ViewProvider# - * @param {*} object the object to be viewed - * @returns {module:openmct.View} a view of this domain object - */ - - /** - * Provide an edit-mode specific view of this object. - * - * If optionally specified, this function will be called when the application - * enters edit mode. This will cause the active non-edit mode view and its - * dom element to be destroyed. - * - * @method edit - * @memberof module:openmct.ViewProvider# - * @param {*} object the object to be edit - * @returns {module:openmct.View} an editable view of this domain object - */ - - return ViewRegistry; -}); + this.providers[key] = provider; +}; + +/** + * @private + */ +ViewRegistry.prototype.getByProviderKey = function (key) { + return this.providers[key]; +}; + +/** + * Used internally to support seamless usage of new views with old + * views. + * @private + */ +ViewRegistry.prototype.getByVPID = function (vpid) { + return this.providers.filter(function (p) { + return p.vpid === vpid; + })[0]; +}; diff --git a/src/ui/router/Browse.js b/src/ui/router/Browse.js index 2ad5c967927..1cbda7bd61e 100644 --- a/src/ui/router/Browse.js +++ b/src/ui/router/Browse.js @@ -1,159 +1,153 @@ -define([], function () { - return function install(openmct) { - let navigateCall = 0; - let browseObject; - let unobserve = undefined; - let currentObjectPath; - let isRoutingInProgress = false; - - openmct.router.route(/^\/browse\/?$/, navigateToFirstChildOfRoot); - openmct.router.route(/^\/browse\/(.*)$/, (path, results, params) => { - isRoutingInProgress = true; - let navigatePath = results[1]; - clearMutationListeners(); - - navigateToPath(navigatePath, params.view); - }); - - openmct.router.on('change:params', onParamsChanged); - - function onParamsChanged(newParams, oldParams, changed) { - if (isRoutingInProgress) { - return; - } +export default function install(openmct) { + let navigateCall = 0; + let browseObject; + let unobserve = undefined; + let currentObjectPath; + let isRoutingInProgress = false; + + openmct.router.route(/^\/browse\/?$/, navigateToFirstChildOfRoot); + openmct.router.route(/^\/browse\/(.*)$/, (path, results, params) => { + isRoutingInProgress = true; + let navigatePath = results[1]; + clearMutationListeners(); + + navigateToPath(navigatePath, params.view); + }); + + openmct.router.on('change:params', onParamsChanged); + + function onParamsChanged(newParams, oldParams, changed) { + if (isRoutingInProgress) { + return; + } - if (changed.view && browseObject) { - let provider = openmct.objectViews.getByProviderKey(changed.view); - viewObject(browseObject, provider); - } + if (changed.view && browseObject) { + let provider = openmct.objectViews.getByProviderKey(changed.view); + viewObject(browseObject, provider); } + } - function viewObject(object, viewProvider) { - currentObjectPath = openmct.router.path; + function viewObject(object, viewProvider) { + currentObjectPath = openmct.router.path; - openmct.layout.$refs.browseObject.show(object, viewProvider.key, true, currentObjectPath); - openmct.layout.$refs.browseBar.domainObject = object; + openmct.layout.$refs.browseObject.show(object, viewProvider.key, true, currentObjectPath); + openmct.layout.$refs.browseBar.domainObject = object; - openmct.layout.$refs.browseBar.viewKey = viewProvider.key; + openmct.layout.$refs.browseBar.viewKey = viewProvider.key; + } + + function updateDocumentTitleOnNameMutation(newName) { + if (typeof newName === 'string' && newName !== document.title) { + document.title = newName; + openmct.layout.$refs.browseBar.domainObject = { + ...openmct.layout.$refs.browseBar.domainObject, + name: newName + }; } + } - function updateDocumentTitleOnNameMutation(newName) { - if (typeof newName === 'string' && newName !== document.title) { - document.title = newName; - openmct.layout.$refs.browseBar.domainObject = { - ...openmct.layout.$refs.browseBar.domainObject, - name: newName - }; - } + function navigateToPath(path, currentViewKey) { + navigateCall++; + let currentNavigation = navigateCall; + + if (unobserve) { + unobserve(); + unobserve = undefined; } - function navigateToPath(path, currentViewKey) { - navigateCall++; - let currentNavigation = navigateCall; + //Split path into object identifiers + if (!Array.isArray(path)) { + path = path.split('/'); + } - if (unobserve) { - unobserve(); - unobserve = undefined; - } + return pathToObjects(path).then((objects) => { + isRoutingInProgress = false; - //Split path into object identifiers - if (!Array.isArray(path)) { - path = path.split('/'); + if (currentNavigation !== navigateCall) { + return; // Prevent race. } - return pathToObjects(path).then((objects) => { - isRoutingInProgress = false; - - if (currentNavigation !== navigateCall) { - return; // Prevent race. - } + objects = objects.reverse(); - objects = objects.reverse(); + openmct.router.path = objects; + openmct.router.emit('afterNavigation'); + browseObject = objects[0]; - openmct.router.path = objects; - openmct.router.emit('afterNavigation'); - browseObject = objects[0]; + openmct.layout.$refs.browseBar.domainObject = browseObject; + if (!browseObject) { + openmct.layout.$refs.browseObject.clear(); - openmct.layout.$refs.browseBar.domainObject = browseObject; - if (!browseObject) { - openmct.layout.$refs.browseObject.clear(); + return; + } - return; - } + let currentProvider = openmct.objectViews.getByProviderKey(currentViewKey); + document.title = browseObject.name; //change document title to current object in main view + // assign listener to global for later clearing + unobserve = openmct.objects.observe(browseObject, 'name', updateDocumentTitleOnNameMutation); - let currentProvider = openmct.objectViews.getByProviderKey(currentViewKey); - document.title = browseObject.name; //change document title to current object in main view - // assign listener to global for later clearing - unobserve = openmct.objects.observe( - browseObject, - 'name', - updateDocumentTitleOnNameMutation - ); + if (currentProvider && currentProvider.canView(browseObject, openmct.router.path)) { + viewObject(browseObject, currentProvider); - if (currentProvider && currentProvider.canView(browseObject, openmct.router.path)) { - viewObject(browseObject, currentProvider); + return; + } + let defaultProvider = openmct.objectViews.get(browseObject, openmct.router.path)[0]; + if (defaultProvider) { + openmct.router.updateParams({ + view: defaultProvider.key + }); + } else { + openmct.router.updateParams({ + view: undefined + }); + openmct.layout.$refs.browseObject.clear(); + } + }); + } + + function pathToObjects(path) { + return Promise.all( + path.map((keyString) => { + let identifier = openmct.objects.parseKeyString(keyString); + if (openmct.objects.supportsMutation(identifier)) { + return openmct.objects.getMutable(identifier); + } else { + return openmct.objects.get(identifier); + } + }) + ); + } + + function navigateToFirstChildOfRoot() { + openmct.objects + .get('ROOT') + .then((rootObject) => { + const composition = openmct.composition.get(rootObject); + if (!composition) { return; } - let defaultProvider = openmct.objectViews.get(browseObject, openmct.router.path)[0]; - if (defaultProvider) { - openmct.router.updateParams({ - view: defaultProvider.key - }); - } else { - openmct.router.updateParams({ - view: undefined - }); - openmct.layout.$refs.browseObject.clear(); + composition + .load() + .then((children) => { + let lastChild = children[children.length - 1]; + if (lastChild) { + let lastChildId = openmct.objects.makeKeyString(lastChild.identifier); + openmct.router.setPath(`#/browse/${lastChildId}`); + } + }) + .catch((e) => console.error(e)); + }) + .catch((e) => console.error(e)); + } + + function clearMutationListeners() { + if (openmct.router.path !== undefined) { + openmct.router.path.forEach((pathObject) => { + if (pathObject.isMutable) { + openmct.objects.destroyMutable(pathObject); } }); } - - function pathToObjects(path) { - return Promise.all( - path.map((keyString) => { - let identifier = openmct.objects.parseKeyString(keyString); - if (openmct.objects.supportsMutation(identifier)) { - return openmct.objects.getMutable(identifier); - } else { - return openmct.objects.get(identifier); - } - }) - ); - } - - function navigateToFirstChildOfRoot() { - openmct.objects - .get('ROOT') - .then((rootObject) => { - const composition = openmct.composition.get(rootObject); - if (!composition) { - return; - } - - composition - .load() - .then((children) => { - let lastChild = children[children.length - 1]; - if (lastChild) { - let lastChildId = openmct.objects.makeKeyString(lastChild.identifier); - openmct.router.setPath(`#/browse/${lastChildId}`); - } - }) - .catch((e) => console.error(e)); - }) - .catch((e) => console.error(e)); - } - - function clearMutationListeners() { - if (openmct.router.path !== undefined) { - openmct.router.path.forEach((pathObject) => { - if (pathObject.isMutable) { - openmct.objects.destroyMutable(pathObject); - } - }); - } - } - }; -}); + } +} From dbb451fa972323e2f1d43ab2c23524e3553ffbc5 Mon Sep 17 00:00:00 2001 From: Tristan F <26509014+LeoDog896@users.noreply.github.com> Date: Fri, 1 Sep 2023 12:02:09 -0400 Subject: [PATCH 02/20] fix: move MCT to ES6 class --- openmct.js | 4 +- src/MCT.js | 579 +++++++++++++++++++++++++---------------------------- 2 files changed, 276 insertions(+), 307 deletions(-) diff --git a/openmct.js b/openmct.js index 2e9c7e70706..82d643e1836 100644 --- a/openmct.js +++ b/openmct.js @@ -75,9 +75,9 @@ if (document.currentScript) { * @property {OpenMCTComponent[]} components */ -import { MCT } from './src/MCT'; +const { MCT } = require('./src/MCT'); /** @type {OpenMCT} */ const openmct = new MCT(); -export default openmct; +module.exports = openmct; diff --git a/src/MCT.js b/src/MCT.js index f7bb48bd1c5..445de30740a 100644 --- a/src/MCT.js +++ b/src/MCT.js @@ -61,160 +61,164 @@ import Browse from './ui/router/Browse'; * @constructor * @memberof module:openmct */ -export function MCT() { - EventEmitter.call(this); - this.buildInfo = { - version: __OPENMCT_VERSION__, - buildDate: __OPENMCT_BUILD_DATE__, - revision: __OPENMCT_REVISION__, - branch: __OPENMCT_BUILD_BRANCH__ - }; - - this.destroy = this.destroy.bind(this); - this.defaultClock = 'local'; - [ - /** - * Tracks current selection state of the application. - * @private - */ - ['selection', () => new Selection(this)], +export class MCT extends EventEmitter { + plugins = plugins; + components = components; - /** - * 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. - * The composition of a domain object is the list of other domain - * objects it "contains" (for instance, that should be displayed - * beneath it in the tree.) - * - * `composition` may be called as a function, in which case it acts - * as [`composition.get`]{@link module:openmct.CompositionAPI#get}. - * - * @type {module:openmct.CompositionAPI} - * @memberof module:openmct.MCT# - * @name composition - */ - ['composition', () => new api.CompositionAPI(this)], - - /** - * Registry for views of domain objects which should appear in the - * main viewing area. - * - * @type {module:openmct.ViewRegistry} - * @memberof module:openmct.MCT# - * @name objectViews - */ - ['objectViews', () => new ViewRegistry()], - - /** - * Registry for views which should appear in the Inspector area. - * These views will be chosen based on the selection state. - * - * @type {module:openmct.InspectorViewRegistry} - * @memberof module:openmct.MCT# - * @name inspectorViews - */ - ['inspectorViews', () => new InspectorViewRegistry()], - - /** - * Registry for views which should appear in Edit Properties - * dialogs, and similar user interface elements used for - * modifying domain objects external to its regular views. - * - * @type {module:openmct.ViewRegistry} - * @memberof module:openmct.MCT# - * @name propertyEditors - */ - ['propertyEditors', () => new ViewRegistry()], - - /** - * Registry for views which should appear in the toolbar area while - * editing. These views will be chosen based on the selection state. - * - * @type {module:openmct.ToolbarRegistry} - * @memberof module:openmct.MCT# - * @name toolbars - */ - ['toolbars', () => new ToolbarRegistry()], - - /** - * Registry for domain object types which may exist within this - * instance of Open MCT. - * - * @type {module:openmct.TypeRegistry} - * @memberof module:openmct.MCT# - * @name types - */ - ['types', () => new api.TypeRegistry()], - - /** - * An interface for interacting with domain objects and the domain - * object hierarchy. - * - * @type {module:openmct.ObjectAPI} - * @memberof module:openmct.MCT# - * @name objects - */ - ['objects', () => new api.ObjectAPI(this.types, this)], + /** + * Tracks current selection state of the application. + * @private + */ + selection = new Selection(this); - /** - * An interface for retrieving and interpreting telemetry data associated - * with a domain object. - * - * @type {module:openmct.TelemetryAPI} - * @memberof module:openmct.MCT# - * @name telemetry - */ - ['telemetry', () => new api.TelemetryAPI(this)], + /** + * MCT's time conductor, which may be used to synchronize view contents + * for telemetry- or time-based views. + * @type {module:openmct.TimeConductor} + * @memberof module:openmct.MCT# + * @name conductor + */ + time = new api.TimeAPI(this); - /** - * An interface for creating new indicators and changing them dynamically. - * - * @type {module:openmct.IndicatorAPI} - * @memberof module:openmct.MCT# - * @name indicators - */ - ['indicators', () => new api.IndicatorAPI(this)], + /** + * An interface for interacting with the composition of domain objects. + * The composition of a domain object is the list of other domain + * objects it "contains" (for instance, that should be displayed + * beneath it in the tree.) + * + * `composition` may be called as a function, in which case it acts + * as [`composition.get`]{@link module:openmct.CompositionAPI#get}. + * + * @type {module:openmct.CompositionAPI} + * @memberof module:openmct.MCT# + * @name composition + */ + composition = new api.CompositionAPI(this); - /** - * MCT's user awareness management, to enable user and - * role specific functionality. - * @type {module:openmct.UserAPI} - * @memberof module:openmct.MCT# - * @name user - */ - ['user', () => new api.UserAPI(this)], + /** + * Registry for views of domain objects which should appear in the + * main viewing area. + * + * @type {module:openmct.ViewRegistry} + * @memberof module:openmct.MCT# + * @name objectViews + */ + objectViews = new ViewRegistry(); - ['notifications', () => new api.NotificationAPI()], + /** + * Registry for views which should appear in the Inspector area. + * These views will be chosen based on the selection state. + * + * @type {module:openmct.InspectorViewRegistry} + * @memberof module:openmct.MCT# + * @name inspectorViews + */ + inspectorViews = new InspectorViewRegistry(); - ['editor', () => new api.EditorAPI(this)], + /** + * Registry for views which should appear in Edit Properties + * dialogs, and similar user interface elements used for + * modifying domain objects external to its regular views. + * + * @type {module:openmct.ViewRegistry} + * @memberof module:openmct.MCT# + * @name propertyEditors + */ + propretyEditors = new ViewRegistry(); - ['overlays', () => new OverlayAPI()], + /** + * Registry for views which should appear in the toolbar area while + * editing. These views will be chosen based on the selection state. + * + * @type {module:openmct.ToolbarRegistry} + * @memberof module:openmct.MCT# + * @name toolbars + */ + toolbars = new ToolbarRegistry(); - ['tooltips', () => new ToolTipAPI()], + /** + * Registry for domain object types which may exist within this + * instance of Open MCT. + * + * @type {module:openmct.TypeRegistry} + * @memberof module:openmct.MCT# + * @name types + */ + types = new api.TypeRegistry(); - ['menus', () => new api.MenuAPI(this)], + /** + * An interface for interacting with domain objects and the domain + * object hierarchy. + * + * @type {module:openmct.ObjectAPI} + * @memberof module:openmct.MCT# + * @name objects + */ + objects = new api.ObjectAPI(this.types, this); - ['actions', () => new api.ActionsAPI(this)], + /** + * An interface for retrieving and interpreting telemetry data associated + * with a domain object. + * + * @type {module:openmct.TelemetryAPI} + * @memberof module:openmct.MCT# + * @name telemetry + */ + telemetry = new api.TelemetryAPI(this); - ['status', () => new api.StatusAPI(this)], + /** + * An interface for creating new indicators and changing them dynamically. + * + * @type {module:openmct.IndicatorAPI} + * @memberof module:openmct.MCT# + * @name indicators + */ + indicators = new api.IndicatorAPI(this); - ['priority', () => api.PriorityAPI], + /** + * MCT's user awareness management, to enable user and + * role specific functionality. + * @type {module:openmct.UserAPI} + * @memberof module:openmct.MCT# + * @name user + */ + user = new api.UserAPI(this); + + notifications = new api.NotificationAPI(); + editor = new api.EditorAPI(this); + overlays = new OverlayAPI(); + tooltips = new ToolTipAPI(); + menus = new api.MenuAPI(this); + actions = new api.ActionsAPI(this); + status = new api.StatusAPI(this); + priority = api.PriorityAPI; + router = new ApplicationRouter(this); + faults = new api.FaultManagementAPI(this); + forms = new api.FormsAPI(this); + branding = BrandingAPI; - ['router', () => new ApplicationRouter(this)], + /** + * 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); - ['faults', () => new api.FaultManagementAPI(this)], + constructor() { + super(); + EventEmitter.call(this); - ['forms', () => new api.FormsAPI(this)], + this.buildInfo = { + version: __OPENMCT_VERSION__, + buildDate: __OPENMCT_BUILD_DATE__, + revision: __OPENMCT_REVISION__, + branch: __OPENMCT_BUILD_BRANCH__ + }; - ['branding', () => BrandingAPI], + this.destroy = this.destroy.bind(this); + this.defaultClock = 'local'; /** * MCT's annotation API that enables @@ -223,186 +227,151 @@ export function MCT() { * @memberof module:openmct.MCT# * @name annotation */ - ['annotation', () => new api.AnnotationAPI(this)] - ].forEach((apiEntry) => { - const apiName = apiEntry[0]; - const apiObject = apiEntry[1](); - - Object.defineProperty(this, apiName, { - value: apiObject, - enumerable: false, - configurable: false, - writable: true - }); - }); - + this.annotation = new api.AnnotationAPI(this); + + // Plugins that are installed by default + this.install(this.plugins.Plot()); + this.install(this.plugins.TelemetryTable()); + this.install(PreviewPlugin()); + this.install(LicensesPlugin()); + this.install(RemoveActionPlugin()); + this.install(MoveActionPlugin()); + this.install(LinkActionPlugin()); + this.install(DuplicateActionPlugin()); + this.install(ExportAsJSONAction()); + this.install(ImportFromJSONAction()); + this.install(this.plugins.FormActions()); + this.install(this.plugins.FolderView()); + this.install(this.plugins.Tabs()); + this.install(ImageryPlugin()); + this.install(this.plugins.FlexibleLayout()); + this.install(this.plugins.GoToOriginalAction()); + this.install(this.plugins.OpenInNewTabAction()); + this.install(this.plugins.WebPage()); + this.install(this.plugins.Condition()); + this.install(this.plugins.ConditionWidget()); + this.install(this.plugins.URLTimeSettingsSynchronizer()); + this.install(this.plugins.NotificationIndicator()); + this.install(this.plugins.NewFolderAction()); + this.install(this.plugins.ViewDatumAction()); + this.install(this.plugins.ViewLargeAction()); + this.install(this.plugins.ObjectInterceptors()); + this.install(this.plugins.DeviceClassifier()); + this.install(this.plugins.UserIndicator()); + this.install(this.plugins.Gauge()); + this.install(this.plugins.InspectorViews()); + } /** - * MCT's annotation API that enables - * human-created comments and categorization linked to data products - * @type {module:openmct.AnnotationAPI} + * Set path to where assets are hosted. This should be the path to main.js. * @memberof module:openmct.MCT# - * @name annotation + * @method setAssetPath */ - this.annotation = new api.AnnotationAPI(this); - - // Plugins that are installed by default - this.install(this.plugins.Plot()); - this.install(this.plugins.TelemetryTable()); - this.install(PreviewPlugin()); - this.install(LicensesPlugin()); - this.install(RemoveActionPlugin()); - this.install(MoveActionPlugin()); - this.install(LinkActionPlugin()); - this.install(DuplicateActionPlugin()); - this.install(ExportAsJSONAction()); - this.install(ImportFromJSONAction()); - this.install(this.plugins.FormActions()); - this.install(this.plugins.FolderView()); - this.install(this.plugins.Tabs()); - this.install(ImageryPlugin()); - this.install(this.plugins.FlexibleLayout()); - this.install(this.plugins.GoToOriginalAction()); - this.install(this.plugins.OpenInNewTabAction()); - this.install(this.plugins.WebPage()); - this.install(this.plugins.Condition()); - this.install(this.plugins.ConditionWidget()); - this.install(this.plugins.URLTimeSettingsSynchronizer()); - this.install(this.plugins.NotificationIndicator()); - this.install(this.plugins.NewFolderAction()); - this.install(this.plugins.ViewDatumAction()); - this.install(this.plugins.ViewLargeAction()); - this.install(this.plugins.ObjectInterceptors()); - this.install(this.plugins.DeviceClassifier()); - this.install(this.plugins.UserIndicator()); - this.install(this.plugins.Gauge()); - this.install(this.plugins.InspectorViews()); -} - -MCT.prototype = Object.create(EventEmitter.prototype); - -MCT.prototype.MCT = MCT; - -/** - * Set path to where assets are hosted. This should be the path to main.js. - * @memberof module:openmct.MCT# - * @method setAssetPath - */ -MCT.prototype.setAssetPath = function (assetPath) { - this._assetPath = assetPath; -}; - -/** - * Get path to where assets are hosted. - * @memberof module:openmct.MCT# - * @method getAssetPath - */ -MCT.prototype.getAssetPath = function () { - const assetPathLength = this._assetPath && this._assetPath.length; - if (!assetPathLength) { - return '/'; - } - - if (this._assetPath[assetPathLength - 1] !== '/') { - return this._assetPath + '/'; - } - - return this._assetPath; -}; - -/** - * Start running Open MCT. This should be called only after any plugins - * have been installed. - * @fires module:openmct.MCT~start - * @memberof module:openmct.MCT# - * @method start - * @param {HTMLElement} [domElement] the DOM element in which to run - * MCT; if undefined, MCT will be run in the body of the document - */ -MCT.prototype.start = function ( - domElement = document.body.firstElementChild, - isHeadlessMode = false -) { - // Create element to mount Layout if it doesn't exist - if (domElement === null) { - domElement = document.createElement('div'); - document.body.appendChild(domElement); - } - domElement.id = 'openmct-app'; - - if (this.types.get('layout') === undefined) { - this.install( - this.plugins.DisplayLayout({ - showAsView: ['summary-widget'] - }) - ); + setAssetPath(assetPath) { + this._assetPath = assetPath; } + /** + * Get path to where assets are hosted. + * @memberof module:openmct.MCT# + * @method getAssetPath + */ + getAssetPath() { + const assetPathLength = this._assetPath && this._assetPath.length; + if (!assetPathLength) { + return '/'; + } - this.element = domElement; + if (this._assetPath[assetPathLength - 1] !== '/') { + return this._assetPath + '/'; + } - if (!this.time.getClock()) { - this.time.setClock(this.defaultClock); + return this._assetPath; } - - this.router.route(/^\/$/, () => { - this.router.setPath('/browse/'); - }); - /** - * Fired by [MCT]{@link module:openmct.MCT} when the application - * is started. - * @event start - * @memberof module:openmct.MCT~ + * Start running Open MCT. This should be called only after any plugins + * have been installed. + * @fires module:openmct.MCT~start + * @memberof module:openmct.MCT# + * @method start + * @param {HTMLElement} [domElement] the DOM element in which to run + * MCT; if undefined, MCT will be run in the body of the document */ - - if (!isHeadlessMode) { - const appLayout = Vue.createApp({ - components: { - Layout: Layout - }, - provide: { - openmct: Vue.markRaw(this) - }, - template: '' + start(domElement = document.body.firstElementChild, isHeadlessMode = false) { + // Create element to mount Layout if it doesn't exist + if (domElement === null) { + domElement = document.createElement('div'); + document.body.appendChild(domElement); + } + domElement.id = 'openmct-app'; + + if (this.types.get('layout') === undefined) { + this.install( + this.plugins.DisplayLayout({ + showAsView: ['summary-widget'] + }) + ); + } + + this.element = domElement; + + if (!this.time.getClock()) { + this.time.setClock(this.defaultClock); + } + + this.router.route(/^\/$/, () => { + this.router.setPath('/browse/'); }); - const component = appLayout.mount(domElement); - component.$nextTick(() => { - this.layout = component.$refs.layout; - this.app = appLayout; - Browse(this); + + /** + * Fired by [MCT]{@link module:openmct.MCT} when the application + * is started. + * @event start + * @memberof module:openmct.MCT~ + */ + if (!isHeadlessMode) { + const appLayout = Vue.createApp({ + components: { + Layout: Layout + }, + provide: { + openmct: Vue.markRaw(this) + }, + template: '' + }); + const component = appLayout.mount(domElement); + component.$nextTick(() => { + this.layout = component.$refs.layout; + this.app = appLayout; + Browse(this); + window.addEventListener('beforeunload', this.destroy); + this.router.start(); + this.emit('start'); + }); + } else { window.addEventListener('beforeunload', this.destroy); + this.router.start(); this.emit('start'); - }); - } else { - window.addEventListener('beforeunload', this.destroy); - - this.router.start(); - this.emit('start'); + } } -}; - -MCT.prototype.startHeadless = function () { - let unreachableNode = document.createElement('div'); + startHeadless() { + let unreachableNode = document.createElement('div'); - return this.start(unreachableNode, true); -}; + return this.start(unreachableNode, true); + } + /** + * Install a plugin in MCT. + * + * @param {Function} plugin a plugin install function which will be + * invoked with the mct instance. + * @memberof module:openmct.MCT# + */ + install(plugin) { + plugin(this); + } -/** - * Install a plugin in MCT. - * - * @param {Function} plugin a plugin install function which will be - * invoked with the mct instance. - * @memberof module:openmct.MCT# - */ -MCT.prototype.install = function (plugin) { - plugin(this); -}; - -MCT.prototype.destroy = function () { - window.removeEventListener('beforeunload', this.destroy); - this.emit('destroy'); - this.router.destroy(); -}; - -MCT.prototype.plugins = plugins; -MCT.prototype.components = components; + destroy() { + window.removeEventListener('beforeunload', this.destroy); + this.emit('destroy'); + this.router.destroy(); + } +} From fc1297bd4cdb49b4e16cb476dafe06a9c206b563 Mon Sep 17 00:00:00 2001 From: Tristan F <26509014+LeoDog896@users.noreply.github.com> Date: Fri, 1 Sep 2023 12:18:02 -0400 Subject: [PATCH 03/20] fix: default drop, correct imports --- src/api/objects/test/object-utilsSpec.js | 2 +- src/plugins/telemetryTable/TelemetryTable.js | 2 +- src/utils/testing.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/api/objects/test/object-utilsSpec.js b/src/api/objects/test/object-utilsSpec.js index 3a9561272ad..8c13e995bd8 100644 --- a/src/api/objects/test/object-utilsSpec.js +++ b/src/api/objects/test/object-utilsSpec.js @@ -1,4 +1,4 @@ -import objectUtils from './objectUtils'; +import objectUtils from 'objectUtils'; describe('objectUtils', function () { describe('keyString util', function () { diff --git a/src/plugins/telemetryTable/TelemetryTable.js b/src/plugins/telemetryTable/TelemetryTable.js index 56afb7503fc..11b327ab715 100644 --- a/src/plugins/telemetryTable/TelemetryTable.js +++ b/src/plugins/telemetryTable/TelemetryTable.js @@ -154,7 +154,7 @@ export default class TelemetryTable extends EventEmitter { this.telemetryCollections[keyString].load(); this.stalenessSubscription[keyString] = {}; - this.stalenessSubscription[keyString].stalenessUtils = new StalenessUtils.default( + this.stalenessSubscription[keyString].stalenessUtils = new StalenessUtils( this.openmct, telemetryObject ); diff --git a/src/utils/testing.js b/src/utils/testing.js index 2d89acfbff7..a3850d94f70 100644 --- a/src/utils/testing.js +++ b/src/utils/testing.js @@ -20,7 +20,7 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -import MCT from 'MCT'; +import { MCT } from 'MCT'; import { markRaw } from 'vue'; let nativeFunctions = []; From 381c2639f66c217073a79f1ac5b43ab7840df120 Mon Sep 17 00:00:00 2001 From: Tristan F <26509014+LeoDog896@users.noreply.github.com> Date: Fri, 1 Sep 2023 13:27:03 -0400 Subject: [PATCH 04/20] fix: correct all imports --- src/MCTSpec.js | 2 +- src/plugins/URLIndicatorPlugin/URLIndicatorSpec.js | 2 +- src/plugins/autoflow/AutoflowTabularView.js | 2 +- src/plugins/summaryWidget/src/ConditionEvaluator.js | 2 +- src/plugins/summaryWidget/src/Rule.js | 2 +- src/plugins/summaryWidget/src/TestDataItem.js | 2 +- src/plugins/summaryWidget/src/input/Select.js | 2 +- src/plugins/summaryWidget/src/views/SummaryWidgetView.js | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/MCTSpec.js b/src/MCTSpec.js index f259ce413a6..7335d234537 100644 --- a/src/MCTSpec.js +++ b/src/MCTSpec.js @@ -20,7 +20,7 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -import testUtils from 'utils/testing'; +import * as testUtils from 'utils/testing'; import plugins from './plugins/plugins'; diff --git a/src/plugins/URLIndicatorPlugin/URLIndicatorSpec.js b/src/plugins/URLIndicatorPlugin/URLIndicatorSpec.js index 45e052de978..3bdba922ba6 100644 --- a/src/plugins/URLIndicatorPlugin/URLIndicatorSpec.js +++ b/src/plugins/URLIndicatorPlugin/URLIndicatorSpec.js @@ -20,7 +20,7 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -import testingUtils from 'utils/testing'; +import * as testingUtils from 'utils/testing'; import URLIndicatorPlugin from './URLIndicatorPlugin'; diff --git a/src/plugins/autoflow/AutoflowTabularView.js b/src/plugins/autoflow/AutoflowTabularView.js index 8d039158266..5cb908b9e1a 100644 --- a/src/plugins/autoflow/AutoflowTabularView.js +++ b/src/plugins/autoflow/AutoflowTabularView.js @@ -20,7 +20,7 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -import * as autoflowTemplate from './autoflow-tabular.html'; +import autoflowTemplate from './autoflow-tabular.html'; import AutoflowTabularConstants from './AutoflowTabularConstants'; import AutoflowTabularController from './AutoflowTabularController'; import VueView from './VueView'; diff --git a/src/plugins/summaryWidget/src/ConditionEvaluator.js b/src/plugins/summaryWidget/src/ConditionEvaluator.js index 9550fac32bb..0d3b142c025 100644 --- a/src/plugins/summaryWidget/src/ConditionEvaluator.js +++ b/src/plugins/summaryWidget/src/ConditionEvaluator.js @@ -9,7 +9,7 @@ * @param {Object} compositionObjs The current set of composition objects to * evaluate for 'any' and 'all' conditions */ -function ConditionEvaluator(subscriptionCache, compositionObjs) { +export default function ConditionEvaluator(subscriptionCache, compositionObjs) { this.subscriptionCache = subscriptionCache; this.compositionObjs = compositionObjs; diff --git a/src/plugins/summaryWidget/src/Rule.js b/src/plugins/summaryWidget/src/Rule.js index 5dc09aa6a2c..2cfd9f87e11 100644 --- a/src/plugins/summaryWidget/src/Rule.js +++ b/src/plugins/summaryWidget/src/Rule.js @@ -2,7 +2,7 @@ import EventEmitter from 'EventEmitter'; import _ from 'lodash'; import * as templateHelpers from '../../../utils/template/templateHelpers'; -import * as ruleTemplate from '../res/ruleTemplate.html'; +import ruleTemplate from '../res/ruleTemplate.html'; import Condition from './Condition'; import eventHelpers from './eventHelpers'; import ColorPalette from './input/ColorPalette'; diff --git a/src/plugins/summaryWidget/src/TestDataItem.js b/src/plugins/summaryWidget/src/TestDataItem.js index 246e2318020..57e579eddc5 100644 --- a/src/plugins/summaryWidget/src/TestDataItem.js +++ b/src/plugins/summaryWidget/src/TestDataItem.js @@ -1,7 +1,7 @@ import EventEmitter from 'EventEmitter'; import * as templateHelpers from '../../../utils/template/templateHelpers'; -import * as itemTemplate from '../res/testDataItemTemplate.html'; +import itemTemplate from '../res/testDataItemTemplate.html'; import eventHelpers from './eventHelpers'; import KeySelect from './input/KeySelect'; import ObjectSelect from './input/ObjectSelect'; diff --git a/src/plugins/summaryWidget/src/input/Select.js b/src/plugins/summaryWidget/src/input/Select.js index 525701605fd..4a78604cee5 100644 --- a/src/plugins/summaryWidget/src/input/Select.js +++ b/src/plugins/summaryWidget/src/input/Select.js @@ -1,7 +1,7 @@ import EventEmitter from 'EventEmitter'; import * as templateHelpers from '../../../../utils/template/templateHelpers'; -import * as selectTemplate from '../../res/input/selectTemplate.html'; +import selectTemplate from '../../res/input/selectTemplate.html'; import eventHelpers from '../eventHelpers'; /** diff --git a/src/plugins/summaryWidget/src/views/SummaryWidgetView.js b/src/plugins/summaryWidget/src/views/SummaryWidgetView.js index a6c7e6a2081..1aa014e151d 100644 --- a/src/plugins/summaryWidget/src/views/SummaryWidgetView.js +++ b/src/plugins/summaryWidget/src/views/SummaryWidgetView.js @@ -1,6 +1,6 @@ import * as urlSanitizeLib from '@braintree/sanitize-url'; -import * as summaryWidgetTemplate from './summary-widget.html'; +import summaryWidgetTemplate from './summary-widget.html'; const WIDGET_ICON_CLASS = 'c-sw__icon js-sw__icon'; From bca638ae3c650f055aa5cc00215cc3ec107f3121 Mon Sep 17 00:00:00 2001 From: Tristan F <26509014+LeoDog896@users.noreply.github.com> Date: Fri, 1 Sep 2023 13:28:16 -0400 Subject: [PATCH 05/20] fix: property typo --- src/MCT.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MCT.js b/src/MCT.js index 445de30740a..98322967454 100644 --- a/src/MCT.js +++ b/src/MCT.js @@ -124,7 +124,7 @@ export class MCT extends EventEmitter { * @memberof module:openmct.MCT# * @name propertyEditors */ - propretyEditors = new ViewRegistry(); + propertyEditors = new ViewRegistry(); /** * Registry for views which should appear in the toolbar area while From 084d0d919ea2b7e7e1aa199f9fc9d916ac7eb01f Mon Sep 17 00:00:00 2001 From: Tristan F <26509014+LeoDog896@users.noreply.github.com> Date: Fri, 1 Sep 2023 13:50:44 -0400 Subject: [PATCH 06/20] fix: avoid anonymous functions --- src/plugins/autoflow/VueView.js | 18 ++++++++++-------- .../displayLayout/DisplayLayoutToolbar.js | 16 ++++++++-------- src/plugins/summaryWidget/src/eventHelpers.js | 4 ++-- src/plugins/tabs/tabs.js | 2 +- 4 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/plugins/autoflow/VueView.js b/src/plugins/autoflow/VueView.js index d6ca9ca2649..946402ee658 100644 --- a/src/plugins/autoflow/VueView.js +++ b/src/plugins/autoflow/VueView.js @@ -22,13 +22,15 @@ import mount from 'utils/mount'; export default function () { - return function VueView(options) { - const { vNode, destroy } = mount(options); + class VueView { + constructor(options) { + const { vNode, destroy } = mount(options); + this.show = function (container) { + container.appendChild(vNode.el); + }; + this.destroy = destroy; + } + } - this.show = function (container) { - container.appendChild(vNode.el); - }; - - this.destroy = destroy; - }; + return VueView; } diff --git a/src/plugins/displayLayout/DisplayLayoutToolbar.js b/src/plugins/displayLayout/DisplayLayoutToolbar.js index 7699c46ce40..d68fd28ca1d 100644 --- a/src/plugins/displayLayout/DisplayLayoutToolbar.js +++ b/src/plugins/displayLayout/DisplayLayoutToolbar.js @@ -165,7 +165,7 @@ export default function DisplayLayoutToolbar(openmct) { return { control: 'menu', domainObject: selectionPath[0].context.item, - method: function (option) { + method(option) { let name = option.name.toLowerCase(); let form = DIALOG_FORM[name]; if (form) { @@ -236,7 +236,7 @@ export default function DisplayLayoutToolbar(openmct) { domainObject: selectedParent, icon: 'icon-trash', title: 'Delete the selected object', - method: function () { + method() { let removeItem = selectionPath[1].context.removeItem; let prompt = openmct.overlays.dialog({ iconClass: 'alert', @@ -290,7 +290,7 @@ export default function DisplayLayoutToolbar(openmct) { class: 'icon-arrow-double-down' } ], - method: function (option) { + method(option) { selectionPath[1].context.orderItem(option.value, getAllTypes(selectedObjects)); } }; @@ -474,7 +474,7 @@ export default function DisplayLayoutToolbar(openmct) { domainObject: selectedParent, icon: 'icon-duplicate', title: 'Duplicate the selected object', - method: function () { + method() { let duplicateItem = selectionPath[1].context.duplicateItem; duplicateItem(selection); @@ -574,7 +574,7 @@ export default function DisplayLayoutToolbar(openmct) { title: 'Switch the way this telemetry is displayed', label: 'View type', options: viewOptions, - method: function (option) { + method(option) { displayLayoutContext.switchViewType(selectedItemContext, option.value, selection); } }; @@ -590,7 +590,7 @@ export default function DisplayLayoutToolbar(openmct) { title: 'Merge into a telemetry table or plot', label: 'View type', options: APPLICABLE_VIEWS['telemetry-view-multi'], - method: function (option) { + method(option) { displayLayoutContext.mergeMultipleTelemetryViews(selection, option.value); } }; @@ -603,7 +603,7 @@ export default function DisplayLayoutToolbar(openmct) { icon: 'icon-object', title: 'Merge into a stacked plot', options: APPLICABLE_VIEWS['telemetry.plot.overlay-multi'], - method: function (option) { + method(option) { displayLayoutContext.mergeMultipleOverlayPlots(selection, option.value); } }; @@ -627,7 +627,7 @@ export default function DisplayLayoutToolbar(openmct) { control: 'button', domainObject: displayLayoutContext.item, icon: ICON_GRID_SHOW, - method: function () { + metho() { displayLayoutContext.toggleGrid(); this.icon = this.icon === ICON_GRID_SHOW ? ICON_GRID_HIDE : ICON_GRID_SHOW; diff --git a/src/plugins/summaryWidget/src/eventHelpers.js b/src/plugins/summaryWidget/src/eventHelpers.js index 79aef224880..e30d0ee6971 100644 --- a/src/plugins/summaryWidget/src/eventHelpers.js +++ b/src/plugins/summaryWidget/src/eventHelpers.js @@ -21,7 +21,7 @@ *****************************************************************************/ const helperFunctions = { - listenTo: function (object, event, callback, context) { + listenTo(object, event, callback, context) { if (!this._listeningTo) { this._listeningTo = []; } @@ -47,7 +47,7 @@ const helperFunctions = { this._listeningTo.push(listener); }, - stopListening: function (object, event, callback, context) { + stopListening(object, event, callback, context) { if (!this._listeningTo) { this._listeningTo = []; } diff --git a/src/plugins/tabs/tabs.js b/src/plugins/tabs/tabs.js index 4c2336ad40f..34ab9452384 100644 --- a/src/plugins/tabs/tabs.js +++ b/src/plugins/tabs/tabs.js @@ -47,7 +47,7 @@ export default class Tabs { let component = null; return { - show: function (element, editMode) { + show(element, editMode) { const { vNode, destroy } = mount( { el: element, From 912b511c4b1604136bda47d1e5e2edacbc6d3acf Mon Sep 17 00:00:00 2001 From: Tristan F <26509014+LeoDog896@users.noreply.github.com> Date: Fri, 1 Sep 2023 13:55:08 -0400 Subject: [PATCH 07/20] fix: correct typo scarily small - can see why this e2e coverage issue is high priority --- src/plugins/displayLayout/DisplayLayoutToolbar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/displayLayout/DisplayLayoutToolbar.js b/src/plugins/displayLayout/DisplayLayoutToolbar.js index d68fd28ca1d..409c3f594f6 100644 --- a/src/plugins/displayLayout/DisplayLayoutToolbar.js +++ b/src/plugins/displayLayout/DisplayLayoutToolbar.js @@ -627,7 +627,7 @@ export default function DisplayLayoutToolbar(openmct) { control: 'button', domainObject: displayLayoutContext.item, icon: ICON_GRID_SHOW, - metho() { + method() { displayLayoutContext.toggleGrid(); this.icon = this.icon === ICON_GRID_SHOW ? ICON_GRID_HIDE : ICON_GRID_SHOW; From c85c644cb915a3eb3581c846f5f6e0d765a1d0ea Mon Sep 17 00:00:00 2001 From: Tristan F <26509014+LeoDog896@users.noreply.github.com> Date: Fri, 1 Sep 2023 14:23:54 -0400 Subject: [PATCH 08/20] fix: use proper uuid format --- example/generator/WorkerInterface.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/generator/WorkerInterface.js b/example/generator/WorkerInterface.js index 416a59f088b..35943f79ee6 100644 --- a/example/generator/WorkerInterface.js +++ b/example/generator/WorkerInterface.js @@ -20,7 +20,7 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -import * as uuid from 'uuid'; +import { v4 as uuid } from 'uuid'; export default function WorkerInterface(openmct, StalenessProvider) { // eslint-disable-next-line no-undef From 000cdac961ec9e2d84500dfe0da828747b4af38f Mon Sep 17 00:00:00 2001 From: Tristan F <26509014+LeoDog896@users.noreply.github.com> Date: Thu, 21 Dec 2023 15:40:04 -0500 Subject: [PATCH 09/20] style: fmt --- src/MCT.js | 2 +- src/plugins/objectMigration/Migrations.js | 4 +- src/plugins/plugins.js | 7 +- src/plugins/summaryWidget/src/Rule.js | 13 +- .../summaryWidget/src/SummaryWidget.js | 26 ++-- .../src/views/SummaryWidgetView.js | 2 +- .../test/ConditionManagerSpec.js | 7 +- .../summaryWidget/test/SummaryWidgetSpec.js | 6 +- src/plugins/telemetryTable/TelemetryTable.js | 120 +++++++++--------- .../collections/TableRowCollection.js | 60 ++++----- 10 files changed, 123 insertions(+), 124 deletions(-) diff --git a/src/MCT.js b/src/MCT.js index 5b9554dc80a..d3e3548824e 100644 --- a/src/MCT.js +++ b/src/MCT.js @@ -38,6 +38,7 @@ import plugins from './plugins/plugins'; import RemoveActionPlugin from './plugins/remove/plugin'; import Selection from './selection/Selection'; import components from './ui/components/components'; +import AppLayout from './ui/layout/AppLayout.vue'; import Layout from './ui/layout/Layout.vue'; import PreviewPlugin from './ui/preview/plugin'; import InspectorViewRegistry from './ui/registries/InspectorViewRegistry'; @@ -45,7 +46,6 @@ import ToolbarRegistry from './ui/registries/ToolbarRegistry'; import ViewRegistry from './ui/registries/ViewRegistry'; import ApplicationRouter from './ui/router/ApplicationRouter'; import Browse from './ui/router/Browse'; -import AppLayout from './ui/layout/AppLayout.vue'; /** * Open MCT is an extensible web application for building mission diff --git a/src/plugins/objectMigration/Migrations.js b/src/plugins/objectMigration/Migrations.js index 5ec1794ea16..d44497a27e2 100644 --- a/src/plugins/objectMigration/Migrations.js +++ b/src/plugins/objectMigration/Migrations.js @@ -20,7 +20,7 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -import { uuid } from "uuid"; +import { v4 as uuid } from 'uuid'; export default function Migrations(openmct) { function getColumnNameKeyMap(domainObject) { @@ -276,4 +276,4 @@ export default function Migrations(openmct) { } } ]; -}; +} diff --git a/src/plugins/plugins.js b/src/plugins/plugins.js index 3cb13a31c2f..f299aedd1a3 100644 --- a/src/plugins/plugins.js +++ b/src/plugins/plugins.js @@ -20,11 +20,11 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ +import ExampleDataVisualizationSourcePlugin from '../../example/dataVisualization/plugin'; import EventGeneratorPlugin from '../../example/eventGenerator/plugin'; import ExampleTags from '../../example/exampleTags/plugin'; import ExampleUser from '../../example/exampleUser/plugin'; import ExampleFaultSource from '../../example/faultManagement/exampleFaultSource'; -import ExampleDataVisualizationSourcePlugin from '../../example/dataVisualization/plugin'; import GeneratorPlugin from '../../example/generator/plugin'; import ExampleImagery from '../../example/imagery/plugin'; import AutoflowPlugin from './autoflow/AutoflowTabularPlugin'; @@ -47,6 +47,7 @@ import GaugePlugin from './gauge/GaugePlugin'; import GoToOriginalAction from './goToOriginalAction/plugin'; import Hyperlink from './hyperlink/plugin'; import ImageryPlugin from './imagery/plugin'; +import InspectorDataVisualization from './inspectorDataVisualization/plugin'; import InspectorViews from './inspectorViews/plugin'; import ObjectInterceptors from './interceptors/plugin'; import ISOTimeFormat from './ISOTimeFormat/plugin'; @@ -83,7 +84,6 @@ import UTCTimeSystem from './utcTimeSystem/plugin'; import ViewDatumAction from './viewDatumAction/plugin'; import ViewLargeAction from './viewLargeAction/plugin'; import WebPagePlugin from './webPage/plugin'; -import InspectorDataVisualization from './inspectorDataVisualization/plugin'; const plugins = {}; @@ -92,8 +92,7 @@ plugins.example.ExampleUser = ExampleUser; plugins.example.ExampleImagery = ExampleImagery; plugins.example.ExampleFaultSource = ExampleFaultSource; plugins.example.EventGeneratorPlugin = EventGeneratorPlugin; -plugins.example.ExampleDataVisualizationSourcePlugin = - ExampleDataVisualizationSourcePlugin.default; +plugins.example.ExampleDataVisualizationSourcePlugin = ExampleDataVisualizationSourcePlugin.default; plugins.example.ExampleTags = ExampleTags; plugins.example.Generator = () => GeneratorPlugin; diff --git a/src/plugins/summaryWidget/src/Rule.js b/src/plugins/summaryWidget/src/Rule.js index 539ef718c24..4b01306e2b6 100644 --- a/src/plugins/summaryWidget/src/Rule.js +++ b/src/plugins/summaryWidget/src/Rule.js @@ -20,7 +20,14 @@ import IconPalette from './input/IconPalette'; * @param {WidgetDnD} widgetDnD A WidgetDnD instance to handle dragging and dropping rules * @param {element} container The DOM element which contains this summary widget */ -export default function Rule(ruleConfig, domainObject, openmct, conditionManager, widgetDnD, container) { +export default function Rule( + ruleConfig, + domainObject, + openmct, + conditionManager, + widgetDnD, + container +) { eventHelpers.extend(this); const self = this; const THUMB_ICON_CLASS = 'c-sw__icon js-sw__icon'; @@ -155,9 +162,7 @@ export default function Rule(ruleConfig, domainObject, openmct, conditionManager function onDragStart(event) { document.querySelectorAll('.t-drag-indicator').forEach((indicator) => { // eslint-disable-next-line no-invalid-this - const ruleHeader = self.domElement - .querySelectorAll('.widget-rule-header')[0] - .cloneNode(true); + const ruleHeader = self.domElement.querySelectorAll('.widget-rule-header')[0].cloneNode(true); indicator.textContent = ''; indicator.appendChild(ruleHeader); }); diff --git a/src/plugins/summaryWidget/src/SummaryWidget.js b/src/plugins/summaryWidget/src/SummaryWidget.js index 3df8a8817d9..1539454fa37 100644 --- a/src/plugins/summaryWidget/src/SummaryWidget.js +++ b/src/plugins/summaryWidget/src/SummaryWidget.js @@ -228,19 +228,19 @@ SummaryWidget.prototype.addOrRemoveDragIndicator = function () { }); }; - /** - * Update the widget's appearance from the configuration of the active rule - */ - SummaryWidget.prototype.updateWidget = function () { - const WIDGET_ICON_CLASS = 'c-sw__icon js-sw__icon'; - const activeRule = this.rulesById[this.activeId]; - - this.applyStyle(this.domElement.querySelector('#widget'), activeRule.getProperty('style')); - this.domElement.querySelector('#widget').title = activeRule.getProperty('message'); - this.domElement.querySelector('#widgetLabel').textContent = activeRule.getProperty('label'); - this.domElement.querySelector('#widgetIcon').classList = - WIDGET_ICON_CLASS + ' ' + activeRule.getProperty('icon'); - }; +/** + * Update the widget's appearance from the configuration of the active rule + */ +SummaryWidget.prototype.updateWidget = function () { + const WIDGET_ICON_CLASS = 'c-sw__icon js-sw__icon'; + const activeRule = this.rulesById[this.activeId]; + + this.applyStyle(this.domElement.querySelector('#widget'), activeRule.getProperty('style')); + this.domElement.querySelector('#widget').title = activeRule.getProperty('message'); + this.domElement.querySelector('#widgetLabel').textContent = activeRule.getProperty('label'); + this.domElement.querySelector('#widgetIcon').classList = + WIDGET_ICON_CLASS + ' ' + activeRule.getProperty('icon'); +}; /** * Get the active rule and update the Widget's appearance. diff --git a/src/plugins/summaryWidget/src/views/SummaryWidgetView.js b/src/plugins/summaryWidget/src/views/SummaryWidgetView.js index 8bfbee0c389..eb563ce4b58 100644 --- a/src/plugins/summaryWidget/src/views/SummaryWidgetView.js +++ b/src/plugins/summaryWidget/src/views/SummaryWidgetView.js @@ -127,4 +127,4 @@ class SummaryWidgetView { } } -export default SummaryWidgetView; \ No newline at end of file +export default SummaryWidgetView; diff --git a/src/plugins/summaryWidget/test/ConditionManagerSpec.js b/src/plugins/summaryWidget/test/ConditionManagerSpec.js index a446e7a03e5..0751e88d509 100644 --- a/src/plugins/summaryWidget/test/ConditionManagerSpec.js +++ b/src/plugins/summaryWidget/test/ConditionManagerSpec.js @@ -181,12 +181,7 @@ describe('A Summary Widget Condition Manager', function () { } }; - mockComposition = jasmine.createSpyObj('composition', [ - 'on', - 'off', - 'load', - 'triggerCallback' - ]); + mockComposition = jasmine.createSpyObj('composition', ['on', 'off', 'load', 'triggerCallback']); mockComposition.on.and.callFake(function (event, callback, context) { mockEventCallbacks[event] = callback.bind(context); }); diff --git a/src/plugins/summaryWidget/test/SummaryWidgetSpec.js b/src/plugins/summaryWidget/test/SummaryWidgetSpec.js index 31ffd90ee29..e3536a132c2 100644 --- a/src/plugins/summaryWidget/test/SummaryWidgetSpec.js +++ b/src/plugins/summaryWidget/test/SummaryWidgetSpec.js @@ -89,9 +89,9 @@ describe('The Summary Widget', function () { summaryWidget.show(mockContainer); }); - xit('queries with legacyId', function () { - expect(mockObjectService.getObjects).toHaveBeenCalledWith(['testNamespace:testKey']); - }); + xit('queries with legacyId', function () { + expect(mockObjectService.getObjects).toHaveBeenCalledWith(['testNamespace:testKey']); + }); it('adds its DOM element to the view', function () { expect(mockContainer.getElementsByClassName('w-summary-widget').length).toBeGreaterThan(0); diff --git a/src/plugins/telemetryTable/TelemetryTable.js b/src/plugins/telemetryTable/TelemetryTable.js index 278963bc667..e1616cff43c 100644 --- a/src/plugins/telemetryTable/TelemetryTable.js +++ b/src/plugins/telemetryTable/TelemetryTable.js @@ -64,10 +64,10 @@ export default class TelemetryTable extends EventEmitter { this.filterObserver = undefined; - this.createTableRowCollections(); - this.resubscribeToStaleness = this.resubscribeAllObjectsToStaleness.bind(this); - this.openmct.time.on('clockChanged', this.resubscribeToStaleness); - } + this.createTableRowCollections(); + this.resubscribeToStaleness = this.resubscribeAllObjectsToStaleness.bind(this); + this.openmct.time.on('clockChanged', this.resubscribeToStaleness); + } /** * @private @@ -156,7 +156,7 @@ export default class TelemetryTable extends EventEmitter { this.telemetryCollections[keyString].on('clear', this.clearData); this.telemetryCollections[keyString].load(); - this.subscribeToStaleness(telemetryObject); + this.subscribeToStaleness(telemetryObject); this.telemetryObjects[keyString] = { telemetryObject, @@ -166,59 +166,59 @@ export default class TelemetryTable extends EventEmitter { limitEvaluator }; - this.emit('object-added', telemetryObject); + this.emit('object-added', telemetryObject); + } + + resubscribeAllObjectsToStaleness() { + if (!this.subscribedStaleObjects || this.subscribedStaleObjects.size < 1) { + return; + } + for (const [, telemetryObject] of this.subscribedStaleObjects) { + this.subscribeToStaleness(telemetryObject); } + } - resubscribeAllObjectsToStaleness() { - if (!this.subscribedStaleObjects || this.subscribedStaleObjects.size < 1) { - return; - } - for (const [, telemetryObject] of this.subscribedStaleObjects) { - this.subscribeToStaleness(telemetryObject); - } + subscribeToStaleness(domainObject) { + const keyString = this.openmct.objects.makeKeyString(domainObject.identifier); + if (this.stalenessSubscription?.[keyString]) { + this.unsubscribeFromStaleness(domainObject.identifier); } - subscribeToStaleness(domainObject) { - const keyString = this.openmct.objects.makeKeyString(domainObject.identifier); - if (this.stalenessSubscription?.[keyString]) { - this.unsubscribeFromStaleness(domainObject.identifier); + this.stalenessSubscription[keyString] = {}; + this.stalenessSubscription[keyString].stalenessUtils = new StalenessUtils.default( + this.openmct, + domainObject + ); + this.openmct.telemetry.isStale(domainObject).then((stalenessResponse) => { + if (stalenessResponse !== undefined) { + this.handleStaleness(keyString, stalenessResponse); } + }); + const stalenessSubscription = this.openmct.telemetry.subscribeToStaleness( + domainObject, + (stalenessResponse) => { + this.handleStaleness(keyString, stalenessResponse); + } + ); + this.subscribedStaleObjects.set(keyString, domainObject); - this.stalenessSubscription[keyString] = {}; - this.stalenessSubscription[keyString].stalenessUtils = new StalenessUtils.default( - this.openmct, - domainObject - ); - this.openmct.telemetry.isStale(domainObject).then((stalenessResponse) => { - if (stalenessResponse !== undefined) { - this.handleStaleness(keyString, stalenessResponse); - } - }); - const stalenessSubscription = this.openmct.telemetry.subscribeToStaleness( - domainObject, - (stalenessResponse) => { - this.handleStaleness(keyString, stalenessResponse); - } - ); - this.subscribedStaleObjects.set(keyString, domainObject); - - this.stalenessSubscription[keyString].unsubscribe = stalenessSubscription; - } + this.stalenessSubscription[keyString].unsubscribe = stalenessSubscription; + } - handleStaleness(keyString, stalenessResponse, skipCheck = false) { - if ( - skipCheck || - this.stalenessSubscription[keyString].stalenessUtils.shouldUpdateStaleness( - stalenessResponse, - keyString - ) - ) { - this.emit('telemetry-staleness', { - keyString, - stalenessResponse: stalenessResponse - }); - } + handleStaleness(keyString, stalenessResponse, skipCheck = false) { + if ( + skipCheck || + this.stalenessSubscription[keyString].stalenessUtils.shouldUpdateStaleness( + stalenessResponse, + keyString + ) + ) { + this.emit('telemetry-staleness', { + keyString, + stalenessResponse: stalenessResponse + }); } + } getTelemetryProcessor(keyString, columnMap, limitEvaluator) { return (telemetry) => { @@ -320,8 +320,8 @@ export default class TelemetryTable extends EventEmitter { }); } - removeTelemetryObject(objectIdentifier) { - const keyString = this.openmct.objects.makeKeyString(objectIdentifier); + removeTelemetryObject(objectIdentifier) { + const keyString = this.openmct.objects.makeKeyString(objectIdentifier); this.configuration.removeColumnsForObject(objectIdentifier, true); this.tableRows.removeRowsByObject(keyString); @@ -329,14 +329,14 @@ export default class TelemetryTable extends EventEmitter { this.removeTelemetryCollection(keyString); delete this.telemetryObjects[keyString]; - this.emit('object-removed', objectIdentifier); + this.emit('object-removed', objectIdentifier); - this.unsubscribeFromStaleness(objectIdentifier); - } + this.unsubscribeFromStaleness(objectIdentifier); + } - unsubscribeFromStaleness(objectIdentifier) { - const keyString = this.openmct.objects.makeKeyString(objectIdentifier); - const SKIP_CHECK = true; + unsubscribeFromStaleness(objectIdentifier) { + const keyString = this.openmct.objects.makeKeyString(objectIdentifier); + const SKIP_CHECK = true; this.stalenessSubscription[keyString].unsubscribe(); this.stalenessSubscription[keyString].stalenessUtils.destroy(); @@ -440,8 +440,8 @@ export default class TelemetryTable extends EventEmitter { destroy() { this.tableRows.destroy(); - this.tableRows.off('resetRowsFromAllData', this.resetRowsFromAllData); - this.openmct.time.off('clockChanged', this.resubscribeToStaleness); + this.tableRows.off('resetRowsFromAllData', this.resetRowsFromAllData); + this.openmct.time.off('clockChanged', this.resubscribeToStaleness); let keystrings = Object.keys(this.telemetryCollections); keystrings.forEach(this.removeTelemetryCollection); diff --git a/src/plugins/telemetryTable/collections/TableRowCollection.js b/src/plugins/telemetryTable/collections/TableRowCollection.js index d3925024510..07cea3c5ae9 100644 --- a/src/plugins/telemetryTable/collections/TableRowCollection.js +++ b/src/plugins/telemetryTable/collections/TableRowCollection.js @@ -154,43 +154,43 @@ export default class TableRowCollection extends EventEmitter { }); } - mergeSortedRows(incomingRows) { - const mergedRows = []; - let existingRowIndex = 0; - let incomingRowIndex = 0; - - while (existingRowIndex < this.rows.length && incomingRowIndex < incomingRows.length) { - const existingRow = this.rows[existingRowIndex]; - const incomingRow = incomingRows[incomingRowIndex]; - - const inPlaceIndex = this.getInPlaceUpdateIndex(incomingRow); - if (inPlaceIndex > -1) { - this.updateRowInPlace(incomingRow, inPlaceIndex); - incomingRowIndex++; + mergeSortedRows(incomingRows) { + const mergedRows = []; + let existingRowIndex = 0; + let incomingRowIndex = 0; + + while (existingRowIndex < this.rows.length && incomingRowIndex < incomingRows.length) { + const existingRow = this.rows[existingRowIndex]; + const incomingRow = incomingRows[incomingRowIndex]; + + const inPlaceIndex = this.getInPlaceUpdateIndex(incomingRow); + if (inPlaceIndex > -1) { + this.updateRowInPlace(incomingRow, inPlaceIndex); + incomingRowIndex++; + } else { + if (this.firstRowInSortOrder(existingRow, incomingRow) === existingRow) { + mergedRows.push(existingRow); + existingRowIndex++; } else { - if (this.firstRowInSortOrder(existingRow, incomingRow) === existingRow) { - mergedRows.push(existingRow); - existingRowIndex++; - } else { - mergedRows.push(incomingRow); - incomingRowIndex++; - } + mergedRows.push(incomingRow); + incomingRowIndex++; } } + } - // tail of existing rows is all that is left to merge - if (existingRowIndex < this.rows.length) { - for (existingRowIndex; existingRowIndex < this.rows.length; existingRowIndex++) { - mergedRows.push(this.rows[existingRowIndex]); - } + // tail of existing rows is all that is left to merge + if (existingRowIndex < this.rows.length) { + for (existingRowIndex; existingRowIndex < this.rows.length; existingRowIndex++) { + mergedRows.push(this.rows[existingRowIndex]); } + } - // tail of incoming rows is all that is left to merge - if (incomingRowIndex < incomingRows.length) { - for (incomingRowIndex; incomingRowIndex < incomingRows.length; incomingRowIndex++) { - mergedRows.push(incomingRows[incomingRowIndex]); - } + // tail of incoming rows is all that is left to merge + if (incomingRowIndex < incomingRows.length) { + for (incomingRowIndex; incomingRowIndex < incomingRows.length; incomingRowIndex++) { + mergedRows.push(incomingRows[incomingRowIndex]); } + } this.rows = mergedRows; } From cad0288900dccca0243c0b02f31b2c24295f8f6c Mon Sep 17 00:00:00 2001 From: Tristan F <26509014+LeoDog896@users.noreply.github.com> Date: Thu, 21 Dec 2023 18:07:18 -0500 Subject: [PATCH 10/20] fix: import vue correctly, get correct layout --- src/MCT.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/MCT.js b/src/MCT.js index d3e3548824e..0af6783968b 100644 --- a/src/MCT.js +++ b/src/MCT.js @@ -21,7 +21,7 @@ *****************************************************************************/ /* eslint-disable no-undef */ import EventEmitter from 'EventEmitter'; -import Vue from 'vue'; +import * as Vue from 'vue'; import api from './api/api'; import BrandingAPI from './api/Branding'; @@ -37,9 +37,7 @@ import MoveActionPlugin from './plugins/move/plugin'; import plugins from './plugins/plugins'; import RemoveActionPlugin from './plugins/remove/plugin'; import Selection from './selection/Selection'; -import components from './ui/components/components'; -import AppLayout from './ui/layout/AppLayout.vue'; -import Layout from './ui/layout/Layout.vue'; +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'; @@ -64,7 +62,6 @@ import Browse from './ui/router/Browse'; */ export class MCT extends EventEmitter { plugins = plugins; - components = components; /** * Tracks current selection state of the application. @@ -331,7 +328,7 @@ export class MCT extends EventEmitter { if (!isHeadlessMode) { const appLayout = Vue.createApp({ components: { - Layout: Layout + Layout }, provide: { openmct: Vue.markRaw(this) From a4a3c85fe2e7d420b478c3b02b29639d298ef156 Mon Sep 17 00:00:00 2001 From: Tristan F <26509014+LeoDog896@users.noreply.github.com> Date: Thu, 21 Dec 2023 18:38:30 -0500 Subject: [PATCH 11/20] fix: createApp without JSON fixes template issues --- src/MCT.js | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/MCT.js b/src/MCT.js index 0af6783968b..514ecfceb2a 100644 --- a/src/MCT.js +++ b/src/MCT.js @@ -21,7 +21,7 @@ *****************************************************************************/ /* eslint-disable no-undef */ import EventEmitter from 'EventEmitter'; -import * as Vue from 'vue'; +import { createApp, markRaw } from 'vue/dist/vue.esm-bundler'; import api from './api/api'; import BrandingAPI from './api/Branding'; @@ -326,18 +326,11 @@ export class MCT extends EventEmitter { * @memberof module:openmct.MCT~ */ if (!isHeadlessMode) { - const appLayout = Vue.createApp({ - components: { - Layout - }, - provide: { - openmct: Vue.markRaw(this) - }, - template: '' - }); + const appLayout = createApp(Layout); + appLayout.provide('openmct', markRaw(this)); const component = appLayout.mount(domElement); component.$nextTick(() => { - this.layout = component.$refs.layout; + this.layout = component; this.app = appLayout; Browse(this); window.addEventListener('beforeunload', this.destroy); From 4e3e00db4925be1788f7815567d8224aa8911edc Mon Sep 17 00:00:00 2001 From: Tristan F <26509014+LeoDog896@users.noreply.github.com> Date: Thu, 21 Dec 2023 20:22:49 -0500 Subject: [PATCH 12/20] fix: don't use default on InspectorDataVisualization --- src/plugins/plugins.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/plugins.js b/src/plugins/plugins.js index f299aedd1a3..454f926bb31 100644 --- a/src/plugins/plugins.js +++ b/src/plugins/plugins.js @@ -169,6 +169,6 @@ plugins.OperatorStatus = OperatorStatus; plugins.Gauge = GaugePlugin; plugins.Timelist = TimeList; plugins.InspectorViews = InspectorViews; -plugins.InspectorDataVisualization = InspectorDataVisualization.default; +plugins.InspectorDataVisualization = InspectorDataVisualization; export default plugins; From 8b84d41c64f75282b9dc6adbb411d71a12367ed1 Mon Sep 17 00:00:00 2001 From: Tristan F <26509014+LeoDog896@users.noreply.github.com> Date: Thu, 21 Dec 2023 20:52:19 -0500 Subject: [PATCH 13/20] fix: remove more .default calls --- src/plugins/plugins.js | 2 +- src/plugins/telemetryTable/TelemetryTable.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/plugins.js b/src/plugins/plugins.js index 454f926bb31..f3b3cf499bd 100644 --- a/src/plugins/plugins.js +++ b/src/plugins/plugins.js @@ -92,7 +92,7 @@ plugins.example.ExampleUser = ExampleUser; plugins.example.ExampleImagery = ExampleImagery; plugins.example.ExampleFaultSource = ExampleFaultSource; plugins.example.EventGeneratorPlugin = EventGeneratorPlugin; -plugins.example.ExampleDataVisualizationSourcePlugin = ExampleDataVisualizationSourcePlugin.default; +plugins.example.ExampleDataVisualizationSourcePlugin = ExampleDataVisualizationSourcePlugin; plugins.example.ExampleTags = ExampleTags; plugins.example.Generator = () => GeneratorPlugin; diff --git a/src/plugins/telemetryTable/TelemetryTable.js b/src/plugins/telemetryTable/TelemetryTable.js index e1616cff43c..11495fc143b 100644 --- a/src/plugins/telemetryTable/TelemetryTable.js +++ b/src/plugins/telemetryTable/TelemetryTable.js @@ -185,7 +185,7 @@ export default class TelemetryTable extends EventEmitter { } this.stalenessSubscription[keyString] = {}; - this.stalenessSubscription[keyString].stalenessUtils = new StalenessUtils.default( + this.stalenessSubscription[keyString].stalenessUtils = new StalenessUtils( this.openmct, domainObject ); From 951c92b699e634c55e405d2e5e7b8371530e1075 Mon Sep 17 00:00:00 2001 From: Tristan F Date: Fri, 22 Dec 2023 19:31:40 -0500 Subject: [PATCH 14/20] Update src/api/api.js Co-authored-by: Jesse Mazzella --- src/api/api.js | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/api/api.js b/src/api/api.js index e9d7fad84a6..7674f1cde1a 100644 --- a/src/api/api.js +++ b/src/api/api.js @@ -38,20 +38,20 @@ import TypeRegistry from './types/TypeRegistry'; import UserAPI from './user/UserAPI'; export default { - ActionsAPI: ActionsAPI, - CompositionAPI: CompositionAPI, - EditorAPI: EditorAPI, - FaultManagementAPI: FaultManagementAPI, - FormsAPI: FormsAPI, - IndicatorAPI: IndicatorAPI, - MenuAPI: MenuAPI, - NotificationAPI: NotificationAPI, - ObjectAPI: ObjectAPI, - PriorityAPI: PriorityAPI, - StatusAPI: StatusAPI, - TelemetryAPI: TelemetryAPI, - TimeAPI: TimeAPI, - TypeRegistry: TypeRegistry, - UserAPI: UserAPI, - AnnotationAPI: AnnotationAPI + ActionsAPI, + CompositionAPI, + EditorAPI, + FaultManagementAPI, + FormsAPI, + IndicatorAPI, + MenuAPI, + NotificationAPI, + ObjectAPI, + PriorityAPI, + StatusAPI, + TelemetryAPI, + TimeAPI, + TypeRegistry, + UserAPI, + AnnotationAPI }; From 3e97dfc85f9e1f44d2057a15e2174954c10ef649 Mon Sep 17 00:00:00 2001 From: Tristan F Date: Fri, 22 Dec 2023 19:31:50 -0500 Subject: [PATCH 15/20] Update src/plugins/plugins.js Co-authored-by: Jesse Mazzella --- src/plugins/plugins.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/plugins.js b/src/plugins/plugins.js index f3b3cf499bd..915810ff06c 100644 --- a/src/plugins/plugins.js +++ b/src/plugins/plugins.js @@ -56,7 +56,7 @@ import LocalStorage from './localStorage/plugin'; import LocalTimeSystem from './localTimeSystem/plugin'; import MyItems from './myItems/plugin'; import NewFolderAction from './newFolderAction/plugin'; -import * as Notebook from './notebook/plugin'; +import { NotebookPlugin, RestrictedNotebookPlugin } from './notebook/plugin'; import NotificationIndicator from './notificationIndicator/plugin'; import ObjectMigration from './objectMigration/plugin'; import OpenInNewTabAction from './openInNewTabAction/plugin'; From b141453b843e1cd4c7e16213d46df39e5cf8d3cc Mon Sep 17 00:00:00 2001 From: Tristan F Date: Fri, 22 Dec 2023 19:31:58 -0500 Subject: [PATCH 16/20] Update src/plugins/plugins.js Co-authored-by: Jesse Mazzella --- src/plugins/plugins.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/plugins.js b/src/plugins/plugins.js index 915810ff06c..e1c4c85fbda 100644 --- a/src/plugins/plugins.js +++ b/src/plugins/plugins.js @@ -128,8 +128,8 @@ plugins.TelemetryTable = TelemetryTablePlugin; plugins.SummaryWidget = SummaryWidget; plugins.TelemetryMean = TelemetryMean; plugins.URLIndicator = URLIndicatorPlugin; -plugins.Notebook = Notebook.NotebookPlugin; -plugins.RestrictedNotebook = Notebook.RestrictedNotebookPlugin; +plugins.Notebook = NotebookPlugin; +plugins.RestrictedNotebook = RestrictedNotebookPlugin; plugins.DisplayLayout = DisplayLayoutPlugin; plugins.FaultManagement = FaultManagementPlugin; plugins.FormActions = FormActions; From dec34edc8f43c5c5026f2aaa7e07905bf4acf435 Mon Sep 17 00:00:00 2001 From: Tristan F <26509014+LeoDog896@users.noreply.github.com> Date: Fri, 22 Dec 2023 20:45:33 -0500 Subject: [PATCH 17/20] fix: suggestions --- src/MCT.js | 51 +++++---- src/ui/registries/ToolbarRegistry.js | 30 ++++++ src/ui/registries/ViewRegistry.js | 149 +++++++++++++++++++++++++++ 3 files changed, 212 insertions(+), 18 deletions(-) diff --git a/src/MCT.js b/src/MCT.js index 514ecfceb2a..37b04a9fc20 100644 --- a/src/MCT.js +++ b/src/MCT.js @@ -23,10 +23,25 @@ import EventEmitter from 'EventEmitter'; import { createApp, markRaw } from 'vue/dist/vue.esm-bundler'; -import api from './api/api'; +import ActionsAPI from './api/actions/ActionsAPI'; +import AnnotationAPI from './api/annotation/AnnotationAPI'; import BrandingAPI from './api/Branding'; +import CompositionAPI from './api/composition/CompositionAPI'; +import EditorAPI from './api/Editor'; +import FaultManagementAPI from './api/faultmanagement/FaultManagementAPI'; +import FormsAPI from './api/forms/FormsAPI'; +import IndicatorAPI from './api/indicators/IndicatorAPI'; +import MenuAPI from './api/menu/MenuAPI'; +import NotificationAPI from './api/notifications/NotificationAPI'; +import ObjectAPI from './api/objects/ObjectAPI'; import OverlayAPI from './api/overlays/OverlayAPI'; +import PriorityAPI from './api/priority/PriorityAPI'; +import StatusAPI from './api/status/StatusAPI'; +import TelemetryAPI from './api/telemetry/TelemetryAPI'; +import TimeAPI from './api/time/TimeAPI'; import ToolTipAPI from './api/tooltips/ToolTipAPI'; +import TypeRegistry from './api/types/TypeRegistry'; +import UserAPI from './api/user/UserAPI'; import DuplicateActionPlugin from './plugins/duplicate/plugin'; import ExportAsJSONAction from './plugins/exportAsJSONAction/plugin'; import ImageryPlugin from './plugins/imagery/plugin'; @@ -76,7 +91,7 @@ export class MCT extends EventEmitter { * @memberof module:openmct.MCT# * @name conductor */ - time = new api.TimeAPI(this); + time = new TimeAPI(this); /** * An interface for interacting with the composition of domain objects. @@ -91,7 +106,7 @@ export class MCT extends EventEmitter { * @memberof module:openmct.MCT# * @name composition */ - composition = new api.CompositionAPI(this); + composition = new CompositionAPI(this); /** * Registry for views of domain objects which should appear in the @@ -142,7 +157,7 @@ export class MCT extends EventEmitter { * @memberof module:openmct.MCT# * @name types */ - types = new api.TypeRegistry(); + types = new TypeRegistry(); /** * An interface for interacting with domain objects and the domain @@ -152,7 +167,7 @@ export class MCT extends EventEmitter { * @memberof module:openmct.MCT# * @name objects */ - objects = new api.ObjectAPI(this.types, this); + objects = new ObjectAPI(this.types, this); /** * An interface for retrieving and interpreting telemetry data associated @@ -162,7 +177,7 @@ export class MCT extends EventEmitter { * @memberof module:openmct.MCT# * @name telemetry */ - telemetry = new api.TelemetryAPI(this); + telemetry = new TelemetryAPI(this); /** * An interface for creating new indicators and changing them dynamically. @@ -171,7 +186,7 @@ export class MCT extends EventEmitter { * @memberof module:openmct.MCT# * @name indicators */ - indicators = new api.IndicatorAPI(this); + indicators = new IndicatorAPI(this); /** * MCT's user awareness management, to enable user and @@ -180,19 +195,19 @@ export class MCT extends EventEmitter { * @memberof module:openmct.MCT# * @name user */ - user = new api.UserAPI(this); + user = new UserAPI(this); - notifications = new api.NotificationAPI(); - editor = new api.EditorAPI(this); + notifications = new NotificationAPI(); + editor = new EditorAPI(this); overlays = new OverlayAPI(); tooltips = new ToolTipAPI(); - menus = new api.MenuAPI(this); - actions = new api.ActionsAPI(this); - status = new api.StatusAPI(this); - priority = api.PriorityAPI; + menus = new MenuAPI(this); + actions = new ActionsAPI(this); + status = new StatusAPI(this); + priority = PriorityAPI; router = new ApplicationRouter(this); - faults = new api.FaultManagementAPI(this); - forms = new api.FormsAPI(this); + faults = new FaultManagementAPI(this); + forms = new FormsAPI(this); branding = BrandingAPI; /** @@ -202,7 +217,7 @@ export class MCT extends EventEmitter { * @memberof module:openmct.MCT# * @name annotation */ - annotation = new api.AnnotationAPI(this); + annotation = new AnnotationAPI(this); constructor() { super(); @@ -225,7 +240,7 @@ export class MCT extends EventEmitter { * @memberof module:openmct.MCT# * @name annotation */ - this.annotation = new api.AnnotationAPI(this); + this.annotation = new AnnotationAPI(this); // Plugins that are installed by default this.install(this.plugins.Plot()); diff --git a/src/ui/registries/ToolbarRegistry.js b/src/ui/registries/ToolbarRegistry.js index 30a2a7c80eb..1df0eb7bc3c 100644 --- a/src/ui/registries/ToolbarRegistry.js +++ b/src/ui/registries/ToolbarRegistry.js @@ -85,3 +85,33 @@ ToolbarRegistry.prototype.addProvider = function (provider) { this.providers[key] = provider; }; + +/** + * Exposes types of toolbars in Open MCT. + * + * @interface ToolbarProvider + * @property {string} key a unique identifier for this toolbar + * @property {string} name the human-readable name of this toolbar + * @property {string} [description] a longer-form description (typically + * a single sentence or short paragraph) of this kind of toolbar + * @memberof module:openmct + */ + +/** + * Checks if this provider can supply toolbar for a selection. + * + * @method forSelection + * @memberof module:openmct.ToolbarProvider# + * @param {module:openmct.selection} selection + * @returns {boolean} 'true' if the toolbar applies to the provided selection, + * otherwise 'false'. + */ + +/** + * Provides controls that comprise a toolbar. + * + * @method toolbar + * @memberof module:openmct.ToolbarProvider# + * @param {object} selection the selection object + * @returns {Object[]} an array of objects defining controls for the toolbar. + */ diff --git a/src/ui/registries/ViewRegistry.js b/src/ui/registries/ViewRegistry.js index 9cbfa17a418..c6cb44daa52 100644 --- a/src/ui/registries/ViewRegistry.js +++ b/src/ui/registries/ViewRegistry.js @@ -121,3 +121,152 @@ ViewRegistry.prototype.getByVPID = function (vpid) { return p.vpid === vpid; })[0]; }; + +/** + * A View is used to provide displayable content, and to react to + * associated life cycle events. + * + * @name View + * @interface + * @memberof module:openmct + */ + +/** + * Populate the supplied DOM element with the contents of this view. + * + * View implementations should use this method to attach any + * listeners or acquire other resources that are necessary to keep + * the contents of this view up-to-date. + * + * @param {HTMLElement} container the DOM element to populate + * @method show + * @memberof module:openmct.View# + */ + +/** + * Indicates whether or not the application is in edit mode. This supports + * views that have distinct visual and behavioral elements when the + * navigated object is being edited. + * + * For cases where a completely separate view is desired for editing purposes, + * see {@link openmct.ViewProvider#edit} + * + * @param {boolean} isEditing + * @method show + * @memberof module:openmct.View# + */ + +/** + * Release any resources associated with this view. + * + * View implementations should use this method to detach any + * listeners or release other resources that are no longer necessary + * once a view is no longer used. + * + * @method destroy + * @memberof module:openmct.View# + */ + +/** + * Returns the selection context. + * + * View implementations should use this method to customize + * the selection context. + * + * @method getSelectionContext + * @memberof module:openmct.View# + */ + +/** + * Exposes types of views in Open MCT. + * + * @interface ViewProvider + * @property {string} key a unique identifier for this view + * @property {string} name the human-readable name of this view + * @property {string} [description] a longer-form description (typically + * a single sentence or short paragraph) of this kind of view + * @property {string} [cssClass] the CSS class to apply to labels for this + * view (to add icons, for instance) + * @memberof module:openmct + */ + +/** + * Check if this provider can supply views for a domain object. + * + * When called by Open MCT, this may include additional arguments + * which are on the path to the object to be viewed; for instance, + * when viewing "A Folder" within "My Items", this method will be + * invoked with "A Folder" (as a domain object) as the first argument + * + * @method canView + * @memberof module:openmct.ViewProvider# + * @param {module:openmct.DomainObject} domainObject the domain object + * to be viewed + * @param {array} objectPath - The current contextual object path of the view object + * eg current domainObject is located under MyItems which is under Root + * @returns {boolean} 'true' if the view applies to the provided object, + * otherwise 'false'. + */ + +/** + * An optional function that defines whether or not this view can be used to edit a given object. + * If not provided, will default to `false` and the view will not support editing. To support editing, + * return true from this function and then - + * * Return a {@link openmct.View} from the `view` function, using the `onEditModeChange` callback to + * add and remove editing elements from the view + * OR + * * Return a {@link openmct.View} from the `view` function defining a read-only view. + * AND + * * Define an {@link openmct.ViewProvider#Edit} function on the view provider that returns an + * editing-specific view. + * + * @method canEdit + * @memberof module:openmct.ViewProvider# + * @param {module:openmct.DomainObject} domainObject the domain object + * to be edited + * @param {array} objectPath - The current contextual object path of the view object + * eg current domainObject is located under MyItems which is under Root + * @returns {boolean} 'true' if the view can be used to edit the provided object, + * otherwise 'false'. + */ + +/** + * Optional method determining the priority of a given view. If this + * function is not defined on a view provider, then a default priority + * of 100 will be applicable for all objects supported by this view. + * + * @method priority + * @memberof module:openmct.ViewProvider# + * @param {module:openmct.DomainObject} domainObject the domain object + * to be viewed + * @returns {number} The priority of the view. If multiple views could apply + * to an object, the view that returns the lowest number will be + * the default view. + */ + +/** + * Provide a view of this object. + * + * When called by Open MCT, the following arguments will be passed to it: + * @param {object} domainObject - the domainObject that the view is provided for + * @param {array} objectPath - The current contextual object path of the view object + * eg current domainObject is located under MyItems which is under Root + * + * @method view + * @memberof module:openmct.ViewProvider# + * @param {*} object the object to be viewed + * @returns {module:openmct.View} a view of this domain object + */ + +/** + * Provide an edit-mode specific view of this object. + * + * If optionally specified, this function will be called when the application + * enters edit mode. This will cause the active non-edit mode view and its + * dom element to be destroyed. + * + * @method edit + * @memberof module:openmct.ViewProvider# + * @param {*} object the object to be edit + * @returns {module:openmct.View} an editable view of this domain object + */ From 3d75f35b73f359c125b08371b963f91607d35632 Mon Sep 17 00:00:00 2001 From: Tristan F <26509014+LeoDog896@users.noreply.github.com> Date: Fri, 22 Dec 2023 20:46:16 -0500 Subject: [PATCH 18/20] fix: drop unnecessary this.annotation initialization --- src/MCT.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/MCT.js b/src/MCT.js index 37b04a9fc20..f7ae4f657ab 100644 --- a/src/MCT.js +++ b/src/MCT.js @@ -210,15 +210,6 @@ export class MCT extends EventEmitter { forms = new FormsAPI(this); branding = BrandingAPI; - /** - * 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 AnnotationAPI(this); - constructor() { super(); EventEmitter.call(this); From 3564a4730dbac2020250174dad8f41fcc66bef11 Mon Sep 17 00:00:00 2001 From: Tristan F <26509014+LeoDog896@users.noreply.github.com> Date: Fri, 22 Dec 2023 20:47:13 -0500 Subject: [PATCH 19/20] fix: move all initialization calls to constructor --- src/MCT.js | 262 ++++++++++++++++++++++++++--------------------------- 1 file changed, 131 insertions(+), 131 deletions(-) diff --git a/src/MCT.js b/src/MCT.js index f7ae4f657ab..b83cb215cdd 100644 --- a/src/MCT.js +++ b/src/MCT.js @@ -76,153 +76,153 @@ import Browse from './ui/router/Browse'; * @memberof module:openmct */ export class MCT extends EventEmitter { - plugins = plugins; + constructor() { + super(); + EventEmitter.call(this); - /** - * Tracks current selection state of the application. - * @private - */ - selection = new Selection(this); + this.buildInfo = { + version: __OPENMCT_VERSION__, + buildDate: __OPENMCT_BUILD_DATE__, + revision: __OPENMCT_REVISION__, + branch: __OPENMCT_BUILD_BRANCH__ + }; - /** - * 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 TimeAPI(this); + this.destroy = this.destroy.bind(this); + this.defaultClock = 'local'; - /** - * An interface for interacting with the composition of domain objects. - * The composition of a domain object is the list of other domain - * objects it "contains" (for instance, that should be displayed - * beneath it in the tree.) - * - * `composition` may be called as a function, in which case it acts - * as [`composition.get`]{@link module:openmct.CompositionAPI#get}. - * - * @type {module:openmct.CompositionAPI} - * @memberof module:openmct.MCT# - * @name composition - */ - composition = new CompositionAPI(this); + this.plugins = plugins; - /** - * Registry for views of domain objects which should appear in the - * main viewing area. - * - * @type {module:openmct.ViewRegistry} - * @memberof module:openmct.MCT# - * @name objectViews - */ - objectViews = new ViewRegistry(); + /** + * Tracks current selection state of the application. + * @private + */ + this.selection = new Selection(this); - /** - * Registry for views which should appear in the Inspector area. - * These views will be chosen based on the selection state. - * - * @type {module:openmct.InspectorViewRegistry} - * @memberof module:openmct.MCT# - * @name inspectorViews - */ - inspectorViews = new InspectorViewRegistry(); + /** + * MCT's time conductor, which may be used to synchronize view contents + * for telemetry- or time-based views. + * @type {module:openmct.TimeConductor} + * @memberof module:openmct.MCT# + * @name conductor + */ + this.time = new TimeAPI(this); - /** - * Registry for views which should appear in Edit Properties - * dialogs, and similar user interface elements used for - * modifying domain objects external to its regular views. - * - * @type {module:openmct.ViewRegistry} - * @memberof module:openmct.MCT# - * @name propertyEditors - */ - propertyEditors = new ViewRegistry(); + /** + * An interface for interacting with the composition of domain objects. + * The composition of a domain object is the list of other domain + * objects it "contains" (for instance, that should be displayed + * beneath it in the tree.) + * + * `composition` may be called as a function, in which case it acts + * as [`composition.get`]{@link module:openmct.CompositionAPI#get}. + * + * @type {module:openmct.CompositionAPI} + * @memberof module:openmct.MCT# + * @name composition + */ + this.composition = new CompositionAPI(this); - /** - * Registry for views which should appear in the toolbar area while - * editing. These views will be chosen based on the selection state. - * - * @type {module:openmct.ToolbarRegistry} - * @memberof module:openmct.MCT# - * @name toolbars - */ - toolbars = new ToolbarRegistry(); + /** + * Registry for views of domain objects which should appear in the + * main viewing area. + * + * @type {module:openmct.ViewRegistry} + * @memberof module:openmct.MCT# + * @name objectViews + */ + this.objectViews = new ViewRegistry(); - /** - * Registry for domain object types which may exist within this - * instance of Open MCT. - * - * @type {module:openmct.TypeRegistry} - * @memberof module:openmct.MCT# - * @name types - */ - types = new TypeRegistry(); + /** + * Registry for views which should appear in the Inspector area. + * These views will be chosen based on the selection state. + * + * @type {module:openmct.InspectorViewRegistry} + * @memberof module:openmct.MCT# + * @name inspectorViews + */ + this.inspectorViews = new InspectorViewRegistry(); - /** - * An interface for interacting with domain objects and the domain - * object hierarchy. - * - * @type {module:openmct.ObjectAPI} - * @memberof module:openmct.MCT# - * @name objects - */ - objects = new ObjectAPI(this.types, this); + /** + * Registry for views which should appear in Edit Properties + * dialogs, and similar user interface elements used for + * modifying domain objects external to its regular views. + * + * @type {module:openmct.ViewRegistry} + * @memberof module:openmct.MCT# + * @name propertyEditors + */ + this.propertyEditors = new ViewRegistry(); - /** - * An interface for retrieving and interpreting telemetry data associated - * with a domain object. - * - * @type {module:openmct.TelemetryAPI} - * @memberof module:openmct.MCT# - * @name telemetry - */ - telemetry = new TelemetryAPI(this); + /** + * Registry for views which should appear in the toolbar area while + * editing. These views will be chosen based on the selection state. + * + * @type {module:openmct.ToolbarRegistry} + * @memberof module:openmct.MCT# + * @name toolbars + */ + this.toolbars = new ToolbarRegistry(); - /** - * An interface for creating new indicators and changing them dynamically. - * - * @type {module:openmct.IndicatorAPI} - * @memberof module:openmct.MCT# - * @name indicators - */ - indicators = new IndicatorAPI(this); + /** + * Registry for domain object types which may exist within this + * instance of Open MCT. + * + * @type {module:openmct.TypeRegistry} + * @memberof module:openmct.MCT# + * @name types + */ + this.types = new TypeRegistry(); - /** - * MCT's user awareness management, to enable user and - * role specific functionality. - * @type {module:openmct.UserAPI} - * @memberof module:openmct.MCT# - * @name user - */ - user = new UserAPI(this); + /** + * An interface for interacting with domain objects and the domain + * object hierarchy. + * + * @type {module:openmct.ObjectAPI} + * @memberof module:openmct.MCT# + * @name objects + */ + this.objects = new ObjectAPI(this.types, this); - notifications = new NotificationAPI(); - editor = new EditorAPI(this); - overlays = new OverlayAPI(); - tooltips = new ToolTipAPI(); - menus = new MenuAPI(this); - actions = new ActionsAPI(this); - status = new StatusAPI(this); - priority = PriorityAPI; - router = new ApplicationRouter(this); - faults = new FaultManagementAPI(this); - forms = new FormsAPI(this); - branding = BrandingAPI; + /** + * An interface for retrieving and interpreting telemetry data associated + * with a domain object. + * + * @type {module:openmct.TelemetryAPI} + * @memberof module:openmct.MCT# + * @name telemetry + */ + this.telemetry = new TelemetryAPI(this); - constructor() { - super(); - EventEmitter.call(this); + /** + * 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); - this.buildInfo = { - version: __OPENMCT_VERSION__, - buildDate: __OPENMCT_BUILD_DATE__, - revision: __OPENMCT_REVISION__, - branch: __OPENMCT_BUILD_BRANCH__ - }; + /** + * 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); - this.destroy = this.destroy.bind(this); - this.defaultClock = 'local'; + this.notifications = new NotificationAPI(); + this.editor = new EditorAPI(this); + this.overlays = new OverlayAPI(); + this.tooltips = new ToolTipAPI(); + this.menus = new MenuAPI(this); + this.actions = new ActionsAPI(this); + this.status = new StatusAPI(this); + this.priority = PriorityAPI; + this.router = new ApplicationRouter(this); + this.faults = new FaultManagementAPI(this); + this.forms = new FormsAPI(this); + this.branding = BrandingAPI; /** * MCT's annotation API that enables From 9dfbdaf6a578886adef9e618e82a61fea076ebda Mon Sep 17 00:00:00 2001 From: Tristan F <26509014+LeoDog896@users.noreply.github.com> Date: Wed, 27 Dec 2023 13:49:37 -0500 Subject: [PATCH 20/20] refactor: move vue dist import to webpack alias --- .webpack/webpack.common.js | 3 ++- src/MCT.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.webpack/webpack.common.js b/.webpack/webpack.common.js index 512dbb25155..c7192823bb5 100644 --- a/.webpack/webpack.common.js +++ b/.webpack/webpack.common.js @@ -76,7 +76,8 @@ const config = { MCT: path.join(projectRootDir, 'src/MCT'), testUtils: path.join(projectRootDir, 'src/utils/testUtils.js'), objectUtils: path.join(projectRootDir, 'src/api/objects/object-utils.js'), - utils: path.join(projectRootDir, 'src/utils') + utils: path.join(projectRootDir, 'src/utils'), + vue: 'vue/dist/vue.esm-bundler' } }, plugins: [ diff --git a/src/MCT.js b/src/MCT.js index b83cb215cdd..2b3d683d483 100644 --- a/src/MCT.js +++ b/src/MCT.js @@ -21,7 +21,7 @@ *****************************************************************************/ /* eslint-disable no-undef */ import EventEmitter from 'EventEmitter'; -import { createApp, markRaw } from 'vue/dist/vue.esm-bundler'; +import { createApp, markRaw } from 'vue'; import ActionsAPI from './api/actions/ActionsAPI'; import AnnotationAPI from './api/annotation/AnnotationAPI';