Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nasa/openmct.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Tsay <david.e.tsay@nasa.gov>2022-07-14 20:53:06 +0300
committerDavid Tsay <david.e.tsay@nasa.gov>2022-07-14 20:53:06 +0300
commit76c5f61f8c0b5250347105aa4e0d6a634cba6c15 (patch)
treee996d7caff80459336e4c0fff5305a3ced10306e
parent8a0b6091ceed8657d4873bcb12c0deb92179e46f (diff)
parent2bfe632e7e1779a5e8c879ee9bb204ee266f33ea (diff)
Merge branch 'release/2.0.5' into vista-r4.10-releasevista-r4.10.0-rc1
-rw-r--r--.circleci/config.yml120
-rw-r--r--.eslintrc.js15
-rw-r--r--.github/ISSUE_TEMPLATE/bug_report.md21
-rw-r--r--.github/PULL_REQUEST_TEMPLATE.md18
-rw-r--r--.github/dependabot.yml13
-rw-r--r--.github/workflows/codeql-analysis.yml8
-rw-r--r--.github/workflows/e2e-pr.yml39
-rw-r--r--.github/workflows/e2e-visual.yml8
-rw-r--r--.github/workflows/e2e.yml6
-rw-r--r--.github/workflows/lighthouse.yml92
-rw-r--r--.github/workflows/npm-prerelease.yml12
-rw-r--r--.github/workflows/pr-platform.yml34
-rw-r--r--.github/workflows/prcop-config.json19
-rw-r--r--.github/workflows/prcop.yml26
-rw-r--r--.gitignore22
-rw-r--r--.npmignore71
-rw-r--r--.npmrc6
-rw-r--r--API.md46
-rw-r--r--LICENSE.md2
-rw-r--r--README.md31
-rw-r--r--app.js37
-rw-r--r--babel.coverage.js9
-rwxr-xr-xbuild-docs.sh2
-rw-r--r--codecov.yml19
-rw-r--r--copyright-notice.html2
-rw-r--r--copyright-notice.js2
-rw-r--r--docs/gendocs.js2
-rw-r--r--docs/src/architecture/framework.md232
-rw-r--r--docs/src/architecture/index.md76
-rw-r--r--docs/src/architecture/platform.md726
-rw-r--r--docs/src/design/index.md3
-rw-r--r--docs/src/design/planning/APIRefactor.md338
-rw-r--r--docs/src/design/proposals/APIRedesign.md1282
-rw-r--r--docs/src/design/proposals/APIRedesign_PeteRichards.md251
-rw-r--r--docs/src/design/proposals/ImperativePlugins.md164
-rw-r--r--docs/src/design/proposals/Roles.md138
-rw-r--r--docs/src/guide/index.md2456
-rw-r--r--docs/src/index.md11
-rw-r--r--docs/src/process/index.md8
-rw-r--r--docs/src/process/testing/procedures.md169
-rw-r--r--e2e/.eslintrc.js10
-rw-r--r--e2e/.percy.yml5
-rw-r--r--e2e/fixtures.js69
-rw-r--r--e2e/playwright-ci.config.js68
-rw-r--r--e2e/playwright-local.config.js87
-rw-r--r--e2e/playwright-performance.config.js43
-rw-r--r--e2e/playwright-visual.config.js17
-rw-r--r--e2e/test-data/PerformanceDisplayLayout.json1
-rw-r--r--e2e/test-data/PerformanceNotebook.json1
-rw-r--r--e2e/test-data/VisualTestData_storage.json22
-rw-r--r--e2e/test-data/recycled_local_storage.json22
-rw-r--r--e2e/tests/api/forms/forms.e2e.spec.js77
-rw-r--r--e2e/tests/branding.e2e.spec.js64
-rw-r--r--e2e/tests/example/eventGenerator.e2e.spec.js63
-rw-r--r--e2e/tests/example/generator/SinewaveLimitProvider.e2e.spec.js119
-rw-r--r--e2e/tests/framework.e2e.spec.js55
-rw-r--r--e2e/tests/moveObjects.e2e.spec.js137
-rw-r--r--e2e/tests/performance/imagery.perf.spec.js177
-rw-r--r--e2e/tests/performance/memleak-imagery.perf.spec.js119
-rw-r--r--e2e/tests/performance/notebook.perf.spec.js158
-rw-r--r--e2e/tests/persistence/addNoneditableObject.js27
-rw-r--r--e2e/tests/persistence/persistability.e2e.spec.js80
-rw-r--r--e2e/tests/plugins/ExportAsJSON/exportAsJson.e2e.spec.js51
-rw-r--r--e2e/tests/plugins/ImportAsJSON/importAsJson.e2e.spec.js49
-rw-r--r--e2e/tests/plugins/clock/Clock.e2e.spec.js67
-rw-r--r--e2e/tests/plugins/condition/condition.e2e.spec.js147
-rw-r--r--e2e/tests/plugins/imagery/exampleImagery.e2e.spec.js753
-rw-r--r--e2e/tests/plugins/notebook/addInitRestrictedNotebook.js (renamed from src/adapter/capabilities/AdapterCapability.js)20
-rw-r--r--e2e/tests/plugins/notebook/notebook.e2e.spec.js198
-rw-r--r--e2e/tests/plugins/notebook/restrictedNotebook.e2e.spec.js255
-rw-r--r--e2e/tests/plugins/notebook/tags.e2e.spec.js205
-rw-r--r--e2e/tests/plugins/plot/autoscale.e2e.spec.js185
-rw-r--r--e2e/tests/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-panned-chrome-darwinbin0 -> 18929 bytes
-rw-r--r--e2e/tests/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-panned-chrome-linuxbin0 -> 18524 bytes
-rw-r--r--e2e/tests/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-prepan-chrome-darwinbin0 -> 21763 bytes
-rw-r--r--e2e/tests/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-prepan-chrome-linuxbin0 -> 21375 bytes
-rw-r--r--e2e/tests/plugins/plot/logPlot.e2e.spec.js298
-rw-r--r--e2e/tests/plugins/plot/missingPlotObj.e2e.spec.js155
-rw-r--r--e2e/tests/plugins/remoteClock/remoteClock.e2e.spec.js (renamed from platform/commonUI/general/test/filters/ReverseFilterSpec.js)38
-rw-r--r--e2e/tests/plugins/telemetryTable/telemetryTable.e2e.spec.js104
-rw-r--r--e2e/tests/plugins/timeConductor/timeConductor.e2e.spec.js235
-rw-r--r--e2e/tests/plugins/timer/timer.e2e.spec.js185
-rw-r--r--e2e/tests/smoke.e2e.spec.js (renamed from e2e/tests/smoke.spec.js)18
-rw-r--r--e2e/tests/ui/layout/search/grandsearch.e2e.spec.js111
-rw-r--r--e2e/tests/visual/addInit.visual.spec.js76
-rw-r--r--e2e/tests/visual/controlledClock.visual.spec.js70
-rw-r--r--e2e/tests/visual/default.spec.js113
-rw-r--r--e2e/tests/visual/default.visual.spec.js232
-rw-r--r--e2e/tests/visual/generateVisualTestData.e2e.spec.js86
-rw-r--r--e2e/tests/visual/search.visual.spec.js104
-rw-r--r--example/eventGenerator/EventMetadataProvider.js (renamed from example/profiling/bundle.js)65
-rw-r--r--example/eventGenerator/EventTelemetryProvider.js96
-rw-r--r--example/eventGenerator/bundle.js80
-rw-r--r--example/eventGenerator/plugin.js (renamed from example/identity/bundle.js)44
-rw-r--r--example/eventGenerator/pluginSpec.js76
-rw-r--r--example/eventGenerator/src/EventTelemetry.js62
-rw-r--r--example/eventGenerator/src/EventTelemetryProvider.js118
-rw-r--r--example/eventGenerator/transcript.json (renamed from example/eventGenerator/data/transcript.json)0
-rw-r--r--example/exampleTags/plugin.js (renamed from platform/persistence/queue/test/PersistenceFailureConstantsSpec.js)25
-rw-r--r--example/exampleTags/tags.json19
-rw-r--r--example/exampleUser/ExampleUserProvider.js207
-rw-r--r--example/exampleUser/exampleUserCreator.js (renamed from example/mobile/res/sass/mobile-example.scss)23
-rw-r--r--example/exampleUser/plugin.js (renamed from platform/commonUI/general/src/SplashScreenManager.js)34
-rw-r--r--example/exampleUser/pluginSpec.js (renamed from platform/identity/test/IdentityProviderSpec.js)45
-rw-r--r--example/export/ExportTelemetryAsCSVAction.js89
-rw-r--r--example/faultManagment/exampleFaultSource.js83
-rw-r--r--example/faultManagment/pluginSpec.js (renamed from src/adapter/policies/AdaptedViewPolicy.js)37
-rw-r--r--example/forms/bundle.js53
-rw-r--r--example/forms/res/templates/exampleForm.html42
-rw-r--r--example/forms/src/ExampleFormController.js205
-rw-r--r--example/generator/GeneratorMetadataProvider.js18
-rw-r--r--example/generator/GeneratorProvider.js12
-rw-r--r--example/generator/SinewaveLimitProvider.js2
-rw-r--r--example/generator/StateGeneratorProvider.js2
-rw-r--r--example/generator/WorkerInterface.js17
-rw-r--r--example/generator/generatorWorker.js41
-rw-r--r--example/generator/plugin.js22
-rw-r--r--example/identity/src/ExampleIdentityService.js94
-rw-r--r--example/imagery/plugin.js30
-rw-r--r--example/msl/README.md20
-rw-r--r--example/msl/bundle.js115
-rw-r--r--example/msl/data/rems.json1
-rw-r--r--example/msl/src/MSLDataDictionary.js78
-rw-r--r--example/msl/src/RemsTelemetryModelProvider.js96
-rw-r--r--example/msl/src/RemsTelemetryProvider.js83
-rw-r--r--example/msl/src/RemsTelemetrySeries.js84
-rw-r--r--example/msl/src/RemsTelemetryServerAdapter.js145
-rw-r--r--example/notifications/bundle.js90
-rw-r--r--example/notifications/res/dialog-launch.html9
-rw-r--r--example/notifications/res/notification-launch.html9
-rw-r--r--example/notifications/src/DialogLaunchController.js157
-rw-r--r--example/notifications/src/NotificationLaunchController.js126
-rw-r--r--example/notifications/src/NotificationLaunchIndicator.js55
-rw-r--r--example/persistence/bundle.js54
-rw-r--r--example/persistence/src/BrowserPersistenceProvider.js102
-rw-r--r--example/policy/bundle.js45
-rw-r--r--example/policy/src/ExamplePolicy.js47
-rw-r--r--example/profiling/src/DigestIndicator.js82
-rw-r--r--example/profiling/src/WatchIndicator.js86
-rw-r--r--example/scratchpad/README.md2
-rw-r--r--example/scratchpad/bundle.js63
-rw-r--r--example/scratchpad/src/ScratchPersistenceProvider.js79
-rw-r--r--example/styleguide/bundle.js188
-rw-r--r--example/styleguide/res/images/diagram-containment.svg1
-rw-r--r--example/styleguide/res/images/diagram-objects.svg1
-rw-r--r--example/styleguide/res/images/diagram-views.svg1
-rw-r--r--example/styleguide/res/templates/colors.html84
-rw-r--r--example/styleguide/res/templates/controls.html172
-rw-r--r--example/styleguide/res/templates/glyphs.html216
-rw-r--r--example/styleguide/res/templates/input.html75
-rw-r--r--example/styleguide/res/templates/intro.html73
-rw-r--r--example/styleguide/res/templates/mct-example.html8
-rw-r--r--example/styleguide/res/templates/menus.html168
-rw-r--r--example/styleguide/res/templates/standards.html48
-rw-r--r--example/styleguide/res/templates/status.html227
-rw-r--r--example/styleguide/src/ExampleStyleGuideModelProvider.js82
-rw-r--r--example/styleguide/src/MCTExample.js30
-rw-r--r--index.html17
-rw-r--r--indexTest.js3
-rw-r--r--karma.conf.js53
-rw-r--r--openmct.js2
-rw-r--r--package.json183
-rw-r--r--platform/README.md2
-rw-r--r--platform/commonUI/README.md17
-rw-r--r--platform/commonUI/browse/bundle.js158
-rw-r--r--platform/commonUI/browse/res/templates/back-arrow.html27
-rw-r--r--platform/commonUI/browse/res/templates/browse-object.html69
-rw-r--r--platform/commonUI/browse/res/templates/browse.html90
-rw-r--r--platform/commonUI/browse/res/templates/browse/inspector-region.html39
-rw-r--r--platform/commonUI/browse/res/templates/browse/object-header-frame.html31
-rw-r--r--platform/commonUI/browse/res/templates/browse/object-header.html35
-rw-r--r--platform/commonUI/browse/res/templates/browse/object-properties.html64
-rw-r--r--platform/commonUI/browse/res/templates/menu-arrow.html26
-rw-r--r--platform/commonUI/browse/src/InspectorRegion.js67
-rw-r--r--platform/commonUI/browse/src/navigation/NavigateAction.js69
-rw-r--r--platform/commonUI/browse/src/navigation/NavigationService.js203
-rw-r--r--platform/commonUI/browse/src/navigation/OrphanNavigationHandler.js76
-rw-r--r--platform/commonUI/browse/test/InspectorRegionSpec.js43
-rw-r--r--platform/commonUI/browse/test/navigation/NavigateActionSpec.js85
-rw-r--r--platform/commonUI/browse/test/navigation/NavigationServiceSpec.js88
-rw-r--r--platform/commonUI/browse/test/navigation/OrphanNavigationHandlerSpec.js182
-rw-r--r--platform/commonUI/dialog/README.md27
-rw-r--r--platform/commonUI/dialog/bundle.js112
-rw-r--r--platform/commonUI/dialog/res/templates/dialog.html43
-rw-r--r--platform/commonUI/dialog/res/templates/message.html32
-rw-r--r--platform/commonUI/dialog/res/templates/notification-message.html25
-rw-r--r--platform/commonUI/dialog/res/templates/overlay-blocking-message.html25
-rw-r--r--platform/commonUI/dialog/res/templates/overlay-dialog.html25
-rw-r--r--platform/commonUI/dialog/res/templates/overlay-message-list.html29
-rw-r--r--platform/commonUI/dialog/res/templates/overlay-options.html43
-rw-r--r--platform/commonUI/dialog/res/templates/overlay.html30
-rw-r--r--platform/commonUI/dialog/src/DialogService.js271
-rw-r--r--platform/commonUI/dialog/src/OverlayService.js113
-rw-r--r--platform/commonUI/dialog/test/DialogServiceSpec.js214
-rw-r--r--platform/commonUI/dialog/test/OverlayServiceSpec.js104
-rw-r--r--platform/commonUI/edit/README.md45
-rw-r--r--platform/commonUI/edit/bundle.js209
-rw-r--r--platform/commonUI/edit/res/templates/edit-action-buttons.html53
-rw-r--r--platform/commonUI/edit/res/templates/edit-object.html78
-rw-r--r--platform/commonUI/edit/res/templates/library.html25
-rw-r--r--platform/commonUI/edit/res/templates/topbar-edit.html39
-rw-r--r--platform/commonUI/edit/src/actions/CancelAction.js90
-rw-r--r--platform/commonUI/edit/src/actions/EditAction.js101
-rw-r--r--platform/commonUI/edit/src/actions/EditAndComposeAction.js59
-rw-r--r--platform/commonUI/edit/src/actions/SaveAction.js98
-rw-r--r--platform/commonUI/edit/src/actions/SaveAndStopEditingAction.js75
-rw-r--r--platform/commonUI/edit/src/actions/SaveInProgressDialog.js24
-rw-r--r--platform/commonUI/edit/src/capabilities/EditorCapability.js64
-rw-r--r--platform/commonUI/edit/src/controllers/EditActionController.js79
-rw-r--r--platform/commonUI/edit/src/controllers/EditObjectController.js86
-rw-r--r--platform/commonUI/edit/src/controllers/EditPanesController.js66
-rw-r--r--platform/commonUI/edit/src/policies/EditPersistableObjectsPolicy.js57
-rw-r--r--platform/commonUI/edit/src/representers/EditRepresenter.js99
-rw-r--r--platform/commonUI/edit/test/actions/CancelActionSpec.js155
-rw-r--r--platform/commonUI/edit/test/actions/EditActionSpec.js108
-rw-r--r--platform/commonUI/edit/test/actions/EditAndComposeActionSpec.js119
-rw-r--r--platform/commonUI/edit/test/actions/SaveActionSpec.js159
-rw-r--r--platform/commonUI/edit/test/actions/SaveAndStopEditingActionSpec.js128
-rw-r--r--platform/commonUI/edit/test/controllers/EditActionControllerSpec.js106
-rw-r--r--platform/commonUI/edit/test/controllers/EditObjectControllerSpec.js138
-rw-r--r--platform/commonUI/edit/test/controllers/EditPanesControllerSpec.js114
-rw-r--r--platform/commonUI/edit/test/policies/EditPersistableObjectsSpec.js102
-rw-r--r--platform/commonUI/edit/test/representers/EditRepresenterSpec.js87
-rw-r--r--platform/commonUI/general/README.md8
-rw-r--r--platform/commonUI/general/bundle.js529
-rw-r--r--platform/commonUI/general/res/templates/angular-indicator.html26
-rw-r--r--platform/commonUI/general/res/templates/bottombar.html28
-rw-r--r--platform/commonUI/general/res/templates/containers/accordion.html34
-rw-r--r--platform/commonUI/general/res/templates/controls/action-group.html35
-rw-r--r--platform/commonUI/general/res/templates/controls/breadcrumb.html15
-rw-r--r--platform/commonUI/general/res/templates/controls/datetime-field.html48
-rw-r--r--platform/commonUI/general/res/templates/controls/datetime-picker.html66
-rw-r--r--platform/commonUI/general/res/templates/controls/input-filter.html32
-rw-r--r--platform/commonUI/general/res/templates/controls/selector.html65
-rw-r--r--platform/commonUI/general/res/templates/controls/switcher.html42
-rw-r--r--platform/commonUI/general/res/templates/controls/time-controller.html100
-rw-r--r--platform/commonUI/general/res/templates/label.html31
-rw-r--r--platform/commonUI/general/res/templates/message-banner.html20
-rw-r--r--platform/commonUI/general/res/templates/object-inspector.html49
-rw-r--r--platform/commonUI/general/res/templates/progress-bar.html10
-rw-r--r--platform/commonUI/general/res/templates/subtree.html27
-rw-r--r--platform/commonUI/general/res/templates/tree-node.html50
-rw-r--r--platform/commonUI/general/res/templates/tree.html30
-rw-r--r--platform/commonUI/general/res/templates/tree/node.html2
-rw-r--r--platform/commonUI/general/res/templates/tree/toggle.html1
-rw-r--r--platform/commonUI/general/res/templates/tree/tree-label.html4
-rw-r--r--platform/commonUI/general/res/templates/tree/wait-node.html24
-rw-r--r--platform/commonUI/general/src/StyleSheetLoader.js82
-rw-r--r--platform/commonUI/general/src/controllers/ActionGroupController.js104
-rw-r--r--platform/commonUI/general/src/controllers/BannerController.js77
-rw-r--r--platform/commonUI/general/src/controllers/ClickAwayController.js97
-rw-r--r--platform/commonUI/general/src/controllers/DateTimeFieldController.js112
-rw-r--r--platform/commonUI/general/src/controllers/DateTimePickerController.js207
-rw-r--r--platform/commonUI/general/src/controllers/GetterSetterController.js89
-rw-r--r--platform/commonUI/general/src/controllers/ObjectInspectorController.js119
-rw-r--r--platform/commonUI/general/src/controllers/SelectorController.js164
-rw-r--r--platform/commonUI/general/src/controllers/TimeRangeController.js313
-rw-r--r--platform/commonUI/general/src/controllers/ToggleController.js66
-rw-r--r--platform/commonUI/general/src/controllers/TreeNodeController.js204
-rw-r--r--platform/commonUI/general/src/controllers/ViewSwitcherController.js73
-rw-r--r--platform/commonUI/general/src/directives/MCTClickElsewhere.js77
-rw-r--r--platform/commonUI/general/src/directives/MCTContainer.js91
-rw-r--r--platform/commonUI/general/src/directives/MCTDrag.js177
-rw-r--r--platform/commonUI/general/src/directives/MCTIndicators.js40
-rw-r--r--platform/commonUI/general/src/directives/MCTPopup.js72
-rw-r--r--platform/commonUI/general/src/directives/MCTResize.js123
-rw-r--r--platform/commonUI/general/src/directives/MCTScroll.js82
-rw-r--r--platform/commonUI/general/src/directives/MCTSelectable.js83
-rw-r--r--platform/commonUI/general/src/directives/MCTSplitPane.js263
-rw-r--r--platform/commonUI/general/src/directives/MCTSplitter.js90
-rw-r--r--platform/commonUI/general/src/directives/MCTTree.js86
-rw-r--r--platform/commonUI/general/src/filters/ReverseFilter.js42
-rw-r--r--platform/commonUI/general/src/services/Overlay.js186
-rw-r--r--platform/commonUI/general/src/services/Popup.js87
-rw-r--r--platform/commonUI/general/src/services/PopupService.js124
-rw-r--r--platform/commonUI/general/src/services/UrlService.js94
-rw-r--r--platform/commonUI/general/src/ui/ToggleView.js65
-rw-r--r--platform/commonUI/general/src/ui/TreeLabelView.js97
-rw-r--r--platform/commonUI/general/src/ui/TreeNodeView.js158
-rw-r--r--platform/commonUI/general/src/ui/TreeView.js141
-rw-r--r--platform/commonUI/general/test/SplashScreenManagerSpec.js90
-rw-r--r--platform/commonUI/general/test/StyleSheetLoaderSpec.js120
-rw-r--r--platform/commonUI/general/test/controllers/ActionGroupControllerSpec.js114
-rw-r--r--platform/commonUI/general/test/controllers/ClickAwayControllerSpec.js92
-rw-r--r--platform/commonUI/general/test/controllers/DateTimeFieldControllerSpec.js221
-rw-r--r--platform/commonUI/general/test/controllers/DateTimePickerControllerSpec.js193
-rw-r--r--platform/commonUI/general/test/controllers/GetterSetterControllerSpec.js83
-rw-r--r--platform/commonUI/general/test/controllers/ObjectInspectorControllerSpec.js113
-rw-r--r--platform/commonUI/general/test/controllers/SelectorControllerSpec.js186
-rw-r--r--platform/commonUI/general/test/controllers/TimeRangeControllerSpec.js317
-rw-r--r--platform/commonUI/general/test/controllers/ToggleControllerSpec.js62
-rw-r--r--platform/commonUI/general/test/controllers/TreeNodeControllerSpec.js209
-rw-r--r--platform/commonUI/general/test/controllers/ViewSwitcherControllerSpec.js155
-rw-r--r--platform/commonUI/general/test/directives/MCTClickElsewhereSpec.js129
-rw-r--r--platform/commonUI/general/test/directives/MCTContainerSpec.js94
-rw-r--r--platform/commonUI/general/test/directives/MCTDragSpec.js303
-rw-r--r--platform/commonUI/general/test/directives/MCTPopupSpec.js132
-rw-r--r--platform/commonUI/general/test/directives/MCTResizeSpec.js166
-rw-r--r--platform/commonUI/general/test/directives/MCTScrollSpec.js114
-rw-r--r--platform/commonUI/general/test/directives/MCTSplitPaneSpec.js233
-rw-r--r--platform/commonUI/general/test/directives/MCTSplitterSpec.js110
-rw-r--r--platform/commonUI/general/test/directives/MCTTreeSpec.js146
-rw-r--r--platform/commonUI/general/test/services/PopupServiceSpec.js98
-rw-r--r--platform/commonUI/general/test/services/PopupSpec.js74
-rw-r--r--platform/commonUI/general/test/services/UrlServiceSpec.js98
-rw-r--r--platform/commonUI/general/test/suite.json29
-rw-r--r--platform/commonUI/general/test/ui/TreeViewSpec.js290
-rw-r--r--platform/commonUI/inspect/bundle.js117
-rw-r--r--platform/commonUI/inspect/res/bubble.html9
-rw-r--r--platform/commonUI/inspect/res/info-bubble.html9
-rw-r--r--platform/commonUI/inspect/res/info-table.html8
-rw-r--r--platform/commonUI/inspect/res/infobubble.html71
-rw-r--r--platform/commonUI/inspect/res/templates/info-button.html24
-rw-r--r--platform/commonUI/inspect/src/InfoConstants.js47
-rw-r--r--platform/commonUI/inspect/src/gestures/InfoButtonGesture.js115
-rw-r--r--platform/commonUI/inspect/src/gestures/InfoGesture.js149
-rw-r--r--platform/commonUI/inspect/src/services/InfoService.js106
-rw-r--r--platform/commonUI/inspect/test/gestures/InfoButtonGestureSpec.js148
-rw-r--r--platform/commonUI/inspect/test/gestures/InfoGestureSpec.js188
-rw-r--r--platform/commonUI/inspect/test/services/InfoServiceSpec.js142
-rw-r--r--platform/commonUI/mobile/bundle.js44
-rw-r--r--platform/commonUI/mobile/src/AgentServiceSpec.js96
-rw-r--r--platform/commonUI/notification/bundle.js47
-rw-r--r--platform/commonUI/notification/src/NotificationService.js64
-rw-r--r--platform/commonUI/regions/bundle.js55
-rw-r--r--platform/commonUI/regions/src/EditableRegionPolicy.js56
-rw-r--r--platform/commonUI/regions/src/InspectorController.js93
-rw-r--r--platform/commonUI/regions/src/Region.js99
-rw-r--r--platform/commonUI/regions/test/EditableRegionPolicySpec.js74
-rw-r--r--platform/commonUI/regions/test/InspectorControllerSpec.js120
-rw-r--r--platform/commonUI/regions/test/RegionSpec.js103
-rw-r--r--platform/containment/README.md2
-rw-r--r--platform/containment/bundle.js76
-rw-r--r--platform/containment/src/ComposeActionPolicy.js78
-rw-r--r--platform/containment/src/CompositionModelPolicy.js26
-rw-r--r--platform/containment/src/CompositionMutabilityPolicy.js45
-rw-r--r--platform/containment/src/CompositionPolicy.js71
-rw-r--r--platform/containment/src/PersistableCompositionPolicy.js61
-rw-r--r--platform/containment/test/ComposeActionPolicySpec.js95
-rw-r--r--platform/containment/test/CompositionModelPolicySpec.js30
-rw-r--r--platform/containment/test/CompositionMutabilityPolicySpec.js51
-rw-r--r--platform/containment/test/CompositionPolicySpec.js131
-rw-r--r--platform/containment/test/PersistableCompositionPolicySpec.js85
-rw-r--r--platform/core/README.md3
-rw-r--r--platform/core/bundle.js357
-rw-r--r--platform/core/src/actions/ActionAggregator.js124
-rw-r--r--platform/core/src/actions/ActionCapability.js119
-rw-r--r--platform/core/src/actions/ActionProvider.js157
-rw-r--r--platform/core/src/actions/LoggingActionDecorator.js80
-rw-r--r--platform/core/src/capabilities/CompositionCapability.js162
-rw-r--r--platform/core/src/capabilities/ContextCapability.js111
-rw-r--r--platform/core/src/capabilities/ContextualDomainObject.js66
-rw-r--r--platform/core/src/capabilities/CoreCapabilityProvider.js110
-rw-r--r--platform/core/src/capabilities/DelegationCapability.js129
-rw-r--r--platform/core/src/capabilities/InstantiationCapability.js85
-rw-r--r--platform/core/src/capabilities/MetadataCapability.js92
-rw-r--r--platform/core/src/capabilities/MutationCapability.js189
-rw-r--r--platform/core/src/capabilities/PersistenceCapability.js206
-rw-r--r--platform/core/src/capabilities/RelationshipCapability.js128
-rw-r--r--platform/core/src/identifiers/Identifier.js84
-rw-r--r--platform/core/src/identifiers/IdentifierProvider.js65
-rw-r--r--platform/core/src/models/CachingModelDecorator.js65
-rw-r--r--platform/core/src/models/ModelAggregator.js99
-rw-r--r--platform/core/src/models/ModelCacheService.js85
-rw-r--r--platform/core/src/models/PersistedModelProvider.js136
-rw-r--r--platform/core/src/models/StaticModelProvider.js71
-rw-r--r--platform/core/src/objects/DomainObjectImpl.js142
-rw-r--r--platform/core/src/objects/DomainObjectProvider.js93
-rw-r--r--platform/core/src/services/Instantiate.js61
-rw-r--r--platform/core/src/services/Now.js48
-rw-r--r--platform/core/src/services/Throttle.js93
-rw-r--r--platform/core/src/services/Topic.js97
-rw-r--r--platform/core/src/types/MergeModels.js106
-rw-r--r--platform/core/src/types/TypeCapability.js55
-rw-r--r--platform/core/src/types/TypeImpl.js194
-rw-r--r--platform/core/src/types/TypeProperty.js163
-rw-r--r--platform/core/src/types/TypePropertyConversion.js99
-rw-r--r--platform/core/src/types/TypeProvider.js197
-rw-r--r--platform/core/src/views/ViewCapability.js58
-rw-r--r--platform/core/src/views/ViewProvider.js161
-rw-r--r--platform/core/test/actions/ActionAggregatorSpec.js73
-rw-r--r--platform/core/test/actions/ActionCapabilitySpec.js96
-rw-r--r--platform/core/test/actions/ActionProviderSpec.js204
-rw-r--r--platform/core/test/actions/LoggingActionDecoratorSpec.js71
-rw-r--r--platform/core/test/capabilities/CompositionCapabilitySpec.js214
-rw-r--r--platform/core/test/capabilities/ContextCapabilitySpec.js79
-rw-r--r--platform/core/test/capabilities/ContextualDomainObjectSpec.js78
-rw-r--r--platform/core/test/capabilities/CoreCapabilityProviderSpec.js112
-rw-r--r--platform/core/test/capabilities/DelegationCapabilitySpec.js110
-rw-r--r--platform/core/test/capabilities/InstantiationCapabilitySpec.js94
-rw-r--r--platform/core/test/capabilities/MetadataCapabilitySpec.js99
-rw-r--r--platform/core/test/capabilities/MutationCapabilitySpec.js139
-rw-r--r--platform/core/test/capabilities/PersistenceCapabilitySpec.js196
-rw-r--r--platform/core/test/capabilities/RelationshipCapabilitySpec.js144
-rw-r--r--platform/core/test/identifiers/IdentifierProviderSpec.js56
-rw-r--r--platform/core/test/identifiers/IdentifierSpec.js80
-rw-r--r--platform/core/test/models/CachingModelDecoratorSpec.js157
-rw-r--r--platform/core/test/models/ModelAggregatorSpec.js84
-rw-r--r--platform/core/test/models/ModelCacheServiceSpec.js68
-rw-r--r--platform/core/test/models/PersistedModelProviderSpec.js173
-rw-r--r--platform/core/test/models/StaticModelProviderSpec.js109
-rw-r--r--platform/core/test/objects/DomainObjectProviderSpec.js86
-rw-r--r--platform/core/test/objects/DomainObjectSpec.js87
-rw-r--r--platform/core/test/services/InstantiateSpec.js108
-rw-r--r--platform/core/test/services/ThrottleSpec.js71
-rw-r--r--platform/core/test/services/TopicSpec.js88
-rw-r--r--platform/core/test/types/MergeModelsSpec.js63
-rw-r--r--platform/core/test/types/TypeCapabilitySpec.js66
-rw-r--r--platform/core/test/types/TypeImplSpec.js117
-rw-r--r--platform/core/test/types/TypePropertyConversionSpec.js65
-rw-r--r--platform/core/test/types/TypePropertySpec.js129
-rw-r--r--platform/core/test/types/TypeProviderSpec.js147
-rw-r--r--platform/core/test/views/ViewCapabilitySpec.js58
-rw-r--r--platform/core/test/views/ViewProviderSpec.js167
-rw-r--r--platform/data/README.md2
-rw-r--r--platform/entanglement/README.md24
-rw-r--r--platform/entanglement/bundle.js124
-rw-r--r--platform/entanglement/src/actions/AbstractComposeAction.js163
-rw-r--r--platform/entanglement/src/actions/SetPrimaryLocationAction.js66
-rw-r--r--platform/entanglement/src/capabilities/LocationCapability.js128
-rw-r--r--platform/entanglement/src/policies/CrossSpacePolicy.js69
-rw-r--r--platform/entanglement/src/services/CopyService.js109
-rw-r--r--platform/entanglement/src/services/CopyTask.js276
-rw-r--r--platform/entanglement/src/services/LocatingCreationDecorator.js47
-rw-r--r--platform/entanglement/src/services/LocatingObjectDecorator.js95
-rw-r--r--platform/entanglement/src/services/LocationService.js90
-rw-r--r--platform/entanglement/test/ControlledPromise.js97
-rw-r--r--platform/entanglement/test/DomainObjectFactory.js160
-rw-r--r--platform/entanglement/test/actions/AbstractComposeActionSpec.js227
-rw-r--r--platform/entanglement/test/actions/SetPrimaryLocationActionSpec.js88
-rw-r--r--platform/entanglement/test/capabilities/LocationCapabilitySpec.js163
-rw-r--r--platform/entanglement/test/policies/CopyPolicySpec.js92
-rw-r--r--platform/entanglement/test/policies/CrossSpacePolicySpec.js117
-rw-r--r--platform/entanglement/test/services/CopyServiceSpec.js479
-rw-r--r--platform/entanglement/test/services/CopyTaskSpec.js267
-rw-r--r--platform/entanglement/test/services/LocatingCreationDecoratorSpec.js76
-rw-r--r--platform/entanglement/test/services/LocatingObjectDecoratorSpec.js131
-rw-r--r--platform/entanglement/test/services/LocationServiceSpec.js151
-rw-r--r--platform/entanglement/test/services/MockCopyService.js96
-rw-r--r--platform/entanglement/test/services/MockLinkService.js86
-rw-r--r--platform/exporters/ExportService.js92
-rw-r--r--platform/exporters/ExportServiceSpec.js159
-rw-r--r--platform/exporters/bundle.js65
-rw-r--r--platform/features/README.md3
-rw-r--r--platform/features/pages/bundle.js77
-rw-r--r--platform/features/static-markup/bundle.js56
-rw-r--r--platform/features/static-markup/res/markup.html24
-rw-r--r--platform/framework/README.md192
-rw-r--r--platform/framework/bundle.js107
-rw-r--r--platform/framework/src/Constants.js48
-rw-r--r--platform/framework/src/FrameworkInitializer.js73
-rw-r--r--platform/framework/src/FrameworkLayer.js104
-rw-r--r--platform/framework/src/LogLevel.js100
-rw-r--r--platform/framework/src/Main.js60
-rw-r--r--platform/framework/src/bootstrap/ApplicationBootstrapper.js70
-rw-r--r--platform/framework/src/load/Bundle.js207
-rw-r--r--platform/framework/src/load/BundleLoader.js160
-rw-r--r--platform/framework/src/load/Extension.js190
-rw-r--r--platform/framework/src/register/CustomRegistrars.js248
-rw-r--r--platform/framework/src/register/ExtensionRegistrar.js232
-rw-r--r--platform/framework/src/register/ExtensionSorter.js118
-rw-r--r--platform/framework/src/register/PartialConstructor.js78
-rw-r--r--platform/framework/src/register/ServiceCompositor.js257
-rw-r--r--platform/framework/src/resolve/BundleResolver.js125
-rw-r--r--platform/framework/src/resolve/ExtensionResolver.js147
-rw-r--r--platform/framework/src/resolve/ImplementationLoader.js64
-rw-r--r--platform/framework/test/FrameworkInitializerSpec.js60
-rw-r--r--platform/framework/test/LogLevelSpec.js98
-rw-r--r--platform/framework/test/bootstrap/ApplicationBootstrapperSpec.js108
-rw-r--r--platform/framework/test/load/BundleLoaderSpec.js122
-rw-r--r--platform/framework/test/load/BundleSpec.js98
-rw-r--r--platform/framework/test/load/ExtensionSpec.js107
-rw-r--r--platform/framework/test/register/CustomRegistrarsSpec.js198
-rw-r--r--platform/framework/test/register/ExtensionRegistrarSpec.js119
-rw-r--r--platform/framework/test/register/ExtensionSorterSpec.js69
-rw-r--r--platform/framework/test/register/PartialConstructorSpec.js97
-rw-r--r--platform/framework/test/register/ServiceCompositorSpec.js258
-rw-r--r--platform/framework/test/resolve/BundleResolverSpec.js71
-rw-r--r--platform/framework/test/resolve/ExtensionResolverSpec.js114
-rw-r--r--platform/framework/test/resolve/ImplementationLoaderSpec.js88
-rw-r--r--platform/identity/README.md2
-rw-r--r--platform/identity/bundle.js87
-rw-r--r--platform/identity/src/IdentityAggregator.js91
-rw-r--r--platform/identity/src/IdentityProvider.js51
-rw-r--r--platform/identity/test/IdentityAggregatorSpec.js139
-rw-r--r--platform/identity/test/IdentityCreationDecoratorSpec.js95
-rw-r--r--platform/identity/test/IdentityIndicatorSpec.js70
-rw-r--r--platform/persistence/README.md1
-rw-r--r--platform/persistence/aggregator/bundle.js46
-rw-r--r--platform/persistence/aggregator/src/PersistenceAggregator.js90
-rw-r--r--platform/persistence/aggregator/test/PersistenceAggregatorSpec.js105
-rw-r--r--platform/persistence/elastic/README.md8
-rw-r--r--platform/persistence/elastic/bundle.js97
-rw-r--r--platform/persistence/elastic/src/ElasticIndicator.js104
-rw-r--r--platform/persistence/elastic/src/ElasticPersistenceProvider.js168
-rw-r--r--platform/persistence/elastic/src/ElasticSearchProvider.js142
-rw-r--r--platform/persistence/elastic/test/ElasticIndicatorSpec.js109
-rw-r--r--platform/persistence/elastic/test/ElasticPersistenceProviderSpec.js253
-rw-r--r--platform/persistence/elastic/test/ElasticSearchProviderSpec.js164
-rw-r--r--platform/persistence/queue/README.md5
-rw-r--r--platform/persistence/queue/bundle.js82
-rw-r--r--platform/persistence/queue/res/templates/persistence-failure-dialog.html52
-rw-r--r--platform/persistence/queue/src/PersistenceFailureController.js55
-rw-r--r--platform/persistence/queue/src/PersistenceFailureDialog.js78
-rw-r--r--platform/persistence/queue/src/PersistenceFailureHandler.js69
-rw-r--r--platform/persistence/queue/src/PersistenceQueue.js76
-rw-r--r--platform/persistence/queue/src/PersistenceQueueHandler.js115
-rw-r--r--platform/persistence/queue/src/PersistenceQueueImpl.js149
-rw-r--r--platform/persistence/queue/src/QueuingPersistenceCapability.js51
-rw-r--r--platform/persistence/queue/src/QueuingPersistenceCapabilityDecorator.js86
-rw-r--r--platform/persistence/queue/test/PersistenceFailureControllerSpec.js45
-rw-r--r--platform/persistence/queue/test/PersistenceFailureDialogSpec.js71
-rw-r--r--platform/persistence/queue/test/PersistenceFailureHandlerSpec.js82
-rw-r--r--platform/persistence/queue/test/PersistenceQueueHandlerSpec.js136
-rw-r--r--platform/persistence/queue/test/PersistenceQueueImplSpec.js153
-rw-r--r--platform/persistence/queue/test/PersistenceQueueSpec.js53
-rw-r--r--platform/persistence/queue/test/QueuingPersistenceCapabilityDecoratorSpec.js83
-rw-r--r--platform/persistence/queue/test/QueuingPersistenceCapabilitySpec.js64
-rw-r--r--platform/policy/README.md93
-rw-r--r--platform/policy/bundle.js69
-rw-r--r--platform/policy/src/PolicyActionDecorator.js55
-rw-r--r--platform/policy/src/PolicyProvider.js143
-rw-r--r--platform/policy/src/PolicyViewDecorator.js55
-rw-r--r--platform/policy/test/PolicyActionDecoratorSpec.js98
-rw-r--r--platform/policy/test/PolicyProviderSpec.js128
-rw-r--r--platform/policy/test/PolicyViewDecoratorSpec.js102
-rw-r--r--platform/representation/README.md120
-rw-r--r--platform/representation/bundle.js144
-rw-r--r--platform/representation/src/MCTInclude.js102
-rw-r--r--platform/representation/src/MCTRepresentation.js308
-rw-r--r--platform/representation/src/TemplateLinker.js177
-rw-r--r--platform/representation/src/TemplatePrefetcher.js48
-rw-r--r--platform/representation/src/gestures/DragGesture.js119
-rw-r--r--platform/representation/src/gestures/DropGesture.js129
-rw-r--r--platform/representation/src/gestures/GestureConstants.js53
-rw-r--r--platform/representation/src/gestures/GestureProvider.js133
-rw-r--r--platform/representation/src/gestures/GestureRepresenter.js68
-rw-r--r--platform/representation/src/services/DndService.js73
-rw-r--r--platform/representation/test/MCTIncludeSpec.js108
-rw-r--r--platform/representation/test/MCTRepresentationSpec.js343
-rw-r--r--platform/representation/test/TemplateLinkerSpec.js239
-rw-r--r--platform/representation/test/TemplatePrefetcherSpec.js73
-rw-r--r--platform/representation/test/gestures/DragGestureSpec.js136
-rw-r--r--platform/representation/test/gestures/DropGestureSpec.js186
-rw-r--r--platform/representation/test/gestures/GestureProviderSpec.js89
-rw-r--r--platform/representation/test/gestures/GestureRepresenterSpec.js80
-rw-r--r--platform/representation/test/services/DndServiceSpec.js65
-rw-r--r--platform/status/README.md2
-rw-r--r--platform/status/bundle.js63
-rw-r--r--platform/status/src/StatusCapability.js98
-rw-r--r--platform/status/src/StatusConstants.js25
-rw-r--r--platform/status/src/StatusRepresenter.js93
-rw-r--r--platform/status/src/StatusService.js91
-rw-r--r--platform/status/test/StatusCapabilitySpec.js87
-rw-r--r--platform/status/test/StatusRepresenterSpec.js116
-rw-r--r--platform/status/test/StatusServiceSpec.js92
-rw-r--r--platform/telemetry/README.md2
-rw-r--r--platform/telemetry/bundle.js130
-rw-r--r--platform/telemetry/src/TelemetryAggregator.js135
-rw-r--r--platform/telemetry/src/TelemetryCapability.js343
-rw-r--r--platform/telemetry/src/TelemetryController.js404
-rw-r--r--platform/telemetry/src/TelemetryDelegator.js71
-rw-r--r--platform/telemetry/src/TelemetryFormatter.js75
-rw-r--r--platform/telemetry/src/TelemetryHandle.js147
-rw-r--r--platform/telemetry/src/TelemetryHandler.js64
-rw-r--r--platform/telemetry/src/TelemetryQueue.js121
-rw-r--r--platform/telemetry/src/TelemetrySubscriber.js77
-rw-r--r--platform/telemetry/src/TelemetrySubscription.js388
-rw-r--r--platform/telemetry/src/TelemetryTable.js58
-rw-r--r--platform/telemetry/test/TelemetryAggregatorSpec.js125
-rw-r--r--platform/telemetry/test/TelemetryCapabilitySpec.js367
-rw-r--r--platform/telemetry/test/TelemetryControllerSpec.js245
-rw-r--r--platform/telemetry/test/TelemetryFormatterSpec.js67
-rw-r--r--platform/telemetry/test/TelemetryHandleSpec.js141
-rw-r--r--platform/telemetry/test/TelemetryHandlerSpec.js83
-rw-r--r--platform/telemetry/test/TelemetryQueueSpec.js79
-rw-r--r--platform/telemetry/test/TelemetrySubscriberSpec.js73
-rw-r--r--platform/telemetry/test/TelemetrySubscriptionSpec.js271
-rw-r--r--platform/telemetry/test/TelemetryTableSpec.js77
-rw-r--r--src/MCT.js307
-rw-r--r--src/MCTSpec.js2
-rw-r--r--src/adapter/actions/LegacyContextMenuAction.js94
-rw-r--r--src/adapter/bundle.js195
-rw-r--r--src/adapter/capabilities/APICapabilityDecorator.js67
-rw-r--r--src/adapter/capabilities/AlternateCompositionCapability.js107
-rw-r--r--src/adapter/capabilities/patchViewCapability.js64
-rw-r--r--src/adapter/capabilities/synchronizeMutationCapability.js50
-rw-r--r--src/adapter/directives/MCTView.js46
-rw-r--r--src/adapter/indicators/legacy-indicators-plugin.js69
-rw-r--r--src/adapter/indicators/legacy-indicators-pluginSpec.js114
-rw-r--r--src/adapter/policies/README.md7
-rw-r--r--src/adapter/runs/AlternateCompositionInitializer.js42
-rw-r--r--src/adapter/runs/LegacyTelemetryProvider.js193
-rw-r--r--src/adapter/runs/RegisterLegacyTypes.js11
-rw-r--r--src/adapter/services/Instantiate.js50
-rw-r--r--src/adapter/services/LegacyObjectAPIInterceptor.js196
-rw-r--r--src/adapter/services/LegacyPersistenceAdapter.js77
-rw-r--r--src/adapter/templates/adapted-view-template.html22
-rw-r--r--src/adapter/views/LegacyViewProvider.js141
-rw-r--r--src/adapter/views/TypeInspectorViewProvider.js98
-rw-r--r--src/adapter/views/installLegacyViews.js32
-rw-r--r--src/api/Branding.js2
-rw-r--r--src/api/Editor.js2
-rw-r--r--src/api/actions/ActionCollection.js5
-rw-r--r--src/api/actions/ActionCollectionSpec.js2
-rw-r--r--src/api/actions/ActionsAPI.js2
-rw-r--r--src/api/actions/ActionsAPISpec.js2
-rw-r--r--src/api/annotation/AnnotationAPI.js277
-rw-r--r--src/api/annotation/AnnotationAPISpec.js176
-rw-r--r--src/api/api.js19
-rw-r--r--src/api/composition/CompositionAPI.js2
-rw-r--r--src/api/composition/CompositionCollection.js2
-rw-r--r--src/api/composition/DefaultCompositionProvider.js2
-rw-r--r--src/api/faultmanagement/FaultManagementAPI.js106
-rw-r--r--src/api/faultmanagement/FaultManagementAPISpec.js144
-rw-r--r--src/api/forms/FormController.js11
-rw-r--r--src/api/forms/FormsAPI.js14
-rw-r--r--src/api/forms/FormsAPISpec.js157
-rw-r--r--src/api/forms/components/FormProperties.vue26
-rw-r--r--src/api/forms/components/FormRow.vue37
-rw-r--r--src/api/forms/components/controls/AutoCompleteField.vue170
-rw-r--r--src/api/forms/components/controls/CheckBoxField.vue13
-rw-r--r--src/api/forms/components/controls/ClockDisplayFormatField.vue11
-rw-r--r--src/api/forms/components/controls/Composite.vue15
-rw-r--r--src/api/forms/components/controls/CompositeItem.vue11
-rw-r--r--src/api/forms/components/controls/Datetime.vue73
-rw-r--r--src/api/forms/components/controls/FileInput.vue43
-rw-r--r--src/api/forms/components/controls/Locator.vue18
-rw-r--r--src/api/forms/components/controls/NumberField.vue28
-rw-r--r--src/api/forms/components/controls/SelectField.vue18
-rw-r--r--src/api/forms/components/controls/TextAreaField.vue23
-rw-r--r--src/api/forms/components/controls/TextField.vue23
-rw-r--r--src/api/forms/components/controls/ToggleSwitchField.vue62
-rw-r--r--src/api/forms/toggle-check-box-mixin.js19
-rw-r--r--src/api/indicators/IndicatorAPI.js36
-rw-r--r--src/api/indicators/IndicatorAPISpec.js2
-rw-r--r--src/api/indicators/SimpleIndicator.js137
-rw-r--r--src/api/menu/MenuAPI.js2
-rw-r--r--src/api/menu/MenuAPISpec.js180
-rw-r--r--src/api/menu/components/Menu.vue9
-rw-r--r--src/api/menu/components/SuperMenu.vue13
-rw-r--r--src/api/menu/menu.js2
-rw-r--r--src/api/notifications/NotificationAPI.js2
-rw-r--r--src/api/notifications/NotificationAPISpec.js2
-rw-r--r--src/api/objects/InMemorySearchProvider.js301
-rw-r--r--src/api/objects/InMemorySearchWorker.js176
-rw-r--r--src/api/objects/InterceptorRegistry.js2
-rw-r--r--src/api/objects/MutableDomainObject.js2
-rw-r--r--src/api/objects/ObjectAPI.js1052
-rw-r--r--src/api/objects/ObjectAPISearchSpec.js47
-rw-r--r--src/api/objects/RootObjectProvider.js2
-rw-r--r--src/api/objects/RootRegistry.js58
-rw-r--r--src/api/objects/Transaction.js25
-rw-r--r--src/api/objects/TransactionSpec.js16
-rw-r--r--src/api/objects/object-utils.js3
-rw-r--r--src/api/objects/test/RootObjectProviderSpec.js2
-rw-r--r--src/api/objects/test/RootRegistrySpec.js172
-rw-r--r--src/api/overlays/components/OverlayComponent.vue1
-rw-r--r--src/api/priority/PriorityAPI.js2
-rw-r--r--src/api/status/StatusAPI.js2
-rw-r--r--src/api/telemetry/DefaultMetadataProvider.js2
-rw-r--r--src/api/telemetry/TelemetryAPI.js376
-rw-r--r--src/api/telemetry/TelemetryAPISpec.js4
-rw-r--r--src/api/telemetry/TelemetryCollection.js76
-rw-r--r--src/api/telemetry/TelemetryCollectionSpec.js101
-rw-r--r--src/api/telemetry/TelemetryMetadataManager.js16
-rw-r--r--src/api/telemetry/TelemetryRequestInterceptor.js68
-rw-r--r--src/api/telemetry/TelemetryValueFormatter.js42
-rw-r--r--src/api/telemetry/constants.js (renamed from platform/entanglement/src/actions/CancelError.js)15
-rw-r--r--src/api/time/GlobalTimeContext.js2
-rw-r--r--src/api/time/IndependentTimeContext.js2
-rw-r--r--src/api/time/TimeAPI.js2
-rw-r--r--src/api/time/TimeAPISpec.js2
-rw-r--r--src/api/time/TimeContext.js6
-rw-r--r--src/api/time/independentTimeAPISpec.js2
-rw-r--r--src/api/types/Type.js2
-rw-r--r--src/api/types/TypeRegistry.js2
-rw-r--r--src/api/types/TypeRegistrySpec.js2
-rw-r--r--src/api/user/StatusAPI.js295
-rw-r--r--src/api/user/StatusUserProvider.js81
-rw-r--r--src/api/user/User.js (renamed from platform/commonUI/mobile/src/AgentService.js)22
-rw-r--r--src/api/user/UserAPI.js163
-rw-r--r--src/api/user/UserAPISpec.js114
-rw-r--r--src/api/user/UserProvider.js (renamed from src/plugins/nonEditableFolder/plugin.js)27
-rw-r--r--src/api/user/UserStatusAPISpec.js103
-rw-r--r--src/api/user/constants.js (renamed from src/plugins/legacySupport/legacyRegistry.js)7
-rw-r--r--src/end.frag2
-rw-r--r--src/exporters/CSVExporter.js4
-rw-r--r--src/exporters/ImageExporter.js8
-rw-r--r--src/exporters/ImageExporterSpec.js2
-rw-r--r--src/exporters/JSONExporter.js4
-rw-r--r--src/plugins/CouchDBSearchFolder/plugin.js3
-rw-r--r--src/plugins/CouchDBSearchFolder/pluginSpec.js5
-rw-r--r--src/plugins/DeviceClassifier/plugin.js2
-rw-r--r--src/plugins/DeviceClassifier/src/DeviceClassifier.js3
-rw-r--r--src/plugins/DeviceClassifier/src/DeviceClassifierSpec.js2
-rw-r--r--src/plugins/DeviceClassifier/src/DeviceMatchers.js2
-rw-r--r--src/plugins/DeviceClassifier/src/DeviceMatchersSpec.js2
-rw-r--r--src/plugins/ISOTimeFormat/ISOTimeFormat.js2
-rw-r--r--src/plugins/ISOTimeFormat/plugin.js2
-rw-r--r--src/plugins/ISOTimeFormat/pluginSpec.js2
-rw-r--r--src/plugins/LADTable/LADTableCompositionPolicy.js2
-rw-r--r--src/plugins/LADTable/LADTableSetViewProvider.js2
-rw-r--r--src/plugins/LADTable/LADTableViewProvider.js2
-rw-r--r--src/plugins/LADTable/components/LADRow.vue69
-rw-r--r--src/plugins/LADTable/components/LADTable.vue2
-rw-r--r--src/plugins/LADTable/components/LadTableSet.vue11
-rw-r--r--src/plugins/LADTable/plugin.js2
-rw-r--r--src/plugins/LADTable/pluginSpec.js19
-rw-r--r--src/plugins/URLIndicatorPlugin/URLIndicator.js25
-rw-r--r--src/plugins/URLIndicatorPlugin/URLIndicatorPlugin.js2
-rw-r--r--src/plugins/URLIndicatorPlugin/URLIndicatorSpec.js43
-rw-r--r--src/plugins/URLTimeSettingsSynchronizer/URLTimeSettingsSynchronizer.js2
-rw-r--r--src/plugins/URLTimeSettingsSynchronizer/plugin.js2
-rw-r--r--src/plugins/URLTimeSettingsSynchronizer/pluginSpec.js2
-rw-r--r--src/plugins/autoflow/AutoflowTabularConstants.js2
-rw-r--r--src/plugins/autoflow/AutoflowTabularController.js2
-rw-r--r--src/plugins/autoflow/AutoflowTabularPlugin.js2
-rw-r--r--src/plugins/autoflow/AutoflowTabularPluginSpec.js29
-rw-r--r--src/plugins/autoflow/AutoflowTabularRowController.js2
-rw-r--r--src/plugins/autoflow/AutoflowTabularView.js2
-rw-r--r--src/plugins/autoflow/VueView.js2
-rw-r--r--src/plugins/autoflow/autoflow-tabular.html2
-rw-r--r--src/plugins/autoflow/dom-observer.js2
-rw-r--r--src/plugins/charts/bar/BarGraphCompositionPolicy.js (renamed from src/plugins/charts/BarGraphCompositionPolicy.js)2
-rw-r--r--src/plugins/charts/bar/BarGraphConstants.js (renamed from src/plugins/charts/BarGraphConstants.js)0
-rw-r--r--src/plugins/charts/bar/BarGraphPlot.vue (renamed from src/plugins/charts/BarGraphPlot.vue)42
-rw-r--r--src/plugins/charts/bar/BarGraphView.vue (renamed from src/plugins/charts/BarGraphView.vue)214
-rw-r--r--src/plugins/charts/bar/BarGraphViewProvider.js (renamed from src/plugins/charts/BarGraphViewProvider.js)7
-rw-r--r--src/plugins/charts/bar/inspector/BarGraphInspectorViewProvider.js (renamed from src/plugins/charts/inspector/BarGraphInspectorViewProvider.js)0
-rw-r--r--src/plugins/charts/bar/inspector/BarGraphOptions.vue399
-rw-r--r--src/plugins/charts/bar/inspector/SeriesOptions.vue (renamed from src/plugins/charts/inspector/SeriesOptions.vue)36
-rw-r--r--src/plugins/charts/bar/plugin.js (renamed from src/plugins/charts/plugin.js)11
-rw-r--r--src/plugins/charts/bar/pluginSpec.js (renamed from src/plugins/charts/pluginSpec.js)208
-rw-r--r--src/plugins/charts/scatter/ScatterPlotCompositionPolicy.js (renamed from platform/identity/src/IdentityCreationDecorator.js)54
-rw-r--r--src/plugins/charts/scatter/ScatterPlotForm.vue146
-rw-r--r--src/plugins/charts/scatter/ScatterPlotView.vue346
-rw-r--r--src/plugins/charts/scatter/ScatterPlotViewProvider.js79
-rw-r--r--src/plugins/charts/scatter/ScatterPlotWithUnderlay.vue393
-rw-r--r--src/plugins/charts/scatter/inspector/PlotOptions.vue (renamed from src/plugins/charts/inspector/BarGraphOptions.vue)34
-rw-r--r--src/plugins/charts/scatter/inspector/PlotOptionsBrowse.vue153
-rw-r--r--src/plugins/charts/scatter/inspector/PlotOptionsEdit.vue262
-rw-r--r--src/plugins/charts/scatter/inspector/ScatterPlotInspectorViewProvider.js48
-rw-r--r--src/plugins/charts/scatter/plugin.js127
-rw-r--r--src/plugins/charts/scatter/pluginSpec.js421
-rw-r--r--src/plugins/charts/scatter/scatterPlotConstants.js4
-rw-r--r--src/plugins/clearData/ClearDataAction.js2
-rw-r--r--src/plugins/clearData/plugin.js4
-rw-r--r--src/plugins/clearData/pluginSpec.js232
-rw-r--r--src/plugins/clearData/test/ClearDataActionSpec.js143
-rw-r--r--src/plugins/clock/ClockViewProvider.js2
-rw-r--r--src/plugins/clock/components/Clock.vue2
-rw-r--r--src/plugins/clock/components/ClockIndicator.vue2
-rw-r--r--src/plugins/clock/plugin.js3
-rw-r--r--src/plugins/clock/pluginSpec.js2
-rw-r--r--src/plugins/condition/Condition.js4
-rw-r--r--src/plugins/condition/ConditionManager.js9
-rw-r--r--src/plugins/condition/ConditionManagerSpec.js2
-rw-r--r--src/plugins/condition/ConditionSetCompositionPolicy.js2
-rw-r--r--src/plugins/condition/ConditionSetCompositionPolicySpec.js2
-rw-r--r--src/plugins/condition/ConditionSetMetadataProvider.js2
-rw-r--r--src/plugins/condition/ConditionSetTelemetryProvider.js2
-rw-r--r--src/plugins/condition/ConditionSetViewProvider.js2
-rw-r--r--src/plugins/condition/ConditionSpec.js2
-rw-r--r--src/plugins/condition/StyleRuleManager.js18
-rw-r--r--src/plugins/condition/components/Condition.vue174
-rw-r--r--src/plugins/condition/components/ConditionCollection.vue55
-rw-r--r--src/plugins/condition/components/ConditionDescription.vue17
-rw-r--r--src/plugins/condition/components/ConditionError.vue14
-rw-r--r--src/plugins/condition/components/ConditionSet.vue26
-rw-r--r--src/plugins/condition/components/Criterion.vue85
-rw-r--r--src/plugins/condition/components/TestData.vue85
-rw-r--r--src/plugins/condition/components/conditionals.scss2
-rw-r--r--src/plugins/condition/components/inspector/ConditionalStylesView.vue475
-rw-r--r--src/plugins/condition/components/inspector/MultiSelectStylesView.vue280
-rw-r--r--src/plugins/condition/components/inspector/StyleEditor.vue74
-rw-r--r--src/plugins/condition/components/inspector/StylesView.vue268
-rw-r--r--src/plugins/condition/components/inspector/conditional-styles.scss9
-rw-r--r--src/plugins/condition/criterion/AllTelemetryCriterion.js2
-rw-r--r--src/plugins/condition/criterion/TelemetryCriterion.js2
-rw-r--r--src/plugins/condition/criterion/TelemetryCriterionSpec.js2
-rw-r--r--src/plugins/condition/plugin.js4
-rw-r--r--src/plugins/condition/pluginSpec.js168
-rw-r--r--src/plugins/condition/utils/constants.js2
-rw-r--r--src/plugins/condition/utils/evaluator.js2
-rw-r--r--src/plugins/condition/utils/evaluatorSpec.js2
-rw-r--r--src/plugins/condition/utils/operations.js2
-rw-r--r--src/plugins/condition/utils/operationsSpec.js2
-rw-r--r--src/plugins/condition/utils/styleUtils.js2
-rw-r--r--src/plugins/condition/utils/time.js2
-rw-r--r--src/plugins/condition/utils/timeSpec.js2
-rw-r--r--src/plugins/conditionWidget/ConditionWidgetViewProvider.js2
-rw-r--r--src/plugins/conditionWidget/components/ConditionWidget.vue113
-rw-r--r--src/plugins/conditionWidget/components/condition-widget.scss2
-rw-r--r--src/plugins/conditionWidget/plugin.js5
-rw-r--r--src/plugins/conditionWidget/pluginSpec.js195
-rw-r--r--src/plugins/defaultRootName/plugin.js2
-rw-r--r--src/plugins/defaultRootName/pluginSpec.js2
-rw-r--r--src/plugins/displayLayout/AlphanumericFormatViewProvider.js2
-rw-r--r--src/plugins/displayLayout/DisplayLayoutToolbar.js42
-rw-r--r--src/plugins/displayLayout/DisplayLayoutType.js2
-rw-r--r--src/plugins/displayLayout/LayoutDrag.js2
-rw-r--r--src/plugins/displayLayout/components/AlphanumericFormat.vue2
-rw-r--r--src/plugins/displayLayout/components/BoxView.vue2
-rw-r--r--src/plugins/displayLayout/components/DisplayLayout.vue50
-rw-r--r--src/plugins/displayLayout/components/EditMarquee.vue2
-rw-r--r--src/plugins/displayLayout/components/EllipseView.vue2
-rw-r--r--src/plugins/displayLayout/components/ImageView.vue2
-rw-r--r--src/plugins/displayLayout/components/LayoutFrame.vue5
-rw-r--r--src/plugins/displayLayout/components/LineView.vue2
-rw-r--r--src/plugins/displayLayout/components/SubobjectView.vue2
-rw-r--r--src/plugins/displayLayout/components/TelemetryView.vue68
-rw-r--r--src/plugins/displayLayout/components/TextView.vue2
-rw-r--r--src/plugins/displayLayout/components/layout-frame.scss5
-rw-r--r--src/plugins/displayLayout/components/telemetry-view.scss8
-rw-r--r--src/plugins/displayLayout/mixins/objectStyles-mixin.js8
-rw-r--r--src/plugins/displayLayout/plugin.js2
-rw-r--r--src/plugins/displayLayout/pluginSpec.js35
-rw-r--r--src/plugins/duplicate/DuplicateAction.js24
-rw-r--r--src/plugins/duplicate/DuplicateTask.js4
-rw-r--r--src/plugins/duplicate/plugin.js2
-rw-r--r--src/plugins/duplicate/pluginSpec.js2
-rw-r--r--src/plugins/exportAsJSONAction/ExportAsJSONAction.js92
-rw-r--r--src/plugins/exportAsJSONAction/ExportAsJSONActionSpec.js77
-rw-r--r--src/plugins/exportAsJSONAction/plugin.js2
-rw-r--r--src/plugins/faultManagement/FaultManagementInspector.vue129
-rw-r--r--src/plugins/faultManagement/FaultManagementInspectorViewProvider.js71
-rw-r--r--src/plugins/faultManagement/FaultManagementListHeader.vue105
-rw-r--r--src/plugins/faultManagement/FaultManagementListItem.vue223
-rw-r--r--src/plugins/faultManagement/FaultManagementListView.vue307
-rw-r--r--src/plugins/faultManagement/FaultManagementObjectProvider.js (renamed from example/notifications/src/DialogLaunchIndicator.js)53
-rw-r--r--src/plugins/faultManagement/FaultManagementPlugin.js42
-rw-r--r--src/plugins/faultManagement/FaultManagementSearch.vue90
-rw-r--r--src/plugins/faultManagement/FaultManagementToolbar.vue102
-rw-r--r--src/plugins/faultManagement/FaultManagementView.vue76
-rw-r--r--src/plugins/faultManagement/FaultManagementViewProvider.js69
-rw-r--r--src/plugins/faultManagement/constants.js122
-rw-r--r--src/plugins/faultManagement/fault-manager.scss268
-rw-r--r--src/plugins/faultManagement/pluginSpec.js (renamed from src/adapter/policies/LegacyCompositionPolicyAdapter.js)42
-rw-r--r--src/plugins/filters/FiltersInspectorViewProvider.js2
-rw-r--r--src/plugins/filters/plugin.js2
-rw-r--r--src/plugins/flexibleLayout/components/container.vue6
-rw-r--r--src/plugins/flexibleLayout/components/dropHint.vue2
-rw-r--r--src/plugins/flexibleLayout/components/flexible-layout.scss4
-rw-r--r--src/plugins/flexibleLayout/components/flexibleLayout.vue10
-rw-r--r--src/plugins/flexibleLayout/components/frame.vue15
-rw-r--r--src/plugins/flexibleLayout/components/resizeHandle.vue2
-rw-r--r--src/plugins/flexibleLayout/flexibleLayoutViewProvider.js2
-rw-r--r--src/plugins/flexibleLayout/plugin.js2
-rw-r--r--src/plugins/flexibleLayout/pluginSpec.js15
-rw-r--r--src/plugins/flexibleLayout/toolbarProvider.js2
-rw-r--r--src/plugins/flexibleLayout/utils/container.js2
-rw-r--r--src/plugins/flexibleLayout/utils/frame.js2
-rw-r--r--src/plugins/folderView/FolderGridView.js2
-rw-r--r--src/plugins/folderView/FolderListView.js2
-rw-r--r--src/plugins/folderView/components/GridItem.vue5
-rw-r--r--src/plugins/folderView/components/ListItem.vue5
-rw-r--r--src/plugins/folderView/components/ListView.vue2
-rw-r--r--src/plugins/folderView/components/list-view.scss32
-rw-r--r--src/plugins/folderView/constants.js2
-rw-r--r--src/plugins/folderView/plugin.js2
-rw-r--r--src/plugins/folderView/pluginSpec.js2
-rw-r--r--src/plugins/formActions/CreateAction.js4
-rw-r--r--src/plugins/formActions/CreateActionSpec.js128
-rw-r--r--src/plugins/formActions/CreateWizard.js10
-rw-r--r--src/plugins/formActions/EditPropertiesAction.js66
-rw-r--r--src/plugins/formActions/PropertiesAction.js2
-rw-r--r--src/plugins/formActions/plugin.js2
-rw-r--r--src/plugins/formActions/pluginSpec.js229
-rw-r--r--src/plugins/gauge/GaugePlugin.js211
-rw-r--r--src/plugins/gauge/GaugePluginSpec.js807
-rw-r--r--src/plugins/gauge/GaugeViewProvider.js67
-rw-r--r--src/plugins/gauge/components/Gauge.vue732
-rw-r--r--src/plugins/gauge/components/GaugeFormController.vue173
-rw-r--r--src/plugins/gauge/gauge-limit-util.js39
-rw-r--r--src/plugins/gauge/gauge.scss318
-rw-r--r--src/plugins/goToOriginalAction/goToOriginalAction.js2
-rw-r--r--src/plugins/goToOriginalAction/plugin.js2
-rw-r--r--src/plugins/goToOriginalAction/pluginSpec.js2
-rw-r--r--src/plugins/hyperlink/HyperlinkLayout.vue15
-rw-r--r--src/plugins/hyperlink/HyperlinkProvider.js2
-rw-r--r--src/plugins/hyperlink/plugin.js2
-rw-r--r--src/plugins/hyperlink/pluginSpec.js2
-rw-r--r--src/plugins/imagery/ImageryTimestripViewProvider.js2
-rw-r--r--src/plugins/imagery/ImageryView.js8
-rw-r--r--src/plugins/imagery/ImageryViewProvider.js6
-rw-r--r--src/plugins/imagery/components/Compass/Compass.vue2
-rw-r--r--src/plugins/imagery/components/Compass/CompassHUD.vue2
-rw-r--r--src/plugins/imagery/components/Compass/CompassRose.vue258
-rw-r--r--src/plugins/imagery/components/Compass/compass.scss2
-rw-r--r--src/plugins/imagery/components/Compass/pluginSpec.js2
-rw-r--r--src/plugins/imagery/components/FilterSettings.vue78
-rw-r--r--src/plugins/imagery/components/ImageControls.vue283
-rw-r--r--src/plugins/imagery/components/ImageryTimeView.vue12
-rw-r--r--src/plugins/imagery/components/ImageryView.vue618
-rw-r--r--src/plugins/imagery/components/ImageryViewMenuSwitcher.vue65
-rw-r--r--src/plugins/imagery/components/LayerSettings.vue59
-rw-r--r--src/plugins/imagery/components/RelatedTelemetry/RelatedTelemetry.js2
-rw-r--r--src/plugins/imagery/components/ZoomSettings.vue89
-rw-r--r--src/plugins/imagery/components/imagery-view.scss305
-rw-r--r--src/plugins/imagery/layers/example-imagery-layer-16x9.pngbin0 -> 8554 bytes
-rw-r--r--src/plugins/imagery/layers/example-imagery-layer-safe.pngbin0 -> 9245 bytes
-rw-r--r--src/plugins/imagery/layers/example-imagery-layer-scale.pngbin0 -> 11616 bytes
-rw-r--r--src/plugins/imagery/lib/eventHelpers.js98
-rw-r--r--src/plugins/imagery/mixins/imageryData.js114
-rw-r--r--src/plugins/imagery/plugin.js6
-rw-r--r--src/plugins/imagery/pluginSpec.js399
-rw-r--r--src/plugins/importFromJSONAction/ImportFromJSONAction.js31
-rw-r--r--src/plugins/importFromJSONAction/ImportFromJSONActionSpec.js2
-rw-r--r--src/plugins/importFromJSONAction/plugin.js2
-rw-r--r--src/plugins/interceptors/missingObjectInterceptor.js2
-rw-r--r--src/plugins/interceptors/pluginSpec.js2
-rw-r--r--src/plugins/latestDataClock/LADClock.js2
-rw-r--r--src/plugins/latestDataClock/plugin.js2
-rw-r--r--src/plugins/legacySupport/BundleRegistry.js78
-rw-r--r--src/plugins/legacySupport/BundleRegistrySpec.js88
-rw-r--r--src/plugins/legacySupport/installDefaultBundles.js101
-rw-r--r--src/plugins/legacySupport/plugin.js126
-rw-r--r--src/plugins/licenses/Licenses.vue2
-rw-r--r--src/plugins/licenses/plugin.js2
-rw-r--r--src/plugins/licenses/third-party-licenses.json7
-rw-r--r--src/plugins/linkAction/LinkAction.js23
-rw-r--r--src/plugins/linkAction/plugin.js2
-rw-r--r--src/plugins/linkAction/pluginSpec.js2
-rw-r--r--src/plugins/localStorage/LocalStorageObjectProvider.js6
-rw-r--r--src/plugins/localStorage/pluginSpec.js2
-rw-r--r--src/plugins/localTimeSystem/LocalTimeFormat.js2
-rw-r--r--src/plugins/localTimeSystem/LocalTimeSystem.js2
-rw-r--r--src/plugins/localTimeSystem/plugin.js2
-rw-r--r--src/plugins/localTimeSystem/pluginSpec.js2
-rw-r--r--src/plugins/move/MoveAction.js10
-rw-r--r--src/plugins/move/plugin.js2
-rw-r--r--src/plugins/move/pluginSpec.js9
-rw-r--r--src/plugins/myItems/myItemsInterceptor.js2
-rw-r--r--src/plugins/myItems/plugin.js10
-rw-r--r--src/plugins/myItems/pluginSpec.js2
-rw-r--r--src/plugins/newFolderAction/newFolderAction.js2
-rw-r--r--src/plugins/newFolderAction/plugin.js2
-rw-r--r--src/plugins/newFolderAction/pluginSpec.js2
-rw-r--r--src/plugins/notebook/NotebookType.js88
-rw-r--r--src/plugins/notebook/NotebookViewProvider.js72
-rw-r--r--src/plugins/notebook/components/Notebook.vue385
-rw-r--r--src/plugins/notebook/components/NotebookEmbed.vue43
-rw-r--r--src/plugins/notebook/components/NotebookEntry.vue126
-rw-r--r--src/plugins/notebook/components/NotebookSnapshotContainer.vue42
-rw-r--r--src/plugins/notebook/components/NotebookSnapshotIndicator.vue15
-rw-r--r--src/plugins/notebook/components/PageCollection.vue22
-rw-r--r--src/plugins/notebook/components/PageComponent.vue74
-rw-r--r--src/plugins/notebook/components/SearchResults.vue32
-rw-r--r--src/plugins/notebook/components/SectionCollection.vue22
-rw-r--r--src/plugins/notebook/components/SectionComponent.vue63
-rw-r--r--src/plugins/notebook/components/Sidebar.vue82
-rw-r--r--src/plugins/notebook/components/sidebar.scss44
-rw-r--r--src/plugins/notebook/monkeyPatchObjectAPIForNotebooks.js22
-rw-r--r--src/plugins/notebook/notebook-constants.js13
-rw-r--r--src/plugins/notebook/plugin.js299
-rw-r--r--src/plugins/notebook/pluginSpec.js29
-rw-r--r--src/plugins/notebook/snapshot.js64
-rw-r--r--src/plugins/notebook/utils/notebook-entries.js30
-rw-r--r--src/plugins/notebook/utils/notebook-entriesSpec.js41
-rw-r--r--src/plugins/notebook/utils/notebook-image.js2
-rw-r--r--src/plugins/notebook/utils/notebook-storageSpec.js2
-rw-r--r--src/plugins/notificationIndicator/plugin.js2
-rw-r--r--src/plugins/notificationIndicator/pluginSpec.js2
-rw-r--r--src/plugins/objectMigration/Migrations.js6
-rw-r--r--src/plugins/objectMigration/plugin.js2
-rw-r--r--src/plugins/openInNewTabAction/openInNewTabAction.js2
-rw-r--r--src/plugins/openInNewTabAction/plugin.js2
-rw-r--r--src/plugins/openInNewTabAction/pluginSpec.js2
-rw-r--r--src/plugins/operatorStatus/AbstractStatusIndicator.js106
-rw-r--r--src/plugins/operatorStatus/operator-status.scss142
-rw-r--r--src/plugins/operatorStatus/operatorStatus/OperatorStatus.vue188
-rw-r--r--src/plugins/operatorStatus/operatorStatus/OperatorStatusIndicator.js63
-rw-r--r--src/plugins/operatorStatus/plugin.js (renamed from platform/identity/src/IdentityIndicator.js)56
-rw-r--r--src/plugins/operatorStatus/pollQuestion/PollQuestion.vue190
-rw-r--r--src/plugins/operatorStatus/pollQuestion/PollQuestionIndicator.js63
-rw-r--r--src/plugins/performanceIndicator/plugin.js2
-rw-r--r--src/plugins/performanceIndicator/pluginSpec.js38
-rw-r--r--src/plugins/persistence/couch/CouchChangesFeed.js35
-rw-r--r--src/plugins/persistence/couch/CouchDocument.js5
-rw-r--r--src/plugins/persistence/couch/CouchObjectProvider.js277
-rw-r--r--src/plugins/persistence/couch/CouchObjectQueue.js2
-rw-r--r--src/plugins/persistence/couch/CouchSearchProvider.js150
-rw-r--r--src/plugins/persistence/couch/CouchStatusIndicator.js88
-rw-r--r--src/plugins/persistence/couch/README.md56
-rw-r--r--src/plugins/persistence/couch/plugin.js12
-rw-r--r--src/plugins/persistence/couch/pluginSpec.js172
-rw-r--r--src/plugins/plan/Plan.vue16
-rw-r--r--src/plugins/plan/PlanViewProvider.js4
-rw-r--r--src/plugins/plan/inspector/ActivityProperty.vue2
-rw-r--r--src/plugins/plan/inspector/PlanActivitiesView.vue13
-rw-r--r--src/plugins/plan/inspector/PlanActivityView.vue17
-rw-r--r--src/plugins/plan/inspector/PlanInspectorViewProvider.js2
-rw-r--r--src/plugins/plan/plan.scss2
-rw-r--r--src/plugins/plan/plugin.js2
-rw-r--r--src/plugins/plan/pluginSpec.js14
-rw-r--r--src/plugins/plan/util.js39
-rw-r--r--src/plugins/plot/LinearScale.js2
-rw-r--r--src/plugins/plot/MctPlot.vue285
-rw-r--r--src/plugins/plot/MctTicks.vue107
-rw-r--r--src/plugins/plot/Plot.vue84
-rw-r--r--src/plugins/plot/PlotViewProvider.js11
-rw-r--r--src/plugins/plot/actions/ViewActions.js (renamed from platform/entanglement/src/policies/CopyPolicy.js)61
-rw-r--r--src/plugins/plot/actions/utils.js3
-rw-r--r--src/plugins/plot/axis/XAxis.vue43
-rw-r--r--src/plugins/plot/axis/YAxis.vue62
-rw-r--r--src/plugins/plot/chart/LimitLabel.vue12
-rw-r--r--src/plugins/plot/chart/LimitLine.vue7
-rw-r--r--src/plugins/plot/chart/MCTChartAlarmLineSet.js12
-rw-r--r--src/plugins/plot/chart/MCTChartAlarmPointSet.js2
-rw-r--r--src/plugins/plot/chart/MCTChartLineLinear.js4
-rw-r--r--src/plugins/plot/chart/MCTChartLineStepAfter.js6
-rw-r--r--src/plugins/plot/chart/MCTChartPointSet.js4
-rw-r--r--src/plugins/plot/chart/MCTChartSeriesElement.js41
-rw-r--r--src/plugins/plot/chart/MctChart.vue7
-rw-r--r--src/plugins/plot/configuration/Collection.js14
-rw-r--r--src/plugins/plot/configuration/ConfigStore.js55
-rw-r--r--src/plugins/plot/configuration/LegendModel.js7
-rw-r--r--src/plugins/plot/configuration/Model.js79
-rw-r--r--src/plugins/plot/configuration/PlotConfigurationModel.js61
-rw-r--r--src/plugins/plot/configuration/PlotSeries.js90
-rw-r--r--src/plugins/plot/configuration/SeriesCollection.js28
-rw-r--r--src/plugins/plot/configuration/XAxisModel.js86
-rw-r--r--src/plugins/plot/configuration/YAxisModel.js253
-rw-r--r--src/plugins/plot/draw/Draw2D.js2
-rw-r--r--src/plugins/plot/draw/DrawLoader.js2
-rw-r--r--src/plugins/plot/draw/DrawWebGL.js3
-rw-r--r--src/plugins/plot/draw/MarkerShapes.js2
-rw-r--r--src/plugins/plot/inspector/PlotOptions.vue2
-rw-r--r--src/plugins/plot/inspector/PlotOptionsBrowse.vue156
-rw-r--r--src/plugins/plot/inspector/PlotOptionsEdit.vue79
-rw-r--r--src/plugins/plot/inspector/PlotOptionsItem.vue62
-rw-r--r--src/plugins/plot/inspector/PlotsInspectorViewProvider.js6
-rw-r--r--src/plugins/plot/inspector/StackedPlotsInspectorViewProvider.js59
-rw-r--r--src/plugins/plot/inspector/forms/LegendForm.vue84
-rw-r--r--src/plugins/plot/inspector/forms/SeriesForm.vue186
-rw-r--r--src/plugins/plot/inspector/forms/YAxisForm.vue200
-rw-r--r--src/plugins/plot/inspector/forms/formUtil.js12
-rw-r--r--src/plugins/plot/legend/PlotLegend.vue77
-rw-r--r--src/plugins/plot/legend/PlotLegendItemCollapsed.vue32
-rw-r--r--src/plugins/plot/legend/PlotLegendItemExpanded.vue23
-rw-r--r--src/plugins/plot/lib/eventHelpers.js9
-rw-r--r--src/plugins/plot/mathUtils.js44
-rw-r--r--src/plugins/plot/overlayPlot/OverlayPlotViewProvider.js11
-rw-r--r--src/plugins/plot/plugin.js24
-rw-r--r--src/plugins/plot/pluginSpec.js353
-rw-r--r--src/plugins/plot/stackedPlot/StackedPlot.vue185
-rw-r--r--src/plugins/plot/stackedPlot/StackedPlotItem.vue150
-rw-r--r--src/plugins/plot/stackedPlot/StackedPlotViewProvider.js11
-rw-r--r--src/plugins/plot/stackedPlot/mixins/objectStyles-mixin.js137
-rw-r--r--src/plugins/plot/stackedPlot/pluginSpec.js771
-rw-r--r--src/plugins/plot/stackedPlot/stackedPlotConfigurationInterceptor.js (renamed from example/mobile/bundle.js)31
-rw-r--r--src/plugins/plot/tickUtils.js38
-rw-r--r--src/plugins/plugins.js91
-rw-r--r--src/plugins/remoteClock/RemoteClock.js32
-rw-r--r--src/plugins/remoteClock/RemoteClockSpec.js2
-rw-r--r--src/plugins/remoteClock/plugin.js2
-rw-r--r--src/plugins/remoteClock/requestInterceptor.js (renamed from example/export/bundle.js)42
-rw-r--r--src/plugins/remove/RemoveAction.js44
-rw-r--r--src/plugins/remove/plugin.js2
-rw-r--r--src/plugins/remove/pluginSpec.js2
-rw-r--r--src/plugins/staticRootPlugin/StaticModelProvider.js179
-rw-r--r--src/plugins/staticRootPlugin/StaticModelProviderSpec.js226
-rw-r--r--src/plugins/staticRootPlugin/plugin.js105
-rw-r--r--src/plugins/staticRootPlugin/static-provider-test.json2
-rw-r--r--src/plugins/summaryWidget/SummaryWidgetViewPolicy.js2
-rw-r--r--src/plugins/summaryWidget/SummaryWidgetsCompositionPolicy.js2
-rw-r--r--src/plugins/summaryWidget/src/Condition.js41
-rw-r--r--src/plugins/summaryWidget/src/ConditionManager.js12
-rw-r--r--src/plugins/summaryWidget/src/Rule.js154
-rw-r--r--src/plugins/summaryWidget/src/SummaryWidget.js71
-rw-r--r--src/plugins/summaryWidget/src/TestDataItem.js28
-rw-r--r--src/plugins/summaryWidget/src/TestDataManager.js18
-rw-r--r--src/plugins/summaryWidget/src/WidgetDnD.js27
-rw-r--r--src/plugins/summaryWidget/src/eventHelpers.js2
-rw-r--r--src/plugins/summaryWidget/src/input/ColorPalette.js21
-rw-r--r--src/plugins/summaryWidget/src/input/IconPalette.js30
-rw-r--r--src/plugins/summaryWidget/src/input/Palette.js71
-rw-r--r--src/plugins/summaryWidget/src/input/Select.js48
-rw-r--r--src/plugins/summaryWidget/src/telemetry/EvaluatorPool.js2
-rw-r--r--src/plugins/summaryWidget/src/telemetry/EvaluatorPoolSpec.js2
-rw-r--r--src/plugins/summaryWidget/src/telemetry/SummaryWidgetCondition.js2
-rw-r--r--src/plugins/summaryWidget/src/telemetry/SummaryWidgetConditionSpec.js2
-rw-r--r--src/plugins/summaryWidget/src/telemetry/SummaryWidgetEvaluator.js2
-rw-r--r--src/plugins/summaryWidget/src/telemetry/SummaryWidgetMetadataProvider.js2
-rw-r--r--src/plugins/summaryWidget/src/telemetry/SummaryWidgetRule.js2
-rw-r--r--src/plugins/summaryWidget/src/telemetry/SummaryWidgetRuleSpec.js2
-rw-r--r--src/plugins/summaryWidget/src/telemetry/SummaryWidgetTelemetryProvider.js2
-rw-r--r--src/plugins/summaryWidget/src/telemetry/SummaryWidgetTelemetryProviderSpec.js2
-rw-r--r--src/plugins/summaryWidget/src/telemetry/operations.js2
-rw-r--r--src/plugins/summaryWidget/test/ConditionManagerSpec.js2
-rw-r--r--src/plugins/summaryWidget/test/ConditionSpec.js62
-rw-r--r--src/plugins/summaryWidget/test/RuleSpec.js29
-rw-r--r--src/plugins/summaryWidget/test/SummaryWidgetSpec.js14
-rw-r--r--src/plugins/summaryWidget/test/SummaryWidgetViewPolicySpec.js2
-rw-r--r--src/plugins/summaryWidget/test/TestDataItemSpec.js53
-rw-r--r--src/plugins/summaryWidget/test/TestDataManagerSpec.js8
-rw-r--r--src/plugins/tabs/components/tabs.vue19
-rw-r--r--src/plugins/tabs/plugin.js2
-rw-r--r--src/plugins/tabs/pluginSpec.js2
-rw-r--r--src/plugins/tabs/tabs.js2
-rwxr-xr-xsrc/plugins/telemetryMean/plugin.js2
-rw-r--r--src/plugins/telemetryMean/src/MeanTelemetryProvider.js2
-rw-r--r--src/plugins/telemetryMean/src/MeanTelemetryProviderSpec.js2
-rw-r--r--src/plugins/telemetryMean/src/MockTelemetryApi.js2
-rw-r--r--src/plugins/telemetryMean/src/TelemetryAverager.js2
-rw-r--r--src/plugins/telemetryTable/TableConfigurationViewProvider.js2
-rw-r--r--src/plugins/telemetryTable/TelemetryTable.js2
-rw-r--r--src/plugins/telemetryTable/TelemetryTableColumn.js2
-rw-r--r--src/plugins/telemetryTable/TelemetryTableConfiguration.js2
-rw-r--r--src/plugins/telemetryTable/TelemetryTableNameColumn.js2
-rw-r--r--src/plugins/telemetryTable/TelemetryTableRow.js2
-rw-r--r--src/plugins/telemetryTable/TelemetryTableType.js2
-rw-r--r--src/plugins/telemetryTable/TelemetryTableUnitColumn.js2
-rw-r--r--src/plugins/telemetryTable/TelemetryTableViewProvider.js2
-rw-r--r--src/plugins/telemetryTable/ViewActions.js2
-rw-r--r--src/plugins/telemetryTable/collections/TableRowCollection.js10
-rw-r--r--src/plugins/telemetryTable/components/table-cell.vue2
-rw-r--r--src/plugins/telemetryTable/components/table-column-header.vue2
-rw-r--r--src/plugins/telemetryTable/components/table-row.vue2
-rw-r--r--src/plugins/telemetryTable/components/table.scss5
-rw-r--r--src/plugins/telemetryTable/components/table.vue50
-rw-r--r--src/plugins/telemetryTable/plugin.js2
-rw-r--r--src/plugins/telemetryTable/pluginSpec.js201
-rw-r--r--src/plugins/timeConductor/Conductor.vue14
-rw-r--r--src/plugins/timeConductor/ConductorAxis.vue2
-rw-r--r--src/plugins/timeConductor/ConductorHistory.vue7
-rw-r--r--src/plugins/timeConductor/ConductorInputsFixed.vue24
-rw-r--r--src/plugins/timeConductor/ConductorInputsRealtime.vue28
-rw-r--r--src/plugins/timeConductor/ConductorMode.vue12
-rw-r--r--src/plugins/timeConductor/ConductorModeIcon.vue2
-rw-r--r--src/plugins/timeConductor/ConductorTimeSystem.vue9
-rw-r--r--src/plugins/timeConductor/DatePicker.vue2
-rw-r--r--src/plugins/timeConductor/conductor.scss5
-rw-r--r--src/plugins/timeConductor/date-picker.scss3
-rw-r--r--src/plugins/timeConductor/independent/IndependentTimeConductor.vue88
-rw-r--r--src/plugins/timeConductor/independent/Mode.vue16
-rw-r--r--src/plugins/timeConductor/plugin.js2
-rw-r--r--src/plugins/timeConductor/pluginSpec.js2
-rw-r--r--src/plugins/timeConductor/timePopup.vue5
-rw-r--r--src/plugins/timeConductor/utcMultiTimeFormat.js2
-rw-r--r--src/plugins/timeline/TimelineCompositionPolicy.js70
-rw-r--r--src/plugins/timeline/TimelineObjectView.vue32
-rw-r--r--src/plugins/timeline/TimelineViewLayout.vue43
-rw-r--r--src/plugins/timeline/TimelineViewProvider.js2
-rw-r--r--src/plugins/timeline/plugin.js5
-rw-r--r--src/plugins/timeline/pluginSpec.js222
-rw-r--r--src/plugins/timeline/timelineInterceptor.js2
-rw-r--r--src/plugins/timelist/Timelist.vue465
-rw-r--r--src/plugins/timelist/TimelistCompositionPolicy.js (renamed from src/plugins/legacySupport/legacyRegistrySpec.js)23
-rw-r--r--src/plugins/timelist/TimelistViewProvider.js68
-rw-r--r--src/plugins/timelist/constants.js24
-rw-r--r--src/plugins/timelist/inspector/EventProperties.vue124
-rw-r--r--src/plugins/timelist/inspector/Filtering.vue91
-rw-r--r--src/plugins/timelist/inspector/TimeListInspectorViewProvider.js70
-rw-r--r--src/plugins/timelist/inspector/TimelistPropertiesView.vue146
-rw-r--r--src/plugins/timelist/plugin.js70
-rw-r--r--src/plugins/timelist/pluginSpec.js379
-rw-r--r--src/plugins/timelist/timelist.scss (renamed from src/adapter/actions/LegacyActionAdapter.js)40
-rw-r--r--src/plugins/timer/TimerViewProvider.js2
-rw-r--r--src/plugins/timer/actions/PauseTimerAction.js11
-rw-r--r--src/plugins/timer/actions/RestartTimerAction.js11
-rw-r--r--src/plugins/timer/actions/StartTimerAction.js11
-rw-r--r--src/plugins/timer/actions/StopTimerAction.js11
-rw-r--r--src/plugins/timer/components/Timer.vue39
-rw-r--r--src/plugins/timer/plugin.js2
-rw-r--r--src/plugins/timer/pluginSpec.js9
-rw-r--r--src/plugins/userIndicator/components/UserIndicator.vue (renamed from platform/commonUI/general/res/templates/controls/action-button.html)41
-rw-r--r--src/plugins/userIndicator/plugin.js (renamed from platform/core/test/services/NowSpec.js)51
-rw-r--r--src/plugins/userIndicator/pluginSpec.js100
-rw-r--r--src/plugins/utcTimeSystem/LocalClock.js2
-rw-r--r--src/plugins/utcTimeSystem/UTCTimeFormat.js2
-rw-r--r--src/plugins/utcTimeSystem/UTCTimeSystem.js2
-rw-r--r--src/plugins/utcTimeSystem/plugin.js2
-rw-r--r--src/plugins/utcTimeSystem/pluginSpec.js2
-rw-r--r--src/plugins/viewDatumAction/ViewDatumAction.js2
-rw-r--r--src/plugins/viewDatumAction/plugin.js2
-rw-r--r--src/plugins/viewDatumAction/pluginSpec.js2
-rw-r--r--src/plugins/viewLargeAction/plugin.js2
-rw-r--r--src/plugins/viewLargeAction/viewLargeAction.js43
-rw-r--r--src/plugins/webPage/WebPageViewProvider.js2
-rw-r--r--src/plugins/webPage/plugin.js2
-rw-r--r--src/plugins/webPage/pluginSpec.js106
-rw-r--r--src/selection/Selection.js2
-rw-r--r--src/start.frag45
-rw-r--r--src/styles/_about.scss2
-rw-r--r--src/styles/_constants-espresso.scss34
-rw-r--r--src/styles/_constants-maelstrom.scss32
-rw-r--r--src/styles/_constants-mobile.scss2
-rw-r--r--src/styles/_constants-snow.scss30
-rwxr-xr-xsrc/styles/_constants.scss26
-rw-r--r--src/styles/_controls.scss176
-rw-r--r--src/styles/_forms.scss51
-rw-r--r--src/styles/_global.scss25
-rwxr-xr-xsrc/styles/_glyphs.scss20
-rw-r--r--src/styles/_legacy-messages.scss2
-rw-r--r--src/styles/_legacy-plots.scss24
-rw-r--r--src/styles/_legacy.scss13
-rw-r--r--src/styles/_mixins.scss8
-rw-r--r--src/styles/_status.scss2
-rw-r--r--src/styles/_table.scss4
-rw-r--r--src/styles/fonts/Open MCT Symbols 16px.json844
-rw-r--r--src/styles/fonts/Open-MCT-Symbols-16px.svg326
-rw-r--r--src/styles/fonts/Open-MCT-Symbols-16px.ttfbin24264 -> 26020 bytes
-rw-r--r--src/styles/fonts/Open-MCT-Symbols-16px.woffbin24340 -> 26096 bytes
-rw-r--r--src/styles/notebook.scss147
-rw-r--r--src/styles/plotly.scss2
-rw-r--r--src/styles/vue-styles.scss9
-rw-r--r--src/tools/url.js4
-rw-r--r--src/tools/urlSpec.js70
-rw-r--r--src/ui/color/Color.js2
-rw-r--r--src/ui/color/ColorHelper.js2
-rw-r--r--src/ui/color/ColorPalette.js2
-rw-r--r--src/ui/color/ColorSwatch.vue70
-rw-r--r--src/ui/components/List/ListHeader.vue56
-rw-r--r--src/ui/components/List/ListItem.vue53
-rw-r--r--src/ui/components/List/ListView.vue142
-rw-r--r--src/ui/components/List/list-view.scss40
-rw-r--r--src/ui/components/ObjectFrame.vue57
-rw-r--r--src/ui/components/ObjectLabel.vue12
-rw-r--r--src/ui/components/ObjectPath.vue139
-rw-r--r--src/ui/components/ObjectView.vue34
-rw-r--r--src/ui/components/ProgressBar.vue7
-rw-r--r--src/ui/components/SelectorDialogTree.vue240
-rw-r--r--src/ui/components/SelectorDialogTreeItem.vue206
-rw-r--r--src/ui/components/TimeSystemAxis.vue7
-rw-r--r--src/ui/components/components.js (renamed from platform/persistence/queue/src/PersistenceFailureConstants.js)15
-rw-r--r--src/ui/components/componentsSpec.js (renamed from src/plugins/nonEditableFolder/pluginSpec.js)22
-rw-r--r--src/ui/components/object-frame.scss18
-rw-r--r--src/ui/components/object-label.scss2
-rw-r--r--src/ui/components/search.vue1
-rw-r--r--src/ui/components/swim-lane/SwimLane.vue41
-rw-r--r--src/ui/components/swim-lane/swimlane.scss3
-rw-r--r--src/ui/components/tags/TagEditor.vue152
-rw-r--r--src/ui/components/tags/TagSelection.vue152
-rw-r--r--src/ui/components/tags/tags.scss67
-rw-r--r--src/ui/inspector/ElementItem.vue2
-rw-r--r--src/ui/inspector/ElementsPool.vue2
-rw-r--r--src/ui/inspector/Inspector.vue23
-rw-r--r--src/ui/inspector/InspectorDetailsSpec.js2
-rw-r--r--src/ui/inspector/InspectorStylesSpec.js2
-rw-r--r--src/ui/inspector/InspectorStylesSpecMocks.js2
-rw-r--r--src/ui/inspector/InspectorViews.vue2
-rw-r--r--src/ui/inspector/Location.vue7
-rw-r--r--src/ui/inspector/ObjectName.vue34
-rw-r--r--src/ui/inspector/details/DetailText.vue2
-rw-r--r--src/ui/inspector/details/Properties.vue5
-rw-r--r--src/ui/inspector/inspector.scss28
-rw-r--r--src/ui/inspector/styles/FontStyleEditor.vue10
-rw-r--r--src/ui/inspector/styles/SavedStyleSelector.vue9
-rw-r--r--src/ui/inspector/styles/SavedStylesInspectorView.vue2
-rw-r--r--src/ui/inspector/styles/SavedStylesView.vue2
-rw-r--r--src/ui/inspector/styles/StylesInspectorView.vue2
-rw-r--r--src/ui/layout/AboutDialog.vue2
-rw-r--r--src/ui/layout/AppLogo.vue2
-rw-r--r--src/ui/layout/BrowseBar.vue19
-rw-r--r--src/ui/layout/CreateButton.vue47
-rw-r--r--src/ui/layout/Layout.vue5
-rw-r--r--src/ui/layout/LayoutSpec.js2
-rw-r--r--src/ui/layout/MCTSearch.vue13
-rw-r--r--src/ui/layout/layout.scss4
-rw-r--r--src/ui/layout/mct-search.scss10
-rw-r--r--src/ui/layout/mct-tree.scss57
-rw-r--r--src/ui/layout/mct-tree.vue192
-rw-r--r--src/ui/layout/pane.vue5
-rw-r--r--src/ui/layout/search/AnnotationSearchResult.vue150
-rw-r--r--src/ui/layout/search/GrandSearch.vue146
-rw-r--r--src/ui/layout/search/GrandSearchSpec.js203
-rw-r--r--src/ui/layout/search/ObjectSearchResult.vue142
-rw-r--r--src/ui/layout/search/SearchResultsDropDown.vue106
-rw-r--r--src/ui/layout/search/search.scss138
-rw-r--r--src/ui/layout/status-bar/Indicators.vue15
-rw-r--r--src/ui/layout/status-bar/NotificationBanner.vue9
-rw-r--r--src/ui/layout/tree-item.vue46
-rw-r--r--src/ui/mixins/context-menu-gesture.js4
-rw-r--r--src/ui/preview/Preview.vue99
-rw-r--r--src/ui/preview/PreviewAction.js8
-rw-r--r--src/ui/preview/ViewHistoricalDataAction.js2
-rw-r--r--src/ui/preview/plugin.js2
-rw-r--r--src/ui/preview/preview-header.vue18
-rw-r--r--src/ui/registries/InspectorViewRegistry.js2
-rw-r--r--src/ui/registries/ToolbarRegistry.js2
-rw-r--r--src/ui/registries/ViewRegistry.js3
-rw-r--r--src/ui/router/ApplicationRouter.js6
-rw-r--r--src/ui/router/Browse.js4
-rw-r--r--src/ui/toolbar/components/toolbar-toggle-button.vue12
-rw-r--r--src/utils/agent/Agent.js18
-rw-r--r--src/utils/agent/AgentSpec.js32
-rw-r--r--src/utils/clock/DefaultClock.js2
-rw-r--r--src/utils/clock/Ticker.js2
-rw-r--r--src/utils/duration.js2
-rw-r--r--src/utils/raf.js14
-rw-r--r--src/utils/rafSpec.js61
-rw-r--r--src/utils/template/templateHelpers.js14
-rw-r--r--src/utils/testing.js2
-rw-r--r--src/utils/textHighlight/TextHighlight.vue4
-rw-r--r--tsconfig.json20
-rw-r--r--webpack.common.js95
-rw-r--r--webpack.coverage.js43
-rw-r--r--webpack.prod.js2
1300 files changed, 34089 insertions, 64900 deletions
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 368984a98..6814113ac 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -2,7 +2,7 @@ version: 2.1
executors:
pw-focal-development:
docker:
- - image: mcr.microsoft.com/playwright:focal
+ - image: mcr.microsoft.com/playwright:v1.23.0-focal
environment:
NODE_ENV: development # Needed to ensure 'dist' folder created and devDependencies installed
parameters:
@@ -12,7 +12,7 @@ parameters:
type: boolean
commands:
build_and_install:
- description: "All steps used to build and install. Will not work on node10"
+ description: "All steps used to build and install. Will use cache if found"
parameters:
node-version:
type: string
@@ -23,7 +23,7 @@ commands:
- node/install:
install-npm: true
node-version: << parameters.node-version >>
- - run: npm install
+ - run: npm install --prefer-offline --no-audit --progress=false
restore_cache_cmd:
description: "Custom command for restoring cache with the ability to bust cache. When BUST_CACHE is set to true, jobs will not restore cache"
parameters:
@@ -31,7 +31,7 @@ commands:
type: string
steps:
- when:
- condition:
+ condition:
equal: [false, << pipeline.parameters.BUST_CACHE >> ]
steps:
- restore_cache:
@@ -41,7 +41,7 @@ commands:
parameters:
node-version:
type: string
- steps:
+ steps:
- save_cache:
key: deps-{{ .Branch }}--<< parameters.node-version >>--{{ checksum "package.json" }}-{{ checksum ".circleci/config.yml" }}
paths:
@@ -58,13 +58,17 @@ commands:
ls -latR >> /tmp/artifacts/dir.txt
- store_artifacts:
path: /tmp/artifacts/
- upload_code_covio:
- description: "Command to upload code coverage reports to codecov.io"
- steps:
- - run: curl -Os https://uploader.codecov.io/latest/linux/codecov;chmod +x codecov;./codecov
+ generate_e2e_code_cov_report:
+ description: "Generate e2e code coverage artifacts and publish to codecov.io. Needed to that we can ignore the exit code status of the npm run test"
+ parameters:
+ suite:
+ type: string
+ steps:
+ - run: npm run cov:e2e:report
+ - run: npm run cov:e2e:<<parameters.suite>>:publish
orbs:
node: circleci/node@4.9.0
- browser-tools: circleci/browser-tools@1.2.3
+ browser-tools: circleci/browser-tools@1.3.0
jobs:
npm-audit:
parameters:
@@ -76,14 +80,14 @@ jobs:
node-version: <<parameters.node-version>>
- run: npm audit --audit-level=low
- generate_and_store_version_and_filesystem_artifacts
- node10-lint:
+ lint:
+ parameters:
+ node-version:
+ type: string
executor: pw-focal-development
steps:
- - checkout
- - node/install:
- install-npm: false #Cannot install latest npm version with node10.
- node-version: lts/dubnium
- - run: npm install
+ - build_and_install:
+ node-version: <<parameters.node-version>>
- run: npm run lint
- generate_and_store_version_and_filesystem_artifacts
unit-test:
@@ -101,7 +105,7 @@ jobs:
equal: [ "FirefoxESR", <<parameters.browser>> ]
steps:
- browser-tools/install-firefox:
- version: "91.4.0esr" #https://archive.mozilla.org/pub/firefox/releases/
+ version: "91.7.1esr" #https://archive.mozilla.org/pub/firefox/releases/
- when:
condition:
equal: [ "FirefoxHeadless", <<parameters.browser>> ]
@@ -113,13 +117,14 @@ jobs:
steps:
- browser-tools/install-chrome:
replace-existing: false
- - run: npm run test:coverage -- --browsers=<<parameters.browser>>
+ - run: npm run test -- --browsers=<<parameters.browser>>
+ - run: npm run cov:unit:publish
- save_cache_cmd:
node-version: <<parameters.node-version>>
- store_test_results:
path: dist/reports/tests/
- store_artifacts:
- path: dist/reports/
+ path: coverage
- generate_and_store_version_and_filesystem_artifacts
e2e-test:
parameters:
@@ -128,49 +133,70 @@ jobs:
suite:
type: string
executor: pw-focal-development
+ parallelism: 4
steps:
- build_and_install:
node-version: <<parameters.node-version>>
- - run: npx playwright install
- - run: npm run test:e2e:<<parameters.suite>>
+ - when: #Only install chrome-beta when running the full suite to save $$$
+ condition:
+ equal: [ "full", <<parameters.suite>> ]
+ steps:
+ - run: npx playwright install chrome-beta
+ - run: SHARD="$((${CIRCLE_NODE_INDEX}+1))"; npm run test:e2e:<<parameters.suite>> -- --shard=${SHARD}/${CIRCLE_NODE_TOTAL}
+ - generate_e2e_code_cov_report:
+ suite: <<parameters.suite>>
- store_test_results:
path: test-results/results.xml
- store_artifacts:
path: test-results
+ - store_artifacts:
+ path: coverage
+ - store_artifacts:
+ path: html-test-results
+ - generate_and_store_version_and_filesystem_artifacts
+ perf-test:
+ parameters:
+ node-version:
+ type: string
+ executor: pw-focal-development
+ steps:
+ - build_and_install:
+ node-version: <<parameters.node-version>>
+ - run: npm run test:perf
+ - store_test_results:
+ path: test-results/results.xml
+ - store_artifacts:
+ path: test-results
+ - store_artifacts:
+ path: html-test-results
- generate_and_store_version_and_filesystem_artifacts
workflows:
overall-circleci-commit-status: #These jobs run on every commit
jobs:
- - node10-lint
+ - lint:
+ name: node14-lint
+ node-version: lts/fermium
- unit-test:
- name: node12-chrome
- node-version: lts/erbium
+ name: node16-chrome
+ node-version: lts/gallium
browser: ChromeHeadless
- - unit-test:
- name: node14-chrome
- node-version: lts/fermium
+ - unit-test:
+ name: node18-chrome
+ node-version: "18"
browser: ChromeHeadless
- post-steps:
- - upload_code_covio
- e2e-test:
- name: e2e-smoke
- node-version: lts/fermium
+ name: e2e-ci
+ node-version: lts/gallium
suite: ci
+ - perf-test:
+ node-version: lts/gallium
the-nightly: #These jobs do not run on PRs, but against master at night
jobs:
- unit-test:
- name: node10-chrome-nightly
- node-version: lts/dubnium
- browser: ChromeHeadless
- - unit-test:
- name: node12-firefoxESR-nightly
- node-version: lts/erbium
+ name: node16-firefoxESR-nightly
+ node-version: lts/gallium
browser: FirefoxESR
- unit-test:
- name: node12-chrome-nightly
- node-version: lts/erbium
- browser: ChromeHeadless
- - unit-test:
name: node14-firefox-nightly
node-version: lts/fermium
browser: FirefoxHeadless
@@ -178,11 +204,19 @@ workflows:
name: node14-chrome-nightly
node-version: lts/fermium
browser: ChromeHeadless
+ - unit-test:
+ name: node16-chrome-nightly
+ node-version: lts/gallium
+ browser: ChromeHeadless
+ - unit-test:
+ name: node18-chrome
+ node-version: "18"
+ browser: ChromeHeadless
- npm-audit:
- node-version: lts/fermium
+ node-version: lts/gallium
- e2e-test:
name: e2e-full-nightly
- node-version: lts/fermium
+ node-version: lts/gallium
suite: full
triggers:
- schedule:
diff --git a/.eslintrc.js b/.eslintrc.js
index 03a5b1fef..26e807490 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -1,4 +1,4 @@
-const LEGACY_FILES = ["platform/**", "example/**"];
+const LEGACY_FILES = ["example/**"];
module.exports = {
"env": {
"browser": true,
@@ -11,12 +11,14 @@ module.exports = {
},
"extends": [
"eslint:recommended",
+ "plugin:compat/recommended",
"plugin:vue/recommended",
"plugin:you-dont-need-lodash-underscore/compatible"
],
"parser": "vue-eslint-parser",
"parserOptions": {
- "parser": "babel-eslint",
+ "parser": "@babel/eslint-parser",
+ "requireConfigFile": false,
"allowImportExportEverywhere": true,
"ecmaVersion": 2015,
"ecmaFeatures": {
@@ -27,6 +29,7 @@ module.exports = {
"you-dont-need-lodash-underscore/omit": "off",
"you-dont-need-lodash-underscore/throttle": "off",
"you-dont-need-lodash-underscore/flatten": "off",
+ "you-dont-need-lodash-underscore/get": "off",
"no-bitwise": "error",
"curly": "error",
"eqeqeq": "error",
@@ -35,7 +38,6 @@ module.exports = {
"no-inner-declarations": "off",
"no-use-before-define": ["error", "nofunc"],
"no-caller": "error",
- "no-sequences": "error",
"no-irregular-whitespace": "error",
"no-new": "error",
"no-shadow": "error",
@@ -239,13 +241,12 @@ module.exports = {
],
"vue/max-attributes-per-line": ["error", {
"singleline": 1,
- "multiline": {
- "max": 1,
- "allowFirstLine": true
- }
+ "multiline": 1,
}],
+ "vue/first-attribute-linebreak": "error",
"vue/multiline-html-element-content-newline": "off",
"vue/singleline-html-element-content-newline": "off",
+ "vue/multi-word-component-names": "off", // TODO enable, align with conventions
"vue/no-mutating-props": "off"
},
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index cdf9474c1..2fd6b8091 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -17,15 +17,6 @@ assignees: ''
#### Expected vs Current Behavior
<!--- Tell us what should have happened -->
-#### Impact Check List
-<!--- Please select from the following options -->
-
-- [ ] Data loss or misrepresented data?
-- [ ] Regression? Did this used to work or has it always been broken?
-- [ ] Is there a workaround available?
-- [ ] Does this impact a critical component?
-- [ ] Is this just a visual bug with no functional impact?
-
#### Steps to Reproduce
<!--- Provide a link to a live example, or an unambiguous set of steps to -->
<!--- reproduce this bug. Include code to reproduce, if relevant -->
@@ -35,10 +26,22 @@ assignees: ''
4.
#### Environment
+<!--- If encountered on local machine, execute the following:
+<!--- npx envinfo --system --browsers --npmPackages --binaries --markdown -->
* Open MCT Version: <!--- date of build, version, or SHA -->
* Deployment Type: <!--- npm dev? VIPER Dev? openmct-yamcs? -->
* OS:
* Browser:
+#### Impact Check List
+<!--- Please select from the following options -->
+- [ ] Data loss or misrepresented data?
+- [ ] Regression? Did this used to work or has it always been broken?
+- [ ] Is there a workaround available?
+- [ ] Does this impact a critical component?
+- [ ] Is this just a visual bug with no functional impact?
+- [ ] Does this block the execution of e2e tests?
+- [ ] Does this have an impact on Performance?
+
#### Additional Information
<!--- Include any screenshots, gifs, or logs which will expedite triage -->
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 819bdf6fb..8cbde6e73 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -1,3 +1,9 @@
+<!--- Note: Please open the PR in draft form until you are ready for active review. -->
+Closes <!--- Insert Issue Number(s) this PR addresses. Start by typing # will open a dropdown of recent issues. Note: this does not work on PRs which target release branches -->
+
+### Describe your changes:
+<!--- Describe your changes and add any comments about your approach either here or inline if code comments aren't added -->
+
### All Submissions:
* [ ] Have you followed the guidelines in our [Contributing document](https://github.com/nasa/openmct/blob/master/CONTRIBUTING.md)?
@@ -10,4 +16,14 @@
* [ ] Unit tests included and/or updated with changes?
* [ ] Command line build passes?
* [ ] Has this been smoke tested?
-* [ ] Testing instructions included in associated issue?
+* [ ] Testing instructions included in associated issue OR is this a dependency/testcase change?
+
+### Reviewer Checklist
+
+* [ ] Changes appear to address issue?
+* [ ] Changes appear not to be breaking changes?
+* [ ] Appropriate unit tests included?
+* [ ] Code style and in-line documentation are appropriate?
+* [ ] Commit messages meet standards?
+* [ ] Has associated issue been labelled unverified? (only applicable if this PR closes the issue)
+* [ ] Has associated issue been labelled bug? (only applicable if this PR is for a bug fix)
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 1e240e543..a5545f0cd 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -5,18 +5,14 @@ updates:
directory: "/"
schedule:
interval: "daily"
- open-pull-requests-limit: 4
+ open-pull-requests-limit: 10
labels:
- "type:maintenance"
- "dependencies"
- "pr:e2e"
- allow:
- - dependency-name: "*eslint*"
- - dependency-name: "*karma*"
- - dependency-name: "*jasmine*"
- - dependency-name: "*playwright*"
- - dependency-name: "*percy*"
- - dependency-name: "*vue-loader*"
+ - "pr:daveit"
+ - "pr:visual"
+ - "pr:platform"
- package-ecosystem: "github-actions"
directory: "/"
@@ -25,3 +21,4 @@ updates:
labels:
- "type:maintenance"
- "dependencies"
+ - "pr:daveit"
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 558fb4b1a..eb834fd9d 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -28,16 +28,16 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
- uses: github/codeql-action/init@v1
+ uses: github/codeql-action/init@v2
with:
languages: javascript
- name: Autobuild
- uses: github/codeql-action/autobuild@v1
+ uses: github/codeql-action/autobuild@v2
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v1
+ uses: github/codeql-action/analyze@v2
diff --git a/.github/workflows/e2e-pr.yml b/.github/workflows/e2e-pr.yml
index b16920369..b21bf8ce7 100644
--- a/.github/workflows/e2e-pr.yml
+++ b/.github/workflows/e2e-pr.yml
@@ -2,52 +2,61 @@ name: "e2e-pr"
on:
workflow_dispatch:
pull_request:
- types: [ labeled ]
+ types:
+ - labeled
+ - opened
jobs:
e2e-full:
if: ${{ github.event.label.name == 'pr:e2e' }}
- runs-on: ubuntu-latest
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ os:
+ - ubuntu-latest
+ - windows-latest
steps:
- name: Trigger Success
- uses: actions/github-script@v5
+ uses: actions/github-script@v6
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
- owner: context.repo.owner,
- repo: context.repo.repo,
+ owner: "nasa",
+ repo: "openmct",
body: 'Started e2e Run. Follow along: https://github.com/nasa/openmct/actions/runs/' + context.runId
})
- - uses: actions/checkout@v2
- - uses: actions/setup-node@v2
+ - uses: actions/checkout@v3
+ - uses: actions/setup-node@v3
with:
- node-version: '14'
+ node-version: '16'
+ - run: npx playwright@1.23.0 install
+ - run: npx playwright install chrome-beta
- run: npm install
- run: npm run test:e2e:full
- name: Archive test results
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v3
with:
path: test-results
- name: Test success
if: ${{ success() }}
- uses: actions/github-script@v5
+ uses: actions/github-script@v6
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
- owner: context.repo.owner,
- repo: context.repo.repo,
+ owner: "nasa",
+ repo: "openmct",
body: 'Success ✅ ! Build artifacts are here: https://github.com/nasa/openmct/actions/runs/' + context.runId
})
- name: Test failure
if: ${{ failure() }}
- uses: actions/github-script@v5
+ uses: actions/github-script@v6
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
- owner: context.repo.owner,
- repo: context.repo.repo,
+ owner: "nasa",
+ repo: "openmct",
body: 'Failure ❌ ! Build artifacts are here: https://github.com/nasa/openmct/actions/runs/' + context.runId
})
diff --git a/.github/workflows/e2e-visual.yml b/.github/workflows/e2e-visual.yml
index fdc415e92..11c8da3ca 100644
--- a/.github/workflows/e2e-visual.yml
+++ b/.github/workflows/e2e-visual.yml
@@ -4,6 +4,7 @@ on:
pull_request:
types:
- labeled
+ - opened
schedule:
- cron: '28 21 * * 1-5'
@@ -12,10 +13,11 @@ jobs:
if: ${{ github.event.label.name == 'pr:visual' }} || ${{ github.event.workflow_dispatch }} || ${{ github.event.schedule }}
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
- - uses: actions/setup-node@v2
+ - uses: actions/checkout@v3
+ - uses: actions/setup-node@v3
with:
- node-version: '14'
+ node-version: '16'
+ - run: npx playwright@1.23.0 install
- run: npm install
- name: Run the e2e visual tests
run: npm run test:e2e:visual
diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml
index bcde03a7f..4dfd2435b 100644
--- a/.github/workflows/e2e.yml
+++ b/.github/workflows/e2e.yml
@@ -10,12 +10,12 @@ jobs:
e2e:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
with:
ref: ${{ github.event.inputs.version }}
- - uses: actions/setup-node@v2
+ - uses: actions/setup-node@v3
with:
- node-version: '14'
+ node-version: '16'
- run: npm install
- name: Run the e2e tests
run: npm run test:e2e:ci
diff --git a/.github/workflows/lighthouse.yml b/.github/workflows/lighthouse.yml
index 4a67227c0..a2df65344 100644
--- a/.github/workflows/lighthouse.yml
+++ b/.github/workflows/lighthouse.yml
@@ -5,16 +5,94 @@ on:
version:
description: 'Which branch do you want to test?' # Limited to branch for now
required: false
- default: 'master'
+ default: 'master'
+ pull_request:
+ types:
+ - labeled
jobs:
- lighthouse:
+ lighthouse-pr:
+ if: ${{ github.event.label.name == 'pr:lighthouse' }}
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
+ - name: Checkout Master for Baseline
+ uses: actions/checkout@v3
+ with:
+ ref: master #explicitly checkout master for baseline
+ - name: Install Node 16
+ uses: actions/setup-node@v3
+ with:
+ node-version: '16'
+ - name: Cache node modules
+ uses: actions/cache@v2
+ env:
+ cache-name: cache-node-modules
+ with:
+ path: ~/.npm
+ key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package.json') }}
+ restore-keys: |
+ ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package.json') }}
+ - name: npm install with lighthouse cli
+ run: npm install && npm install -g @lhci/cli
+ - name: Run lhci against master to generate baseline and ignore exit codes
+ run: lhci autorun || true
+ - name: Perform clean checkout of PR
+ uses: actions/checkout@v3
+ with:
+ clean: true
+ - name: Install Node version which is compatible with PR
+ uses: actions/setup-node@v3
+ - name: npm install with lighthouse cli
+ run: npm install && npm install -g @lhci/cli
+ - name: Run lhci with PR
+ run: lhci autorun
+ env:
+ LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}
+ lighthouse-nightly:
+ if: ${{ github.event.schedule }}
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - name: Install Node 16
+ uses: actions/setup-node@v3
+ with:
+ node-version: '16'
+ - name: Cache node modules
+ uses: actions/cache@v2
+ env:
+ cache-name: cache-node-modules
+ with:
+ path: ~/.npm
+ key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package.json') }}
+ restore-keys: |
+ ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package.json') }}
+ - name: npm install with lighthouse cli
+ run: npm install && npm install -g @lhci/cli
+ - name: Run lhci against master to generate baseline
+ run: lhci autorun
+ env:
+ LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}
+ lighthouse-dispatch:
+ if: ${{ github.event.workflow_dispatch }}
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
with:
ref: ${{ github.event.inputs.version }}
- - uses: actions/setup-node@v2
+ - name: Install Node 14
+ uses: actions/setup-node@v3
+ with:
+ node-version: '16'
+ - name: Cache node modules
+ uses: actions/cache@v3
+ env:
+ cache-name: cache-node-modules
with:
- node-version: '14'
- - run: npm install && npm install -g @lhci/cli #Don't want to include this in our deps
- - run: lhci autorun \ No newline at end of file
+ path: ~/.npm
+ key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package.json') }}
+ restore-keys: |
+ ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package.json') }}
+ - name: npm install with lighthouse cli
+ run: npm install && npm install -g @lhci/cli
+ - name: Run lhci against master to generate baseline
+ run: lhci autorun
+ \ No newline at end of file
diff --git a/.github/workflows/npm-prerelease.yml b/.github/workflows/npm-prerelease.yml
index 9230bfff1..b92d21790 100644
--- a/.github/workflows/npm-prerelease.yml
+++ b/.github/workflows/npm-prerelease.yml
@@ -11,10 +11,10 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
- - uses: actions/setup-node@v2
+ - uses: actions/checkout@v3
+ - uses: actions/setup-node@v3
with:
- node-version: 14
+ node-version: 16
- run: npm install
- run: npm test
@@ -22,10 +22,10 @@ jobs:
needs: build
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
- - uses: actions/setup-node@v2
+ - uses: actions/checkout@v3
+ - uses: actions/setup-node@v3
with:
- node-version: 14
+ node-version: 16
registry-url: https://registry.npmjs.org/
- run: npm install
- run: npm publish --access public --tag unstable
diff --git a/.github/workflows/pr-platform.yml b/.github/workflows/pr-platform.yml
new file mode 100644
index 000000000..dc4612307
--- /dev/null
+++ b/.github/workflows/pr-platform.yml
@@ -0,0 +1,34 @@
+name: "pr-platform"
+on:
+ workflow_dispatch:
+ pull_request:
+ types: [ labeled ]
+
+jobs:
+ e2e-full:
+ if: ${{ github.event.label.name == 'pr:platform' }}
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ os:
+ - ubuntu-latest
+ - macos-latest
+ - windows-latest
+ node_version:
+ - 14
+ - 16
+ - 18
+ architecture:
+ - x64
+ name: Node ${{ matrix.node_version }} - ${{ matrix.architecture }} on ${{ matrix.os }}
+ steps:
+ - uses: actions/checkout@v3
+ - name: Setup node
+ uses: actions/setup-node@v3
+ with:
+ node-version: ${{ matrix.node_version }}
+ architecture: ${{ matrix.architecture }}
+ - run: npm install
+ - run: npm test
+ - run: npm run lint -- --quiet
diff --git a/.github/workflows/prcop-config.json b/.github/workflows/prcop-config.json
new file mode 100644
index 000000000..5c278b0ec
--- /dev/null
+++ b/.github/workflows/prcop-config.json
@@ -0,0 +1,19 @@
+{
+ "linters": [
+ {
+ "name": "descriptionRegexp",
+ "config": {
+ "regexp": "x] Testing instructions",
+ "errorMessage": ":police_officer: PR Description does not confirm that associated issue(s) contain Testing instructions"
+ }
+ },
+ {
+ "name": "descriptionMinWords",
+ "config": {
+ "minWordsCount": 160,
+ "errorMessage": ":police_officer: Please, be sure to use existing PR template."
+ }
+ }
+ ],
+ "disableWord": "pr:daveit"
+}
diff --git a/.github/workflows/prcop.yml b/.github/workflows/prcop.yml
new file mode 100644
index 000000000..4ee2c5569
--- /dev/null
+++ b/.github/workflows/prcop.yml
@@ -0,0 +1,26 @@
+name: PRCop
+
+on:
+ pull_request:
+ types:
+ - opened
+ - reopened
+ - edited
+ - synchronize
+ - ready_for_review
+ - review_requested
+ - review_request_removed
+ pull_request_review_comment:
+ types:
+ - created
+
+jobs:
+ prcop:
+ runs-on: ubuntu-latest
+ name: Template Check
+ steps:
+ - name: Linting Pull Request
+ uses: makaroni4/prcop@v1.0.35
+ with:
+ config-file: ".github/workflows/prcop-config.json"
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.gitignore b/.gitignore
index 7c608343c..11481c4c4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,8 +15,6 @@
*.idea
*.iml
-# External dependencies
-
# Build output
target
dist
@@ -24,30 +22,24 @@ dist
# Mac OS X Finder
.DS_Store
-# Closed source libraries
-closed-lib
-
# Node, Bower dependencies
node_modules
bower_components
-# Protractor logs
-protractor/logs
-
# npm-debug log
npm-debug.log
# karma reports
report.*.json
-# Lighthouse reports
-.lighthouseci
-
# e2e test artifacts
test-results
-allure-results
+html-test-results
-package-lock.json
-
-#codecov artifacts
+# codecov artifacts
+.nyc_output
+coverage
codecov
+
+# :(
+package-lock.json
diff --git a/.npmignore b/.npmignore
index 13b8b511e..4dc1a0018 100644
--- a/.npmignore
+++ b/.npmignore
@@ -1,44 +1,27 @@
-*.scssc
-*.zip
-*.gzip
-*.tgz
-*.DS_Store
-
-*.sass-cache
-*COMPILE.css
-
-# Intellij project configuration files
-*.idea
-*.iml
-
-# External dependencies
-
-# Build output
-target
-
-# Mac OS X Finder
-.DS_Store
-
-# Closed source libraries
-closed-lib
-
-# Node, Bower dependencies
-node_modules
-bower_components
-
-Procfile
-
-# Protractor logs
-protractor/logs
-
-# npm-debug log
-npm-debug.log
-
-# Infra and tests
-.circleci
-.github
-e2e
-codecov.yml
-lighthouserc.yml
-*.Spec.js
-karma.conf.js
+# Ignore everything first (will not ignore special files like LICENSE.md,
+# README.md, and package.json)...
+/**/*
+
+# ...but include these folders...
+!/dist/**/*
+!/src/**/*
+
+# We might be able to remove this if it is not imported by any project directly.
+# https://github.com/nasa/openmct/issues/4992
+!/example/**/*
+
+# We will remove this in https://github.com/nasa/openmct/issues/4922
+!/app.js
+
+# ...except for these files in the above folders.
+/src/**/*Spec.js
+/src/**/test/
+# TODO move test utils into test/ folders
+/src/utils/testing.js
+
+# Also include these special top-level files.
+!copyright-notice.js
+!copyright-notice.html
+!index.html
+!openmct.js
+!SECURITY.md \ No newline at end of file
diff --git a/.npmrc b/.npmrc
index 3b8898187..d747c7f48 100644
--- a/.npmrc
+++ b/.npmrc
@@ -1,6 +1,4 @@
loglevel=warn
-# Temporary: istanbul-instrumenter-loader is working with webpack 5, but states
-# webpack 4 being the latest version it supports, so this legacy-peer-deps
-# allows us to install it anyway.
-legacy-peer-deps=true \ No newline at end of file
+#Prevent folks from ignoring an important error when building from source
+engine-strict=true \ No newline at end of file
diff --git a/API.md b/API.md
index c5f27843c..a56a0de18 100644
--- a/API.md
+++ b/API.md
@@ -52,6 +52,8 @@
- [The URL Status Indicator](#the-url-status-indicator)
- [Creating a Simple Indicator](#creating-a-simple-indicator)
- [Custom Indicators](#custom-indicators)
+ - [Priority API](#priority-api)
+ - [Priority Types](#priority-types)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
@@ -247,16 +249,24 @@ To do so, use the `addRoot` method of the object API.
eg.
```javascript
openmct.objects.addRoot({
- namespace: "example.namespace",
- key: "my-key"
- });
+ namespace: "example.namespace",
+ key: "my-key"
+},
+openmct.priority.HIGH);
```
-The `addRoot` function takes a single [object identifier](#domain-objects-and-identifiers)
-as an argument.
+The `addRoot` function takes a two arguments, the first can be an [object identifier](#domain-objects-and-identifiers) for a root level object, or an array of identifiers for root
+level objects, or a function that returns a promise for an identifier or an array of root level objects, the second is a [priority](#priority-api) or numeric value.
+
+When using the `getAll` method of the object API, they will be returned in order of priority.
+
+eg.
+```javascript
+openmct.objects.addRoot(identifier, openmct.priority.LOW); // low = -1000, will appear last in composition or tree
+openmct.objects.addRoot(otherIdentifier, openmct.priority.HIGH); // high = 1000, will appear first in composition or tree
+```
-Root objects are loaded just like any other objects, i.e. via an object
-provider.
+Root objects are loaded just like any other objects, i.e. via an object provider.
## Object Providers
@@ -1051,3 +1061,25 @@ A completely custom indicator can be added by simply providing a DOM element to
element: domNode
});
```
+
+## Priority API
+
+Open MCT provides some built-in priority values that can be used in the application for view providers, indicators, root object order, and more.
+
+### Priority Types
+
+Currently, the Open MCT Priority API provides (type: numeric value):
+- HIGH: 1000
+- Default: 0
+- LOW: -1000
+
+View provider Example:
+
+``` javascript
+ class ViewProvider {
+ ...
+ priority() {
+ return openmct.priority.HIGH;
+ }
+}
+``` \ No newline at end of file
diff --git a/LICENSE.md b/LICENSE.md
index 04d36576a..2e87024fd 100644
--- a/LICENSE.md
+++ b/LICENSE.md
@@ -1,6 +1,6 @@
# Open MCT License
-Open MCT, Copyright (c) 2014-2021, United States Government as represented by the Administrator of the National Aeronautics and Space Administration. All rights reserved.
+Open MCT, Copyright (c) 2014-2022, United States Government as represented by the Administrator of the National Aeronautics and Space Administration. All rights reserved.
Open MCT is licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
diff --git a/README.md b/README.md
index dbc04ecf7..832167476 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# Open MCT [![license](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](http://www.apache.org/licenses/LICENSE-2.0) [![Language grade: JavaScript](https://img.shields.io/lgtm/grade/javascript/g/nasa/openmct.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/nasa/openmct/context:javascript) [![codecov](https://codecov.io/gh/nasa/openmct/branch/master/graph/badge.svg?token=7DQIipp3ej)](https://codecov.io/gh/nasa/openmct) [![This project is using Percy.io for visual regression testing.](https://percy.io/static/images/percy-badge.svg)](https://percy.io/b2e34b17/openmct)
+# Open MCT [![license](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](http://www.apache.org/licenses/LICENSE-2.0) [![Language grade: JavaScript](https://img.shields.io/lgtm/grade/javascript/g/nasa/openmct.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/nasa/openmct/context:javascript) [![codecov](https://codecov.io/gh/nasa/openmct/branch/master/graph/badge.svg?token=7DQIipp3ej)](https://codecov.io/gh/nasa/openmct) [![This project is using Percy.io for visual regression testing.](https://percy.io/static/images/percy-badge.svg)](https://percy.io/b2e34b17/openmct) [![npm version](https://img.shields.io/npm/v/openmct.svg)](https://www.npmjs.com/package/openmct)
Open MCT (Open Mission Control Technologies) is a next-generation mission control framework for visualization of data on desktop and mobile devices. It is developed at NASA's Ames Research Center, and is being used by NASA for data analysis of spacecraft missions, as well as planning and operation of experimental rover systems. As a generalizable and open source framework, Open MCT could be used as the basis for building applications for planning, operation, and analysis of any systems producing telemetry data.
@@ -11,6 +11,22 @@ Once you've created something amazing with Open MCT, showcase your work in our G
Try Open MCT now with our [live demo](https://openmct-demo.herokuapp.com/).
![Demo](https://nasa.github.io/openmct/static/res/images/Open-MCT.Browse.Layout.Mars-Weather-1.jpg)
+## Open MCT v2.0.0
+Support for our legacy bundle-based API, and the libraries that it was built on (like Angular 1.x), have now been removed entirely from this repository.
+
+For now if you have an Open MCT application that makes use of the legacy API, [a plugin](https://github.com/nasa/openmct-legacy-plugin) is provided that bootstraps the legacy bundling mechanism and API. This plugin will not be maintained over the long term however, and the legacy support plugin will not be tested for compatibility with future versions of Open MCT. It is provided for convenience only.
+
+### How do I know if I am using legacy API?
+You might still be using legacy API if your source code
+
+* Contains files named bundle.js, or bundle.json,
+* Makes calls to `openmct.$injector()`, or `openmct.$angular`,
+* Makes calls to `openmct.legacyRegistry`, `openmct.legacyExtension`, or `openmct.legacyBundle`.
+
+
+### What should I do if I am using legacy API?
+Please refer to [the modern Open MCT API](https://nasa.github.io/openmct/documentation/). Post any questions to the [Discussions section](https://github.com/nasa/openmct/discussions) of the Open MCT GitHub repository.
+
## Building and Running Open MCT Locally
Building and running Open MCT in your local dev environment is very easy. Be sure you have [Git](https://git-scm.com/downloads) and [Node.js](https://nodejs.org/) installed, then follow the directions below. Need additional information? Check out the [Getting Started](https://nasa.github.io/openmct/getting-started/) page on our website.
@@ -20,7 +36,7 @@ Building and running Open MCT in your local dev environment is very easy. Be sur
`git clone https://github.com/nasa/openmct.git`
-2. Install development dependencies
+2. Install development dependencies. Note: Check the package.json engine for our tested and supported node versions.
`npm install`
@@ -30,11 +46,6 @@ Building and running Open MCT in your local dev environment is very easy. Be sur
Open MCT is now running, and can be accessed by pointing a web browser at [http://localhost:8080/](http://localhost:8080/)
-## Open MCT v1.0.0
-This represents a major overhaul of Open MCT with significant changes under the hood. We aim to maintain backward compatibility but if you do find compatibility issues, please let us know by filing an issue in this repository. If you are having major issues with v1.0.0 please check-out the v0.14.0 tag until we can resolve them for you.
-
-If you are migrating an application built with Open MCT as a dependency to v1.0.0 from an earlier version, please refer to [our migration guide](https://nasa.github.io/openmct/documentation/migration-guide).
-
## Documentation
Documentation is available on the [Open MCT website](https://nasa.github.io/openmct/documentation/).
@@ -54,6 +65,12 @@ Open MCT is built using [`npm`](http://npmjs.com/) and [`webpack`](https://webpa
See our documentation for a guide on [building Applications with Open MCT](https://github.com/nasa/openmct/blob/master/API.md#starting-an-open-mct-application).
+## Compatibility
+
+This is a fast moving project and we do our best to test and support the widest possible range of browsers, operating systems, and nodejs APIs. We have a published list of support available in our package.json's `browserslist` key.
+
+If you encounter an issue with a particular browser, OS, or nodejs API, please file a [GitHub issue](https://github.com/nasa/openmct/issues/new/choose)
+
## Plugins
Open MCT can be extended via plugins that make calls to the Open MCT API. A plugin is a group
diff --git a/app.js b/app.js
index 76f9f66ff..baa951e12 100644
--- a/app.js
+++ b/app.js
@@ -1,4 +1,4 @@
-/*global require,process,console*/
+/*global process*/
/**
* Usage:
@@ -12,6 +12,7 @@ const express = require('express');
const app = express();
const fs = require('fs');
const request = require('request');
+const __DEV__ = process.env.NODE_ENV === 'development';
// Defaults
options.port = options.port || options.p || 8080;
@@ -49,14 +50,18 @@ class WatchRunPlugin {
}
const webpack = require('webpack');
-const webpackConfig = require('./webpack.dev.js');
-webpackConfig.plugins.push(new webpack.HotModuleReplacementPlugin());
-webpackConfig.plugins.push(new WatchRunPlugin());
-
-webpackConfig.entry.openmct = [
- 'webpack-hot-middleware/client?reload=true',
- webpackConfig.entry.openmct
-];
+let webpackConfig;
+if (__DEV__) {
+ webpackConfig = require('./webpack.dev');
+ webpackConfig.plugins.push(new webpack.HotModuleReplacementPlugin());
+ webpackConfig.entry.openmct = [
+ 'webpack-hot-middleware/client?reload=true',
+ webpackConfig.entry.openmct
+ ];
+ webpackConfig.plugins.push(new WatchRunPlugin());
+} else {
+ webpackConfig = require('./webpack.coverage');
+}
const compiler = webpack(webpackConfig);
@@ -64,14 +69,16 @@ app.use(require('webpack-dev-middleware')(
compiler,
{
publicPath: '/dist',
- logLevel: 'warn'
+ stats: 'errors-warnings'
}
));
-app.use(require('webpack-hot-middleware')(
- compiler,
- {}
-));
+if (__DEV__) {
+ app.use(require('webpack-hot-middleware')(
+ compiler,
+ {}
+ ));
+}
// Expose index.html for development users.
app.get('/', function (req, res) {
@@ -79,6 +86,6 @@ app.get('/', function (req, res) {
});
// Finally, open the HTTP server and log the instance to the console
-app.listen(options.port, options.host, function() {
+app.listen(options.port, options.host, function () {
console.log('Open MCT application running at %s:%s', options.host, options.port);
});
diff --git a/babel.coverage.js b/babel.coverage.js
new file mode 100644
index 000000000..6a752a9e5
--- /dev/null
+++ b/babel.coverage.js
@@ -0,0 +1,9 @@
+// This is a Babel config that webpack.coverage.js uses in order to instrument
+// code with coverage instrumentation.
+const babelConfig = {
+ plugins: [['babel-plugin-istanbul', {
+ extension: ['.js', '.vue']
+ }]]
+};
+
+module.exports = babelConfig;
diff --git a/build-docs.sh b/build-docs.sh
index 6627f3d17..d30af4f10 100755
--- a/build-docs.sh
+++ b/build-docs.sh
@@ -1,7 +1,7 @@
#!/bin/bash
#*****************************************************************************
-#* Open MCT, Copyright (c) 2014-2021, United States Government
+#* Open MCT, Copyright (c) 2014-2022, United States Government
#* as represented by the Administrator of the National Aeronautics and Space
#* Administration. All rights reserved.
#*
diff --git a/codecov.yml b/codecov.yml
index 245a63ec7..d69c35bbd 100644
--- a/codecov.yml
+++ b/codecov.yml
@@ -13,19 +13,16 @@ coverage:
round: down
range: "66...100"
-ignore:
- - "**/*Spec.js"
- - "e2e"
-
-parsers:
- gcov:
- branch_detection:
- conditional: true
- loop: true
- method: false
- macro: false
+flags:
+ unit:
+ carryforward: true
+ e2e-ci:
+ carryforward: true
+ e2e-full:
+ carryforward: true
comment:
layout: "reach,diff,flags,files,footer"
behavior: default
require_changes: false
+ show_carryforward_flags: true \ No newline at end of file
diff --git a/copyright-notice.html b/copyright-notice.html
index 4a5b0519e..135675cd0 100644
--- a/copyright-notice.html
+++ b/copyright-notice.html
@@ -1,5 +1,5 @@
<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
+ Open MCT, Copyright (c) 2014-2022, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
diff --git a/copyright-notice.js b/copyright-notice.js
index ab665bbbf..5b39517c4 100644
--- a/copyright-notice.js
+++ b/copyright-notice.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/docs/gendocs.js b/docs/gendocs.js
index 207a3e497..a5f3188e9 100644
--- a/docs/gendocs.js
+++ b/docs/gendocs.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/docs/src/architecture/framework.md b/docs/src/architecture/framework.md
deleted file mode 100644
index 78adb9815..000000000
--- a/docs/src/architecture/framework.md
+++ /dev/null
@@ -1,232 +0,0 @@
-# Overview
-
-The framework layer's most basic responsibility is allowing individual
-software components to communicate. The software components it recognizes
-are:
-
-* _Extensions_: Individual units of functionality that can be added to
- or removed from Open MCT. _Extension categories_ distinguish what
- type of functionality is being added/removed.
-* _Bundles_: A grouping of related extensions
- (named after an analogous concept from [OSGi](http://www.osgi.org/))
- that may be added or removed as a group.
-
-The framework layer operates by taking a set of active bundles, and
-exposing extensions to one another as-needed, using
-[dependency injection](https://en.wikipedia.org/wiki/Dependency_injection).
-Extensions are responsible for declaring their dependencies in a
-manner which the framework layer can understand.
-
-```nomnoml
-#direction: down
-[Open MCT|
- [Dependency injection framework]-->[Platform bundle #1]
- [Dependency injection framework]-->[Platform bundle #2]
- [Dependency injection framework]-->[Plugin bundle #1]
- [Dependency injection framework]-->[Plugin bundle #2]
- [Platform bundle #1|[Extensions]]
- [Platform bundle #2|[Extensions]]
- [Plugin bundle #1|[Extensions]]
- [Plugin bundle #2|[Extensions]]
- [Platform bundle #1]<->[Platform bundle #2]
- [Plugin bundle #1]<->[Platform bundle #2]
- [Plugin bundle #1]<->[Plugin bundle #2]
-]
-```
-
-The "dependency injection framework" in this case is
-[AngularJS](https://angularjs.org/). Open MCT's framework layer
-is really just a thin wrapper over Angular that recognizes the
-concepts of bundles and extensions (as declared in JSON files) and
-registering extensions with Angular. It additionally acts as a
-mediator between Angular and [RequireJS](http://requirejs.org/),
-which is used to load JavaScript sources which implement
-extensions.
-
-```nomnoml
-[Framework layer|
- [AngularJS]<-[Framework Component]
- [RequireJS]<-[Framework Component]
- [Framework Component]1o-*[Bundles]
-]
-```
-
-It is worth noting that _no other components_ are "aware" of the
-framework component directly; Angular and Require are _used by_ the
-framework components, and extensions in various bundles will have
-their dependencies satisfied by Angular as a consequence of registration
-activities which were performed by the framework component.
-
-
-## Application Initialization
-
-The framework component initializes an Open MCT application following
-a simple sequence of steps.
-
-```nomnoml
-[<start> Start]->[<state> Load bundles.json]
-[Load bundles.json]->[<state> Load bundle.json files]
-[Load bundle.json files]->[<state> Resolve implementations]
-[Resolve implementations]->[<state> Register with Angular]
-[Register with Angular]->[<state> Bootstrap application]
-[Bootstrap application]->[<end> End]
-```
-
-1. __Loading bundles.json.__ A file named `bundles.json` is loaded to determine
- which bundles to load. Bundles are given in this file as relative paths
- which point to bundle directories.
-2. __Load bundle.json files.__ Individual bundle definitions are loaded; a
- `bundle.json` file is expected in each bundle directory.
-2. __Resolving implementations.__ Any scripts which provide implementations for
- extensions exposed by bundles are loaded, using RequireJS.
-3. __Register with Angular.__ Resolved extensions are registered with Angular,
- such that they can be used by the application at run-time. This stage
- includes both registration of Angular built-ins (directives, controllers,
- routes, constants, and services) as well as registration of non-Angular
- extensions.
-4. __Bootstrap application.__ Once all extensions have been registered,
- the Angular application
- [is bootstrapped](https://docs.angularjs.org/guide/bootstrap).
-
-## Architectural Paradigm
-
-```nomnoml
-[Extension]
-[Extension]o->[Dependency #1]
-[Extension]o->[Dependency #2]
-[Extension]o->[Dependency #3]
-```
-
-Open MCT's architecture relies on a simple premise: Individual units
-(extensions) only have access to the dependencies they declare that they
-need, and they acquire references to these dependencies via dependency
-injection. This has several desirable traits:
-
-* Programming to an interface is enforced. Any given dependency can be
- swapped out for something which exposes an equivalent interface. This
- improves flexibility against refactoring, simplifies testing, and
- provides a common mechanism for extension and reconfiguration.
-* The dependencies of a unit must be explicitly defined. This means that
- it can be easily determined what a given unit's role is within the
- larger system, in terms of what other components it will interact with.
- It also helps to enforce good separation of concerns: When a set of
- declared dependencies becomes long it is obvious, and this is usually
- a sign that a given unit is involved in too many concerns and should
- be refactored into smaller pieces.
-* Individual units do not need to be aware of the framework; they need
- only be aware of the interfaces to the components they specifically
- use. This avoids introducing a ubiquitous dependency upon the framework
- layer itself; it is plausible to modify or replace the framework
- without making changes to individual software components which run upon
- the framework.
-
-A drawback to this approach is that it makes it difficult to define
-"the architecture" of Open MCT, in terms of describing the specific
-units that interact at run-time. The run-time architecture is determined
-by the framework as the consequence of wiring together dependencies.
-As such, the specific architecture of any given application built on
-Open MCT can look very different.
-
-Keeping that in mind, there are a few useful patterns supported by the
-framework that are useful to keep in mind.
-
-The specific service infrastructure provided by the platform is described
-in the [Platform Architecture](platform.md).
-
-## Extension Categories
-
-One of the capabilities that the framework component layers on top of
-AngularJS is support for many-to-one dependencies. That is, a specific
-extension may declare a dependency to _all extensions of a specific
-category_, instead of being limited to declaring specific dependencies.
-
-```nomnoml
-#direction: right
-[Specific Extension] 1 o-> * [Extension of Some Category]
-```
-
-This is useful for introducing specific extension points to an application.
-Some unit of software will depend upon all extensions of a given category
-and integrate their behavior into the system in some fashion; plugin authors
-can then add new extensions of that category to augment existing behaviors.
-
-Some developers may be familiar with the use of registries to achieve
-similar characteristics. This approach is similar, except that the registry
-is effectively implicit whenever a new extension category is used or
-depended-upon. This has some advantages over a more straightforward
-registry-based approach:
-
-* These many-to-one relationships are expressed as dependencies; an
- extension category is registered as having dependencies on all individual
- extensions of this category. This avoids ordering issues that may occur
- with more conventional registries, which may be observed before all
- dependencies are resolved.
-* The need for service registries of specific types is removed, reducing
- the number of interfaces to manage within the system. Groups of
- extensions are provided as arrays.
-
-## Composite Services
-
-Composite services (registered via extension category `components`) are
-a pattern supported by the framework. These allow service instances to
-be built from multiple components at run-time; support for this pattern
-allows additional bundles to introduce or modify behavior associated
-with these services without modifying or replacing original service
-instances.
-
-```nomnoml
-#direction: down
-[<abstract> FooService]
-[FooDecorator #1]--:>[FooService]
-[FooDecorator #n]--:>[FooService]
-[FooAggregator]--:>[FooService]
-[FooProvider #1]--:>[FooService]
-[FooProvider #n]--:>[FooService]
-
-[FooDecorator #1]o->[<state> ...decorators...]
-[...decorators...]o->[FooDecorator #n]
-[FooDecorator #n]o->[FooAggregator]
-[FooAggregator]o->[FooProvider #1]
-[FooAggregator]o->[<state> ...providers...]
-[FooAggregator]o->[FooProvider #n]
-
-[FooDecorator #1]--[<note> Exposed as fooService]
-```
-
-In this pattern, components all implement an interface which is
-standardized for that service. Components additionally declare
-that they belong to one of three types:
-
-* __Providers.__ A provider actually implements the behavior
- (satisfies the contract) for that kind of service. For instance,
- if a service is responsible for looking up documents by an identifier,
- one provider may do so by querying a database, while another may
- do so by reading a static JSON document. From the outside, either
- provider would look the same (they expose the same interface) and
- they could be swapped out easily.
-* __Aggregator.__ An aggregator takes many providers and makes them
- behave as one. Again, this implements the same interface as an
- individual provider, so users of the service do not need to be
- concerned about the difference between consulting many providers
- and consulting one. Continuing with the example of a service that
- looks up documents by identifiers, an aggregator here might consult
- all providers, and return any document is found (perhaps picking one
- over the other or merging documents if there are multiple matches.)
-* __Decorators.__ A decorator exposes the same interface as other
- components, but instead of fully implementing the behavior associated
- with that kind of service, it only acts as an intermediary, delegating
- the actual behavior to a different component. Decorators may transform
- inputs or outputs, or initiate some side effects associated with a
- service. This is useful if certain common behavior associated with a
- service (caching, for instance) may be useful across many different
- implementations of that same service.
-
-The framework will register extensions in this category such that an
-aggregator will depend on all of its providers, and decorators will
-depend upon on one another in a chain. The result of this compositing step
-(the last decorator, if any; otherwise the aggregator, if any;
-otherwise a single provider) will be exposed as a single service that
-other extensions can acquire through dependency injection. Because all
-components of the same type of service expose the same interface, users
-of that service do not need to be aware that they are talking to an
-aggregator or a provider, for instance.
diff --git a/docs/src/architecture/index.md b/docs/src/architecture/index.md
deleted file mode 100644
index 6f2fbcfce..000000000
--- a/docs/src/architecture/index.md
+++ /dev/null
@@ -1,76 +0,0 @@
-# Introduction
-
-The purpose of this document is to familiarize developers with the
-overall architecture of Open MCT.
-
-The target audience includes:
-
-* _Platform maintainers_: Individuals involved in developing,
- extending, and maintaining capabilities of the platform.
-* _Integration developers_: Individuals tasked with integrated
- Open MCT into a larger system, who need to understand
- its inner workings sufficiently to complete this integration.
-
-As the focus of this document is on architecture, whenever possible
-implementation details (such as relevant API or JSON syntax) have been
-omitted. These details may be found in the developer guide.
-
-# Overview
-
-Open MCT is client software: It runs in a web browser and
-provides a user interface, while communicating with various
-server-side resources through browser APIs.
-
-```nomnoml
-#direction: right
-[Client|[Browser|[Open MCT]->[Browser APIs]]]
-[Server|[Web services]]
-[Client]<->[Server]
-```
-
-While Open MCT can be configured to run as a standalone client,
-this is rarely very useful. Instead, it is intended to be used as a
-display and interaction layer for information obtained from a
-variety of back-end services. Doing so requires authoring or utilizing
-adapter plugins which allow Open MCT to interact with these services.
-
-Typically, the pattern here is to provide a known interface that
-Open MCT can utilize, and implement it such that it interacts with
-whatever back-end provides the relevant information.
-Examples of back-ends that can be utilized in this fashion include
-databases for the persistence of user-created objects, or sources of
-telemetry data.
-
-## Software Architecture
-
-The simplest overview of Open MCT is to look at it as a "layered"
-architecture, where each layer more clearly specifies the behavior
-of the software.
-
-```nomnoml
-#direction: down
-[Open MCT|
- [Platform]<->[Application]
- [Framework]->[Application]
- [Framework]->[Platform]
-]
-```
-
-These layers are:
-
-* [_Framework_](framework.md): The framework layer is responsible for
- managing the interactions between application components. It has no
- application-specific knowledge; at this layer, we have only
- established an abstraction by which different software components
- may communicate and/or interact.
-* [_Platform_](platform.md): The platform layer defines the general look,
- feel, and behavior of Open MCT. This includes user-facing components like
- Browse mode and Edit mode, as well as underlying elements of the
- information model and the general service infrastructure.
-* _Application_: The application layer defines specific features of
- an application built on Open MCT. This includes adapters to
- specific back-ends, new types of things for users to create, and
- new ways of visualizing objects within the system. This layer
- typically consists of a mix of custom plug-ins to Open MCT,
- as well as optional features (such as Plot view) included alongside
- the platform.
diff --git a/docs/src/architecture/platform.md b/docs/src/architecture/platform.md
deleted file mode 100644
index 48ceebb9e..000000000
--- a/docs/src/architecture/platform.md
+++ /dev/null
@@ -1,726 +0,0 @@
-# Overview
-
-The Open MCT platform utilizes the [framework layer](framework.md)
-to provide an extensible baseline for applications which includes:
-
-* A common user interface (and user interface paradigm) for dealing with
- domain objects of various sorts.
-* A variety of extension points for introducing new functionality
- of various kinds within the context of the common user interface.
-* A service infrastructure to support building additional components.
-
-## Platform Architecture
-
-While the framework provides a more general architectural paradigm for
-building application, the platform adds more specificity by defining
-additional extension types and allowing for integration with back end
-components.
-
-The run-time architecture of an Open MCT application can be categorized
-into certain high-level tiers:
-
-```nomnoml
-[DOM]->[<state> AngularJS]
-[AngularJS]->[Presentation Layer]
-[Presentation Layer]->[Information Model]
-[Presentation Layer]->[Service Infrastructure]
-[Information Model]->[Service Infrastructure]
-[Service Infrastructure]->[<state> Browser APIs]
-[Browser APIs]->[Back-end]
-```
-
-Applications built using Open MCT may add or configure functionality
-in __any of these tiers__.
-
-* _DOM_: The rendered HTML document, composed from HTML templates which
- have been processed by AngularJS and will be updated by AngularJS
- to reflect changes from the presentation layer. User interactions
- are initiated from here and invoke behavior in the presentation layer. HTML 
- templates are written in Angular’s template syntax; see the [Angular documentation on templates](https://docs.angularjs.org/guide/templates)​. 
- These describe the page as actually seen by the user. Conceptually, 
- stylesheets (controlling the look-and-feel of the rendered templates) belong 
- in this grouping as well. 
-* [_Presentation layer_](#presentation-layer): The presentation layer
- is responsible for updating (and providing information to update)
- the displayed state of the application. The presentation layer consists
- primarily of _controllers_ and _directives_. The presentation layer is
- concerned with inspecting the information model and preparing it for
- display.
-* [_Information model_](#information-model): ​Provides a common (within Open MCT 
- Web) set of interfaces for dealing with “things” ­ domain objects ­ within the 
- system. User-facing concerns in a Open MCT Web application are expressed as 
- domain objects; examples include folders (used to organize other domain 
- objects), layouts (used to build displays), or telemetry points (used as 
- handles for streams of remote measurements.) These domain objects expose a 
- common set of interfaces to allow reusable user interfaces to be built in the 
- presentation and template tiers; the specifics of these behaviors are then 
- mapped to interactions with underlying services. 
-* [_Service infrastructure_](#service-infrastructure): The service
- infrastructure is responsible for providing the underlying general
- functionality needed to support the information model. This includes
- exposing underlying sets of extensions and mediating with the
- back-end.
-* _Back-end_: The back-end is out of the scope of Open MCT, except
- for the interfaces which are utilized by adapters participating in the
- service infrastructure. Includes the underlying persistence stores, telemetry 
- streams, and so forth which the Open MCT Web client is being used to interact 
- with.
-
-## Application Start-up
-
-Once the
-[application has been initialized](Framework.md#application-initialization)
-Open MCT primarily operates in an event-driven paradigm; various
-events (mouse clicks, timers firing, receiving responses to XHRs) trigger
-the invocation of functions, typically in the presentation layer for
-user actions or in the service infrastructure for server responses.
-
-The "main point of entry" into an initialized Open MCT application
-is effectively the
-[route](https://docs.angularjs.org/api/ngRoute/service/$route#example)
-which is associated with the URL used to access Open MCT (or a
-default route.) This route will be associated with a template which
-will be displayed; this template will include references to directives
-and controllers which will be interpreted by Angular and used to
-initialize the state of the display in a manner which is backed by
-both the information model and the service infrastructure.
-
-```nomnoml
-[<start> Start]->[<state> page load]
-[page load]->[<state> route selection]
-[route selection]->[<state> compile, display template]
-[compile, display template]->[Template]
-[Template]->[<state> use Controllers]
-[Template]->[<state> use Directives]
-[use Controllers]->[Controllers]
-[use Directives]->[Directives]
-[Controllers]->[<state> consult information model]
-[consult information model]->[<state> expose data]
-[expose data]->[Angular]
-[Angular]->[<state> update display]
-[Directives]->[<state> add event listeners]
-[Directives]->[<state> update display]
-[add event listeners]->[<end> End]
-[update display]->[<end> End]
-```
-
-
-# Presentation Layer
-
-The presentation layer of Open MCT is responsible for providing
-information to display within templates, and for handling interactions
-which are initiated from templated DOM elements. AngularJS acts as
-an intermediary between the web page as the user sees it, and the
-presentation layer implemented as Open MCT extensions.
-
-```nomnoml
-[Presentation Layer|
- [Angular built-ins|
- [routes]
- [controllers]
- [directives]
- [templates]
- ]
- [Domain object representation|
- [views]
- [representations]
- [representers]
- [gestures]
- ]
-]
-```
-
-## Angular built-ins
-
-Several extension categories in the presentation layer map directly
-to primitives from AngularJS:
-
-* [_Controllers_](https://docs.angularjs.org/guide/controller) provide
- data to templates, and expose functionality that can be called from
- templates.
-* [_Directives_](https://docs.angularjs.org/guide/directive) effectively
- extend HTML to provide custom behavior associated with specific
- attributes and tags.
-* [_Routes_](https://docs.angularjs.org/api/ngRoute/service/$route#example)
- are used to associate specific URLs (including the fragment identifier)
- with specific application states. (In Open MCT, these are used to
- describe the mode of usage - e.g. browse or edit - as well as to
- identify the object being used.)
-* [_Templates_](https://docs.angularjs.org/guide/templates) are partial
- HTML documents that will be rendered and kept up-to-date by AngularJS.
- Open MCT introduces a custom `mct-include` directive which acts
- as a wrapper around `ng-include` to allow templates to be referred
- to by symbolic names.
-
-## Domain object representation
-
-The remaining extension categories in the presentation layer are specific
-to displaying domain objects.
-
-* _Representations_ are templates that will be used to display
- domain objects in specific ways (e.g. "as a tree node.")
-* _Views_ are representations which are exposed to the user as options
- for displaying domain objects.
-* _Representers_ are extensions which modify or augment the process
- of representing domain objects generally (e.g. by attaching
- gestures to them.)
-* _Gestures_ provide associations between specific user actions
- (expressed as DOM events) and resulting behavior upon domain objects
- (typically expressed as members of the `actions` extension category)
- that can be reused across domain objects. For instance, `drag` and
- `drop` are both gestures associated with using drag-and-drop to
- modify the composition of domain objects by interacting with their
- representations.
-
-# Information Model
-
-```nomnoml
-#direction: right
-[Information Model|
- [DomainObject|
- getId() : string
- getModel() : object
- getCapability(key : string) : Capability
- hasCapability(key : string) : boolean
- useCapability(key : string, args...) : *
- ]
- [DomainObject] 1 +- 1 [Model]
- [DomainObject] 1 o- * [Capability]
-]
-```
-
-Domain objects are the most fundamental component of Open MCT's
-information model. A domain object is some distinct thing relevant to a
-user's work flow, such as a telemetry channel, display, or similar.
-Open MCT is a tool for viewing, browsing, manipulating, and otherwise
-interacting with a graph of domain objects.
-
-A domain object should be conceived of as the union of the following:
-
-* _Identifier_: A machine-readable string that uniquely identifies the
- domain object within this application instance.
-* _Model_: The persistent state of the domain object. A domain object's
- model is a JavaScript object that can be losslessly converted to JSON.
-* _Capabilities_: Dynamic behavior associated with the domain object.
- Capabilities are JavaScript objects which provide additional methods
- for interacting with the domain objects which expose those capabilities.
- Not all domain objects expose all capabilities. The interface exposed
- by any given capability will depend on its type (as identified
- by the `key` argument.) For instance, a `persistence` capability
- has a different interface from a `telemetry` capability. Using
- capabilities requires some prior knowledge of their interface.
-
-## Capabilities and Services
-
-```nomnoml
-#direction: right
-[DomainObject]o-[FooCapability]
-[FooCapability]o-[FooService]
-[FooService]o-[foos]
-```
-
-At run-time, the user is primarily concerned with interacting with
-domain objects. These interactions are ultimately supported via back-end
-services, but to allow customization per-object, these are often mediated
-by capabilities.
-
-A common pattern that emerges in the Open MCT Platform is as follows:
-
-* A `DomainObject` has some particular behavior that will be supported
- by a service.
-* A `Capability` of that domain object will define that behavior,
- _for that domain object_, supported by a service.
-* A `Service` utilized by that capability will perform the actual behavior.
-* An extension category will be utilized by that capability to determine
- the set of possible behaviors.
-
-Concrete examples of capabilities which follow this pattern
-(or a subset of this pattern) include:
-
-```nomnoml
-#direction: right
-[DomainObject]1 o- *[Capability]
-[Capability]<:--[TypeCapability]
-[Capability]<:--[ActionCapability]
-[Capability]<:--[PersistenceCapability]
-[Capability]<:--[TelemetryCapability]
-[TypeCapability]o-[TypeService]
-[TypeService]o-[types]
-[ActionCapability]o-[ActionService]
-[ActionService]o-[actions]
-[PersistenceCapability]o-[PersistenceService]
-[TelemetryCapability]o-[TelemetryService]
-```
-
-# Service Infrastructure
-
-Most services exposed by the Open MCT platform follow the
-[composite services](Framework.md#composite-services) to permit
-a higher degree of flexibility in how a service can be modified
-or customized for specific applications.
-
-To simplify usage for plugin developers, the platform also usually
-includes a provider implementation for these service type that consumes
-some extension category. For instance, an `ActionService` provider is
-included which depends upon extension category `actions`, and exposes
-all actions declared as such to the system. As such, plugin developers
-can simply implement the new actions they wish to be made available without
-worrying about the details of composite services or implementing a new
-`ActionService` provider; however, the ability to implement a new provider
-remains useful when the expressive power of individual extensions is
-insufficient.
-
-```nomnoml
-[ Service Infrastructure |
- [ObjectService]->[ModelService]
- [ModelService]->[PersistenceService]
- [ObjectService]->[CapabilityService]
- [CapabilityService]->[capabilities]
- [capabilities]->[TelemetryService]
- [capabilities]->[PersistenceService]
- [capabilities]->[TypeService]
- [capabilities]->[ActionService]
- [capabilities]->[ViewService]
- [PersistenceService]->[<database> Document store]
- [TelemetryService]->[<database> Telemetry source]
- [ActionService]->[actions]
- [ActionService]->[PolicyService]
- [ViewService]->[PolicyService]
- [ViewService]->[views]
- [PolicyService]->[policies]
- [TypeService]->[types]
-]
-```
-
-A short summary of the roles of these services:
-
-* _[ObjectService](#object-service)_: Allows retrieval of domain objects by
- their identifiers; in practice, often the main point of entry into the
- [information model](#information-model).
-* _[ModelService](#model-service)_: Provides domain object models, retrieved
- by their identifier.
-* _[CapabilityService](#capability-service)_: Provides capabilities, as they
- apply to specific domain objects (as judged from their model.)
-* _[TelemetryService](#telemetry-service)_: Provides access to historical
- and real-time telemetry data.
-* _[PersistenceService](#persistence-service)_: Provides the ability to
- store and retrieve documents (such as domain object models.)
-* _[ActionService](#action-service)_: Provides distinct user actions that
- can take place within the system (typically, upon or using domain objects.)
-* _[ViewService](#view-service)_: Provides views for domain objects. A view
- is a user-selectable representation of a domain object (in practice, an
- HTML template.)
-* _[PolicyService](#policy-service)_: Handles decisions about which
- behavior are allowed within certain specific contexts.
-* _[TypeService](#type-service)_: Provides information to distinguish
- different types of domain objects from one another within the system.
-
-## Object Service
-
-```nomnoml
-#direction: right
-[<abstract> ObjectService|
- getObjects(ids : Array.<string>) : Promise.<object.<string, DomainObject>>
-]
-[DomainObjectProvider]--:>[ObjectService]
-[DomainObjectProvider]o-[ModelService]
-[DomainObjectProvider]o-[CapabilityService]
-```
-
-As domain objects are central to Open MCT's information model,
-acquiring domain objects is equally important.
-
-```nomnoml
-#direction: right
-[<start> Start]->[<state> Look up models]
-[<state> Look up models]->[<state> Look up capabilities]
-[<state> Look up capabilities]->[<state> Instantiate DomainObject]
-[<state> Instantiate DomainObject]->[<end> End]
-```
-
-Open MCT includes an implementation of an `ObjectService` which
-satisfies this capability by:
-
-* Consulting the [Model Service](#model-service) to acquire domain object
- models by identifier.
-* Passing these models to a [Capability Service](#capability-service) to
- determine which capabilities are applicable.
-* Combining these results together as [DomainObject](#information-model)
- instances.
-
-## Model Service
-
-```nomnoml
-#direction: down
-[<abstract> ModelService|
- getModels(ids : Array.<string>) : Promise.<object.<string, object>>
-]
-[StaticModelProvider]--:>[ModelService]
-[RootModelProvider]--:>[ModelService]
-[PersistedModelProvider]--:>[ModelService]
-[ModelAggregator]--:>[ModelService]
-[CachingModelDecorator]--:>[ModelService]
-[MissingModelDecorator]--:>[ModelService]
-
-[MissingModelDecorator]o-[CachingModelDecorator]
-[CachingModelDecorator]o-[ModelAggregator]
-[ModelAggregator]o-[StaticModelProvider]
-[ModelAggregator]o-[RootModelProvider]
-[ModelAggregator]o-[PersistedModelProvider]
-
-[PersistedModelProvider]o-[PersistenceService]
-[RootModelProvider]o-[roots]
-[StaticModelProvider]o-[models]
-```
-
-The platform's model service is responsible for providing domain object
-models (effectively, JSON documents describing the persistent state
-associated with domain objects.) These are retrieved by identifier.
-
-The platform includes multiple components of this variety:
-
-* `PersistedModelProvider` looks up domain object models from
- a persistence store (the [`PersistenceService`](#persistence-service));
- this is how user-created and user-modified
- domain object models are retrieved.
-* `RootModelProvider` provides domain object models that have been
- declared via the `roots` extension category. These will appear at the
- top level of the tree hierarchy in the user interface.
-* `StaticModelProvider` provides domain object models that have been
- declared via the `models` extension category. This is useful for
- allowing plugins to expose new domain objects declaratively.
-* `ModelAggregator` merges together the results from multiple providers.
- If multiple providers return models for the same domain object,
- the most recently modified version (as determined by the `modified`
- property of the model) is chosen.
-* `CachingModelDecorator` caches model instances in memory. This
- ensures that only a single instance of a domain object model is
- present at any given time within the application, and prevent
- redundant retrievals.
-* `MissingModelDecorator` adds in placeholders when no providers
- have returned domain object models for a specific identifier. This
- allows the user to easily see that something was expected to be
- present, but wasn't.
-
-## Capability Service
-
-```nomnoml
-#direction: down
-[<abstract> CapabilityService|
- getCapabilities(model : object) : object.<string, Function>
-]
-[CoreCapabilityProvider]--:>[CapabilityService]
-[QueuingPersistenceCapabilityDecorator]--:>[CapabilityService]
-
-[CoreCapabilityProvider]o-[capabilities]
-[QueuingPersistenceCapabilityDecorator]o-[CoreCapabilityProvider]
-```
-
-The capability service is responsible for determining which capabilities
-are applicable for a given domain object, based on its model. Primarily,
-this is handled by the `CoreCapabilityProvider`, which examines
-capabilities exposed via the `capabilities` extension category.
-
-Additionally, `platform/persistence/queue` decorates the persistence
-capability specifically to batch persistence attempts among multiple
-objects (this allows failures to be recognized and handled in groups.)
-
-## Telemetry Service
-
-```nomnoml
-[<abstract> TelemetryService|
- requestData(requests : Array.<TelemetryRequest>) : Promise.<object>
- subscribe(requests : Array.<TelemetryRequest>) : Function
-]<--:[TelemetryAggregator]
-```
-
-The telemetry service is responsible for acquiring telemetry data.
-
-Notably, the platform does not include any providers for
-`TelemetryService`; applications built on Open MCT will need to
-implement a provider for this service if they wish to expose telemetry
-data. This is usually the most important step for integrating Open MCT
-into an existing telemetry system.
-
-Requests for telemetry data are usually initiated in the
-[presentation layer](#presentation-layer) by some `Controller` referenced
-from a view. The `telemetryHandler` service is most commonly used (although
-one could also use an object's `telemetry` capability directly) as this
-handles capability delegation, by which a domain object such as a Telemetry
-Panel can declare that its `telemetry` capability should be handled by the
-objects it contains. Ultimately, the request for historical data and the
-new subscriptions will reach the `TelemetryService`, and, by way of the
-provider(s) which are present for that `TelemetryService`, will pass the
-same requests to the back-end.
-
-```nomnoml
-[<start> Start]->[Controller]
-[Controller]->[<state> declares object of interest]
-[declares object of interest]->[TelemetryHandler]
-[TelemetryHandler]->[<state> requests telemetry from capabilities]
-[TelemetryHandler]->[<state> subscribes to telemetry using capabilities]
-[requests telemetry from capabilities]->[TelemetryCapability]
-[subscribes to telemetry using capabilities]->[TelemetryCapability]
-[TelemetryCapability]->[<state> requests telemetry]
-[TelemetryCapability]->[<state> subscribes to telemetry]
-[requests telemetry]->[TelemetryService]
-[subscribes to telemetry]->[TelemetryService]
-[TelemetryService]->[<state> issues request]
-[TelemetryService]->[<state> updates subscriptions]
-[TelemetryService]->[<state> listens for real-time data]
-[issues request]->[<database> Telemetry Back-end]
-[updates subscriptions]->[Telemetry Back-end]
-[listens for real-time data]->[Telemetry Back-end]
-[Telemetry Back-end]->[<end> End]
-```
-
-The back-end, in turn, is expected to provide whatever historical
-telemetry is available to satisfy the request that has been issue.
-
-```nomnoml
-[<start> Start]->[<database> Telemetry Back-end]
-[Telemetry Back-end]->[<state> transmits historical telemetry]
-[transmits historical telemetry]->[TelemetryService]
-[TelemetryService]->[<state> packages telemetry, fulfills requests]
-[packages telemetry, fulfills requests]->[TelemetryCapability]
-[TelemetryCapability]->[<state> unpacks telemetry per-object, fulfills request]
-[unpacks telemetry per-object, fulfills request]->[TelemetryHandler]
-[TelemetryHandler]->[<state> exposes data]
-[TelemetryHandler]->[<state> notifies controller]
-[exposes data]->[Controller]
-[notifies controller]->[Controller]
-[Controller]->[<state> prepares data for template]
-[prepares data for template]->[Template]
-[Template]->[<state> displays data]
-[displays data]->[<end> End]
-```
-
-One peculiarity of this approach is that we package many responses
-together at once in the `TelemetryService`, then unpack these in the
-`TelemetryCapability`, then repackage these in the `TelemetryHandler`.
-The rationale for this is as follows:
-
-* In the `TelemetryService`, we want to have the ability to combine
- multiple requests into one call to the back-end, as many back-ends
- will support this. It follows that we give the response as a single
- object, packages in a manner that allows responses to individual
- requests to be easily identified.
-* In the `TelemetryCapability`, we want to provide telemetry for a
- _single object_, so the telemetry data gets unpacked. This allows
- for the unpacking of data to be handled in a single place, and
- also permits a flexible substitution method; domain objects may have
- implementations of the `telemetry` capability that do not use the
- `TelemetryService` at all, while still maintaining compatibility
- with any presentation layer code written to utilize this capability.
- (This is true of capabilities generally.)
-* In the `TelemetryHandler`, we want to group multiple responses back
- together again to make it easy for the presentation layer to consume.
- In this case, the grouping is different from what may have occurred
- in the `TelemetryService`; this grouping is based on what is expected
- to be useful _in a specific view_. The `TelemetryService`
- may be receiving requests from multiple views.
-
-```nomnoml
-[<start> Start]->[<database> Telemetry Back-end]
-[Telemetry Back-end]->[<state> notifies client of new data]
-[notifies client of new data]->[TelemetryService]
-[TelemetryService]->[<choice> relevant subscribers?]
-[relevant subscribers?] yes ->[<state> notify subscribers]
-[relevant subscribers?] no ->[<state> ignore]
-[ignore]->[<end> Ignored]
-[notify subscribers]->[TelemetryCapability]
-[TelemetryCapability]->[<state> notify listener]
-[notify listener]->[TelemetryHandler]
-[TelemetryHandler]->[<state> exposes data]
-[TelemetryHandler]->[<state> notifies controller]
-[exposes data]->[Controller]
-[notifies controller]->[Controller]
-[Controller]->[<state> prepares data for template]
-[prepares data for template]->[Template]
-[Template]->[<state> displays data]
-[displays data]->[<end> End]
-```
-
-The flow of real-time data is similar, and is handled by a sequence
-of callbacks between the presentation layer component which is
-interested in data and the telemetry service. Providers in the
-telemetry service listen to the back-end for new data (via whatever
-mechanism their specific back-end supports), package this data in
-the same manner as historical data, and pass that to the callbacks
-which are associated with relevant requests.
-
-## Persistence Service
-
-```nomnoml
-#direction: right
-[<abstract> PersistenceService|
- listSpaces() : Promise.<Array.<string>>
- listObjects() : Promise.<Array.<string>>
- createObject(space : string, key : string, document : object) : Promise.<boolean>
- readObject(space : string, key : string, document : object) : Promise.<object>
- updateObject(space : string, key : string, document : object) : Promise.<boolean>
- deleteObject(space : string, key : string, document : object) : Promise.<boolean>
-]
-
-[ElasticPersistenceProvider]--:>[PersistenceService]
-[ElasticPersistenceProvider]->[<database> ElasticSearch]
-
-[CouchPersistenceProvider]--:>[PersistenceService]
-[CouchPersistenceProvider]->[<database> CouchDB]
-```
-
-Closely related to the notion of domain objects models is their
-persistence. The `PersistenceService` allows these to be saved
-and loaded. (Currently, this capability is only used for domain
-object models, but the interface has been designed without this idea
-in mind; other kinds of documents could be saved and loaded in the
-same manner.)
-
-There is no single definitive implementation of a `PersistenceService` in
-the platform. Optional adapters are provided to store and load documents
-from CouchDB and ElasticSearch, respectively; plugin authors may also
-write additional adapters to utilize different back end technologies.
-
-## Action Service
-
-```nomnoml
-[ActionService|
- getActions(context : ActionContext) : Array.<Action>
-]
-[ActionProvider]--:>[ActionService]
-[CreateActionProvider]--:>[ActionService]
-[ActionAggregator]--:>[ActionService]
-[LoggingActionDecorator]--:>[ActionService]
-[PolicyActionDecorator]--:>[ActionService]
-
-[LoggingActionDecorator]o-[PolicyActionDecorator]
-[PolicyActionDecorator]o-[ActionAggregator]
-[ActionAggregator]o-[ActionProvider]
-[ActionAggregator]o-[CreateActionProvider]
-
-[ActionProvider]o-[actions]
-[CreateActionProvider]o-[TypeService]
-[PolicyActionDecorator]o-[PolicyService]
-```
-
-Actions are discrete tasks or behaviors that can be initiated by a user
-upon or using a domain object. Actions may appear as menu items or
-buttons in the user interface, or may be triggered by certain gestures.
-
-Responsibilities of platform components of the action service are as
-follows:
-
-* `ActionProvider` exposes actions registered via extension category
- `actions`, supporting simple addition of new actions. Actions are
- filtered down to match action contexts based on criteria defined as
- part of an action's extension definition.
-* `CreateActionProvider` provides the various Create actions which
- populate the Create menu. These are driven by the available types,
- so do not map easily to extension category `actions`; instead, these
- are generated after looking up which actions are available from the
- [`TypeService`](#type-service).
-* `ActionAggregator` merges together actions from multiple providers.
-* `PolicyActionDecorator` enforces the `action` policy category by
- filtering out actions which violate this policy, as determined by
- consulting the [`PolicyService`](#policy-service).
-* `LoggingActionDecorator` wraps exposed actions and writes to the
- console when they are performed.
-
-## View Service
-
-```nomnoml
-[ViewService|
- getViews(domainObject : DomainObject) : Array.<View>
-]
-[ViewProvider]--:>[ViewService]
-[PolicyViewDecorator]--:>[ViewService]
-
-[ViewProvider]o-[views]
-[PolicyViewDecorator]o-[ViewProvider]
-```
-
-The view service provides views that are relevant to a specified domain
-object. A "view" is a user-selectable visualization of a domain object.
-
-The responsibilities of components of the view service are as follows:
-
-* `ViewProvider` exposes views registered via extension category
- `views`, supporting simple addition of new views. Views are
- filtered down to match domain objects based on criteria defined as
- part of a view's extension definition.
-* `PolicyViewDecorator` enforces the `view` policy category by
- filtering out views which violate this policy, as determined by
- consulting the [`PolicyService`](#policy-service).
-
-## Policy Service
-
-```nomnoml
-[PolicyService|
- allow(category : string, candidate : object, context : object, callback? : Function) : boolean
-]
-[PolicyProvider]--:>[PolicyService]
-[PolicyProvider]o-[policies]
-```
-
-The policy service provides a general-purpose extensible decision-making
-mechanism; plugins can add new extensions of category `policies` to
-modify decisions of a known category.
-
-Often, the policy service is referenced from a decorator for another
-service, to filter down the results of using that service based on some
-appropriate policy category.
-
-The policy provider works by looking up all registered policy extensions
-which are relevant to a particular _category_, then consulting each in
-order to see if they allow a particular _candidate_ in a particular
-_context_; the types for the `candidate` and `context` arguments will
-vary depending on the `category`. Any one policy may disallow the
-decision as a whole.
-
-
-```nomnoml
-[<start> Start]->[<state> is something allowed?]
-[is something allowed?]->[PolicyService]
-[PolicyService]->[<state> look up relevant policies by category]
-[look up relevant policies by category]->[<state> consult policy #1]
-[consult policy #1]->[Policy #1]
-[Policy #1]->[<choice> policy #1 allows?]
-[policy #1 allows?] no ->[<state> decision disallowed]
-[policy #1 allows?] yes ->[<state> consult policy #2]
-[consult policy #2]->[Policy #2]
-[Policy #2]->[<choice> policy #2 allows?]
-[policy #2 allows?] no ->[<state> decision disallowed]
-[policy #2 allows?] yes ->[<state> consult policy #3]
-[consult policy #3]->[<state> ...]
-[...]->[<state> consult policy #n]
-[consult policy #n]->[Policy #n]
-[Policy #n]->[<choice> policy #n allows?]
-[policy #n allows?] no ->[<state> decision disallowed]
-[policy #n allows?] yes ->[<state> decision allowed]
-[decision disallowed]->[<end> Disallowed]
-[decision allowed]->[<end> Allowed]
-```
-
-The policy decision is effectively an "and" operation over the individual
-policy decisions: That is, all policies must agree to allow a particular
-policy decision, and the first policy to disallow a decision will cause
-the entire decision to be disallowed. As a consequence of this, policies
-should generally be written with a default behavior of "allow", and
-should only disallow the specific circumstances they are intended to
-disallow.
-
-## Type Service
-
-```nomnoml
-[TypeService|
- listTypes() : Array.<Type>
- getType(key : string) : Type
-]
-[TypeProvider]--:>[TypeService]
-[TypeProvider]o-[types]
-```
-
-The type service provides metadata about the different types of domain
-objects that exist within an Open MCT application. The platform
-implementation reads these types in from extension category `types`
-and wraps them in a JavaScript interface.
diff --git a/docs/src/design/index.md b/docs/src/design/index.md
deleted file mode 100644
index 7b4c3e4eb..000000000
--- a/docs/src/design/index.md
+++ /dev/null
@@ -1,3 +0,0 @@
-Design proposals:
-
-* [API Redesign](proposals/APIRedesign.md) \ No newline at end of file
diff --git a/docs/src/design/planning/APIRefactor.md b/docs/src/design/planning/APIRefactor.md
deleted file mode 100644
index ec98a115f..000000000
--- a/docs/src/design/planning/APIRefactor.md
+++ /dev/null
@@ -1,338 +0,0 @@
-# API Refactoring
-
-This document summarizes a path toward implementing API changes
-from the [API Redesign](../proposals/APIRedesign.md) for Open MCT
-v1.0.0.
-
-# Goals
-
-These plans are intended to minimize:
-
-* Waste; avoid allocating effort to temporary changes.
-* Downtime; avoid making changes in large increments that blocks
- delivery of new features for substantial periods of time.
-* Risk; ensure that changes can be validated quickly, avoid putting
- large effort into changes that have not been validated.
-
-# Plan
-
-```nomnoml
-#comment: This diagram is in nomnoml syntax and should be rendered.
-#comment: See https://github.com/nasa/openmctweb/issues/264#issuecomment-167166471
-
-
-[<start> Start]->[<state> Imperative bundle registration]
-
-[<state> Imperative bundle registration]->[<state> Build and packaging]
-[<state> Imperative bundle registration]->[<state> Refactor API]
-
-[<state> Build and packaging |
- [<start> Start]->[<state> Incorporate a build step]
- [<state> Incorporate a build step |
- [<start> Start]->[<state> Choose package manager]
- [<start> Start]->[<state> Choose build system]
- [<state> Choose build system]<->[<state> Choose package manager]
- [<state> Choose package manager]->[<state> Implement]
- [<state> Choose build system]->[<state> Implement]
- [<state> Implement]->[<end> End]
- ]->[<state> Separate repositories]
- [<state> Separate repositories]->[<end> End]
-]->[<state> Release candidacy]
-
-
-[<start> Start]->[<state> Design registration API]
-
-[<state> Design registration API |
- [<start> Start]->[<state> Decide on role of Angular]
- [<state> Decide on role of Angular]->[<state> Design API]
- [<state> Design API]->[<choice> Passes review?]
- [<choice> Passes review?] no ->[<state> Design API]
- [<choice> Passes review?]-> yes [<end> End]
-]->[<state> Refactor API]
-
-[<state> Refactor API |
- [<start> Start]->[<state> Imperative extension registration]
- [<state> Imperative extension registration]->[<state> Refactor individual extensions]
-
- [<state> Refactor individual extensions |
- [<start> Start]->[<state> Prioritize]
- [<state> Prioritize]->[<choice> Sufficient value added?]
- [<choice> Sufficient value added?] no ->[<end> End]
- [<choice> Sufficient value added?] yes ->[<state> Design]
- [<state> Design]->[<choice> Passes review?]
- [<choice> Passes review?] no ->[<state> Design]
- [<choice> Passes review?]-> yes [<state> Implement]
- [<state> Implement]->[<end> End]
- ]->[<state> Remove legacy bundle support]
-
- [<state> Remove legacy bundle support]->[<end> End]
-]->[<state> Release candidacy]
-
-[<state> Release candidacy |
- [<start> Start]->[<state> Verify |
- [<start> Start]->[<choice> API well-documented?]
- [<start> Start]->[<choice> API well-tested?]
- [<choice> API well-documented?]-> no [<state> Write documentation]
- [<choice> API well-documented?] yes ->[<end> End]
- [<state> Write documentation]->[<choice> API well-documented?]
- [<choice> API well-tested?]-> no [<state> Write test cases]
- [<choice> API well-tested?]-> yes [<end> End]
- [<state> Write test cases]->[<choice> API well-tested?]
- ]
- [<start> Start]->[<state> Validate |
- [<start> Start]->[<choice> Passes review?]
- [<start> Start]->[<state> Use internally]
- [<state> Use internally]->[<choice> Proves useful?]
- [<choice> Passes review?]-> no [<state> Address feedback]
- [<state> Address feedback]->[<choice> Passes review?]
- [<choice> Passes review?] yes -> [<end> End]
- [<choice> Proves useful?] yes -> [<end> End]
- [<choice> Proves useful?] no -> [<state> Fix problems]
- [<state> Fix problems]->[<state> Use internally]
- ]
- [<state> Validate]->[<end> End]
- [<state> Verify]->[<end> End]
-]->[<state> Release]
-
-[<state> Release]->[<end> End]
-```
-
-## Step 1. Imperative bundle registration
-
-Register whole bundles imperatively, using their current format.
-
-For example, in each bundle add a `bundle.js` file:
-
-```js
-define([
- 'mctRegistry',
- 'json!bundle.json'
-], function (mctRegistry, bundle) {
- mctRegistry.install(bundle, "path/to/bundle");
-});
-```
-
-Where `mctRegistry.install` is placeholder API that wires into the
-existing bundle registration mechanisms. The main point of entry
-would need to be adapted to clearly depend on these bundles
-(in the require sense of a dependency), and the framework layer
-would need to implement and integrate with this transitional
-API.
-
-Benefits:
-
-* Achieves an API Redesign goal with minimal immediate effort.
-* Conversion to an imperative syntax may be trivially automated.
-* Minimal change; reuse existing bundle definitions, primarily.
-* Allows early validation of switch to imperative; unforeseen
- consequences of the change may be detected at this point.
-* Allows implementation effort to progress in parallel with decisions
- about API changes, including fundamental ones such as the role of
- Angular. May act in some sense as a prototype to inform those
- decisions.
-* Creates a location (framework layer) where subsequent changes to
- the manner in which extensions are registered may be centralized.
- When there is a one-to-one correspondence between the existing
- form of an extension and its post-refactor form, adapters can be
- written here to defer the task of making changes ubiquitously
- throughout bundles, allowing for earlier validation and
- verification of those changes, and avoiding ubiquitous changes
- which might require us to go dark. (Mitigates
- ["greenfield paradox"](http://stepaheadsoftware.blogspot.com/2012/09/greenfield-or-refactor-legacy-code-base.html);
- want to add value with new API but don't want to discard value
- of tested/proven legacy codebase.)
-
-Detriments:
-
-* Requires transitional API to be implemented/supported; this is
- waste. May mitigate this by time-bounding the effort put into
- this step to ensure that waste is minimal.
-
-Note that API changes at this point do not meaningfully reflect
-the desired 1.0.0 API, so no API reviews are necessary.
-
-## Step 2. Incorporate a build step
-
-After the previous step is completed, there should be a
-straightforward dependency graph among AMD modules, and an
-imperative (albeit transitional) API allowing for other plugins
-to register themselves. This should allow for a build step to
-be included in a straightforward fashion.
-
-Some goals for this build step:
-
-* Compile (and, preferably, optimize/minify) Open MCT
- sources into a single `.js` file.
- * It is desirable to do the same for HTML sources, but
- may wish to defer this until a subsequent refactoring
- step if appropriate.
-* Provide non-code assets in a format that can be reused by
- derivative projects in a straightforward fashion.
-
-Should also consider which dependency/packaging manager should
-be used by dependent projects to obtain Open MCT. Approaches
-include:
-
-1. Plain `npm`. Dependents then declare their dependency with
- `npm` and utilize built sources and assets in a documented
- fashion. (Note that there are
- [documented challenges](http://blog.npmjs.org/post/101775448305/npm-and-front-end-packaging)
- in using `npm` in this fashion.)
-2. Build with `npm`, but recommend dependents install using
- `bower`, as this is intended for front-end development. This may
- require checking in built products, however, which
- we wish to avoid (this could be solved by maintaining
- a separate repository for built products.)
-
-In all cases, there is a related question of which build system
-to use for asset generation/management and compilation/minification/etc.
-
-1. [`webpack`](https://webpack.github.io/)
- is well-suited in principle, as it is specifically
- designed for modules with non-JS dependencies. However,
- there may be limitations and/or undesired behavior here
- (for instance, CSS dependencies get in-lined as style tags,
- removing our ability to control ordering) so it may
-2. `gulp` or `grunt`. Commonplace, but both still require
- non-trivial coding and/or configuration in order to produce
- appropriate build artifacts.
-3. [Just `npm`](http://blog.keithcirkel.co.uk/how-to-use-npm-as-a-build-tool/).
- Reduces the amount of tooling being used, but may introduce
- some complexity (e.g. custom scripts) to the build process,
- and may reduce portability.
-
-## Step 3. Separate repositories
-
-Refactor existing applications built on Open MCT such that they
-are no longer forks, but instead separate projects with a dependency
-on the built artifacts from Step 2.
-
-Note that this is achievable already using `bower` (see `warp-bower`
-branch at http://developer.nasa.gov/mct/warp for an example.)
-However, changes involved in switching to an imperative API and
-introducing a build process may change (and should simplify) the
-approach used to utilize Open MCT as a dependency, so these
-changes should be introduced first.
-
-## Step 4. Design registration API
-
-Design the registration API that will replace declarative extension
-categories and extensions (including Angular built-ins and composite
-services.)
-
-This may occur in parallel with implementation steps.
-
-It will be necessary
-to have a decision about the role of Angular at this point; are extensions
-registered via provider configuration (Angular), or directly in some
-exposed registry?
-
-Success criteria here should be based on peer review. Scope of peer
-review should be based on perceived risk/uncertainty surrounding
-proposed changes, to avoid waste; may wish to limit this review to
-the internal team. (The extent to which external
-feedback is available is limited, but there is an inherent timeliness
-to external review; need to balance this.)
-
-Benefits:
-
-* Solves the "general case" early, allowing for early validation.
-
-Note that in specific cases, it may be desirable to refactor some
-current "extension category" in a manner that will not appear as
-registries, _or_ to locate these in different
-namespaces, _or_ to remove/replace certain categories entirely.
-This work is deferred intentionally to allow for a solution of the
-general case.
-
-## Step 5. Imperative extension registration
-
-Register individual extensions imperatively, implementing API changes
-from the previous step. At this stage, _usage_ of the API may be confined
-to a transitional adapter in the framework layer; bundles may continue
-to utilize the transitional API for registering extensions in the
-legacy format.
-
-An important, ongoing sub-task here will be to discover and define dependencies
-among bundles. Composite services and extension categories are presently
-"implicit"; after the API redesign, these will become "explicit", insofar
-as some specific component will be responsible for creating any registries.
-As such, "bundles" which _use_ specific registries will need to have an
-enforceable dependency (e.g. require) upon those "bundles" which
-_declare_ those registries.
-
-## Step 6. Refactor individual extensions
-
-Refactor individual extension categories and/or services that have
-been identified as needing changes. This includes, but is not
-necessarily limited to:
-
-* Views/Representations/Templates (refactored into "components.")
-* Capabilities (refactored into "roles", potentially.)
-* Telemetry (from `TelemetrySeries` to `TelemetryService`.)
-
-Changes should be made one category at a time (either serially
-or separately in parallel) and should involve a tight cycle of:
-
-1. Prioritization/reprioritization; highest-value API improvements
- should be done first.
-2. Design.
-3. Review. Refactoring individual extensions will require significant
- effort (likely the most significant effort in the process) so changes
- should be validated early to minimize risk/waste.
-4. Implementation. These changes will not have a one-to-one relationship
- with existing extensions, so changes cannot be centralized; usages
- will need to be updated across all "bundles" instead of centralized
- in a legacy adapter. If changes are of sufficient complexity, some
- planning should be done to spread out the changes incrementally.
-
-By necessity, these changes may break functionality in applications
-built using Open MCT. On a case-by-case basis, should consider
-providing temporary "legacy support" to allow downstream updates
-to occur as a separate task; the relevant trade here is between
-waste/effort required to maintain legacy support, versus the
-downtime which may be introduced by making these changes simultaneously
-across several repositories.
-
-
-## Step 7. Remove legacy bundle support
-
-Update bundles to remove any usages of legacy support for bundles
-(including that used by dependent projects.) Then, remove legacy
-support from Open MCT.
-
-## Step 8. Release candidacy
-
-Once API changes are complete, Open MCT should enter a release
-candidacy cycle. Important things to look at here:
-
-* Are changes really complete?
- * Are they sufficiently documented?
- * Are they sufficiently tested?
-* Are changes really sufficient?
- * Do reviewers think they are usable?
- * Does the development team find them useful in practice? This
- will require calendar time to ascertain; should allocate time
- for this, particularly in alignment with the sprint/release
- cycle.
- * Has learning curve been measurably decreased? Comparing a to-do
- list tutorial to [other examples(http://todomvc.com/) could
- provide an empirical basis to this. How much code is required?
- How much explanation is required? How many dependencies must
- be installed before initial setup?
- * Does the API offer sufficient power to implement the extensions we
- anticipate?
- * Any open API-related issues which should block a 1.0.0 release?
-
-Any problems identified during release candidacy will require
-subsequent design changes and planning.
-
-## Step 9. Release
-
-Once API changes have been verified and validated, proceed
-with release, including:
-
-* Tagging as version 1.0.0 (at an appropriate time in the
- sprint/release cycle.)
-* Close any open issues which have been resolved (or made obsolete)
- by API changes. \ No newline at end of file
diff --git a/docs/src/design/proposals/APIRedesign.md b/docs/src/design/proposals/APIRedesign.md
deleted file mode 100644
index 93098c3d7..000000000
--- a/docs/src/design/proposals/APIRedesign.md
+++ /dev/null
@@ -1,1282 +0,0 @@
-# Overview
-
-The purpose of this document is to review feedback on Open MCT's
-current API and propose improvements to the API, particularly for a
-1.0.0 release.
-
-Strategically, this is handled by:
-
-* Identifying broader goals.
-* Documenting feedback and related background information.
-* Reviewing feedback to identify trends and useful features.
- * In particular, pull out "pain points" to attempt to address,
- as well as positive attributes to attempt to preserve.
-* Proposing a set of API changes to address these "pain points."
- * This also takes into account scheduling concerns.
-* Once agreed-upon, formalize this set of changes (e.g. as UML
- diagrams) and plan to implement them.
-
-# Goals
-
-## Characteristics of a good API
-
-A good API:
-
-* Is easy to understand.
-* Rewards doing things "the right way."
-* Saves development effort.
-* Is powerful enough to support a broad range of applications.
-* Lends itself to good documentation.
-
-These characteristics can sometimes be at odds with each other, or
-with other concerns. These should typically be viewed as participants
-in trades.
-
-## Evaluating APIs
-
-APIs may be evaluated based on:
-
-* Number of interfaces.
- * How many application-specific interfaces do I need to know to
- solve a certain class of problems?
-* Size of interfaces.
- * How many methods does each interface have?
-* Depth of interfaces.
- * Specifically, how many methods do I need to call before the return
- value is of a form that is not specific to the API?
-* Clarity of interfaces.
- * How much documentation or learning is required before an interface is
- useful?
-* Consistency of interfaces.
- * How similar is one interface to an analogous interface?
-* Utility of interfaces.
- * How much development effort is reduced by utilizing these interfaces,
- versus accomplishing the same goals with other tools?
-* Power of interfaces.
- * How much application functionality can I influence with the interfaces
- that are available to me?
-
-In general, prefer to have a small number of simple, shallow, clear,
-useful, powerful interfaces.
-
-# Developer Feedback
-
-## Developer Intern Feedback
-
-This feedback comes from interns who worked closely with
-Open MCT as their primary task over the Summer of 2015.
-
-### Developer Intern 1
-
-Worked on bug fixes in the platform and a plugin for search.
-
-* Initially, it was confusing that many things in files that are in
- very different locations in the code base refer to each other.
- * Perhaps explain more the organization strategy behind the
- different main sections, like "commonUI" vs "core".
-* This may be just me, but there are often long chains of related
- functions calling each other, and when I had to modify the behavior,
- I had a hard time remembering to look for the highest level function
- in the call chain to change. I also sometimes had a hard time finding
- the connections between the functions. But, that is important because
- the implementation of the functions along the chain may change later.
-* One very helpful thing that you could add might just be documentation
- that is not in paragraph format like in the current developer guide.
- I would just like a list of all the functions and members of each kind
- of object there is, and descriptions of what they are and how they're
- used.
- * Also, the current developer guide pdf's words that are in 'code font',
- rather than the normal text, are not searchable.
- (Depending on the pdf viewer.)
-* I do appreciate that there is some example code.
-* I am still slightly confused about what "domainObject" refers to in
- different situations.
-* The tutorials are helpful, but only really for designing new views.
- It doesn't help much with gaining understanding of how the other parts
- of the application work.
-* The general idea of 'telemetry' in this context is kind of confusing.
- It is hard to figure out what the difference between the various ways of
- dealing with telemetry are. e.g., what is the difference between just
- "Telemetry" and the "Telemetry Service"? There are many
- "Telemetry Things" which seem related, but in an unclear way.
-
-### Developer Intern 2
-
-Worked on platform bug fixes and mobile support.
-
-* No guide for the UI and front end for the HTML/CSS part of Open MCT.
- Not sure if this is applicable or needed for developers, however would
- be helpful to any front end development
-* Found it difficult to follow the plot controller & subplot
- functions/features, such as zooming.
-* If the developer guide could have for references to which files or
- functions are key for gestures, browse navigation, etc it would be
- helpful for future developers as a place to start looking. I found
- it occasionally difficult to find which files or functions I wanted
- at first.
-
-## Plugin Developer Feedback
-
-This feedback comes from developers who have worked on plugins for
-Open MCT, but have not worked on the platform.
-
-### Plugin Developer 1
-
-Used Open MCT over the course of several months (on a
-less-than-half-time basis) to develop a
-spectrum visualization plugin.
-
-* Not a lot of time to work on this, made it hard to get up the learning
- curve.
- * Note that this is the norm, particularly for GDS development.
-* JavaScript carries its own learning curve.
-* The fact that it pulls in other tools whose APIs need to be learned
- also makes the learning curve harder to get up.
-* Tracking down interconnected parts was a bit difficult.
-* Could really use examples.
-* Easy to get lost when not immersed in the style.
-
-### Plugin Developer 2
-
-Used Open MCT over the course of several weeks (on a half-time basis)
-to develop a tabular visualization plugin.
-
-* Pain points
- * Unable to copy and paste from tutorial pdfs into code
- * Wanted to verify my environment was setup properly so that I
- could get the final product working in the end without having
- to type everything out. Perhaps there could be something in
- github that has the final completed tutorial for new users to
- checkout? Or a step by step one kind of like the tutorials on
- the angular js webpage?
- * Typing too long without seeing results of what I was doing
- * At some points in the tutorial I ended up typing for the sake
- of typing without knowing what I was really typing for.
- * If there were break points where we could run the incomplete
- code and just see a variable dump or something even that would
- be helpful to know that I am on the right track.
- * Documentation on features are a bit hard to find.
- * I'm not sure what I can do until I search through examples of
- existing code and work my way backwards.
- * Maybe you can link the features we are using in the tutorial to
- their respective parts in the developer guide? Not sure if that
- can be done on PDFs, so maybe a webpage instead?
-* Positive Attributes
- * Unable to copy and paste from tutorial pdfs into code
- * I know I also listed this as a pain, but it was kind of helpful
- being forced to read and type everything out.
- * "Widgets" are self contained in their own directories. I don't have
- to be afraid of exploding things.
- * All files/config that I care about for a "widget" can be found in
- the bundles.json
-* Misc
- * Coming from a not so strong webdev background and on top of that a
- zero strong angular background I think starting off with a simple
- "Hello World" webpage tutorial would have been nice.
- * Start off with a bare bones bundle json with an empty controller
- and static "Hello World" in the view
- * Add the variable "Hello World" into the controller for the view
- to display
- * Add a model property to the bundle.json to take in "Hello World"
- as a parameter and pass through to the controller/view
-
-### Open Source Contributor
-
- * [Failures are non-graceful when services are missing.](
- https://github.com/nasa/openmctweb/issues/79)
-
-## Misc. Feedback (mostly verbal)
-
-* Easy to add things.
-* Separation of concerns is unclear (particularly: "where's the MVC?")
-* Telemetry API is confusing. In particular, `TelemetrySeries` should
- just be an array.
- * Came out of design discussions for Limits.
-* Capabilities are confusing.
-
-## Long-term Developer Notes
-
-The following notes are from original platform developer, with long
-term experience using Open MCT.
-
-* Bundle mechanism allows for grouping related components across concerns,
- and adding and removing these easily. (e.g. model and view components of
- Edit mode are all grouped together in the Edit bundle.)
-
-## AngularJS
-
-Angular 2.0.0 is coming (maybe by end of 2015.)
-
-It will not be backwards-compatible with Angular 1.x.
-The differences are significant enough that switching to
-Angular 2 will require only slightly less effort than switching
-to an entirely different framework.
-
-We can expect AngularJS 1.x to reach end-of-life reasonably soon thereafter.
-
-Our API is currently a superset of Angular's API, so this directly affects
-our API. Specifically, API changes should be oriented towards removing
-or reducing the Angular dependency.
-
-### Angular's Role
-
-Angular is Open MCT's:
-
-* Dependency injection framework.
-* Template rendering.
-* DOM interactions.
-* Services library.
-* Form validator.
-* Routing.
-
-This is the problem with frameworks: They become a single point of
-failure for unrelated concerns.
-
-### Rationale for Adopting Angular
-
-The rationale for adopting AngularJS as a framework is
-documented in https://trunk.arc.nasa.gov/jira/browse/WTD-208.
-Summary of the expected benefits:
-
-* Establishes design patterns that are well-documented and
- understood in industry. This can be beneficial in training
- new staff, and lowers the documentation burden on the local
- development team. If MCT-Web were to stay with its current
- architecture, significant developer-oriented documentation
- and training materials would need to be produced.
-* The maintainability of MCT-Web would be enhanced by using a
- framework like Angular. The local team would enjoy the benefits of
- maintenance performed by the sponsor, but would not incur any cost
- for this. This would include future upgrades, testing, and bug fixes.
-* Replaces DOM-manipulation with a declarative data-binding syntax
- which automatically updates views when the model data changes. This
- pattern has the potential to save the development team from
- time-consuming and difficult-to-debug DOM manipulation.
-* Provides data binding to backend models.
-* Provides patterns for form validation.
-* Establishes documented patterns for add-on modules and services.
-* Supports unit tests and system tests (tests which simulate user
- interactions in the browser)
-* Angular software releases can be expected to be tested, which would
- allow MCT-Web developers to focus on MCT-specific features, instead
- of the maintenance of custom infrastructure.
-
-### Actual Experience with Angular
-
-Most of the expected benefits of Angular have been invalidated
-by experience:
-
-* Feedback from new developers is that Angular was a hindrance to
- training, not a benefit. ("One more thing to learn.") Significant
- documentation remains necessary for Open MCT.
-* Expected enhancements to maintainability will be effectively
- invalidated by an expected Angular end-of-life.
-* Data binding and automatic view updates do save development effort,
- but also carry a performance penalty. This can be solved, but requires
- resorting to exactly the sort of DOM manipulations we want to avoid.
- In some cases this can require more total development (writing a
- poorly-performing Angular version, then "optimizing" by rewriting a
- non-Angular version.)
-* Expected reduction of test scope will also be invalidated by an
- expected end-of-life.
-
-Other problems:
-
-* Hinders integrating non-Angular components. (Need to wrap with
- Angular API, e.g. as directives, which may be non-trivial.)
-* Interferes with debugging by swallowing or obscuring exceptions.
-
-# Feedback Review
-
-## Problem Summary
-
-The following attributes of the current API are undesirable:
-
-- [ ] It is difficult to tell "where things are" in the code base.
-- [ ] It is difficult to see how objects are passed around at run-time.
-- [ ] It is difficult to trace flow of control generally.
-- [ ] Multiple interfaces for related concepts (e.g. telemetry) is confusing.
-- [ ] API documentation is missing or not well-formatted for use.
-- [ ] High-level separation of concerns is not made clear.
-- [ ] Interface depth of telemetry API is excessive (esp. `TelemetrySeries`)
-- [ ] Capabilities as a concept lack clarity.
-- [ ] Too many interfaces and concepts to learn.
-- [ ] Exposing third-party APIs (e.g. Angular's) increases the learning curve.
-- [ ] Want more examples, easier-to-use documentation.
-- [ ] UI-relevant features (HTML, CSS) under-documented
-- [ ] Good MVC for views of domain objects not enforced (e.g. plots)
-
-## Positive Features
-
-It is desirable to retain the following features in an API redesign:
-
-- [ ] Creating new features and implementing them additively is well-supported.
-- [ ] Easy to add/remove features which involve multiple concerns.
-- [ ] Features can be self-contained.
-- [ ] Declarative syntax makes it easy to tell what's in use.
-
-## Requirements
-
-The following are considered "must-haves" of any complete API
-redesign:
-
-- [ ] Don't require usage of Angular API.
-- [ ] Don't require support for Angular API.
-
-# Proposals
-
-## RequireJS as dependency injector
-
-Use Require.JS for dependency injection.
-
-Dependencies will then be responsible for being sufficiently
-mutable/extensible/customizable. This can be facilitated by
-adding platform classes which can facilitate the addition
-of reusable components.
-
-Things that we usefully acquire via dependency injection currently:
-
-* Services.
-* Extensions (by category).
-* Configuration constants.
-
-Services would be defined (by whatever component is responsible
-for declaring it) using `define` and the explicit name of the
-service. To allow for the power of composite services, the
-platform would provide a `CompositeService` class that supports
-this process by providing `register`, `decorate`, and `composite`
-methods to register providers, decorators, and aggregators
-respectively. (Note that nomenclature changes are also implied
-here, to map more clearly to the Composite Pattern and to
-avoid the use of the word "provider", which has ambiguity with
-Angular.)
-
-```js
-define(
- "typeService",
- ["CompositeService"],
- function (CompositeService) {
- var typeService = new CompositeService([
- "listTypes",
- "getType"
- ]);
-
- // typeService has `listTypes` and `getType` as methods;
- // at this point they are stubbed (will return undefined
- // or throw or similar) but this will change as
- // decorators/compositors/providers are added.
-
- // You could build in a compositor here, or
- // someone could also define one later
- typeService.composite(function (typeServices) {
- // ... return a TypeService
- });
-
- // Similarly, you could register a default implementation
- // here, or from some other script.
- typeService.register(function (typeService) {
- // ... return a TypeService
- }, { priority: 'default' });
-
- return typeService;
- }
-);
-```
-
-Other code could then register additional `TypeService`
-implementations (or decorators, or even compositors) by
-requiring `typeService` and calling those methods; or, it
-could use `typeService` directly. Priority ordering could
-be utilized by adding a second "options" argument.
-
-For extension categories, you could simply use registries:
-
-```js
-define(
- "typeRegistry",
- ["ExtensionRegistry"],
- function (ExtensionRegistry) {
- return new ExtensionRegistry();
- }
-);
-```
-
-Where `ExtensionRegistry` extends `Array`, and adds a
-`register` method which inserts into the array at some
-appropriate point (e.g. with an options parameter that
-respects priority order.)
-
-This makes unit testing somewhat more difficult when you
-want to mock injected dependencies; there are tools out
-there (e.g. [Squire](https://github.com/iammerrick/Squire.js/))
-which can help with this, however.
-
-### Benefits
-
-* Clarifies "how objects are passed around at run-time";
- answer is always "via RequireJS."
-* Preserves flexibility/power provided by composite services.
-* Lends itself fairly naturally to API documentation via JSDoc
- (as compared to declaring things in bundles, which does not.)
-* Reduces interface complexity for acquiring dependencies;
- one interface for both explicit and "implicit" dependencies,
- instead of separate approaches for static and substitutable
- dependencies.
-* Removes need to understand Angular's DI mechanism.
-* Improves usability of documentation (`typeService` is an
- instance of `CompositeService` and implements `TypeService`
- so you can easily traverse links in the JSDoc.)
-* Can be used more easily from Web Workers, allowing services
- to be used on background threads trivially.
-
-### Detriments
-
-* Having services which both implement the service, and
- have methods for registering the service, is a little
- weird; would be cleaner if these were separate.
- (Mixes concerns.)
-* Syntax becomes non-declarative, which may make it harder to
- understand "what uses what."
-* Allows for ordering problems (e.g. you start using a
- service before everything has been registered.)
-
-## Arbitrary HTML Views
-
-Currently, writing new views requires writing Angular templates.
-This must change if we want to reduce our dependence on Angular.
-
-Instead, propose that:
-
-* What are currently called "views" we call something different.
- (Want the term view to be more like "view" in the MVC sense.)
- * For example, call them "applications."
-* Consolidate what are currently called "representations" and
- "templates", and instead have them be "views".
-
-For parity with actions, a `View` would be a constructor which
-takes an `ActionContext` as a parameter (with similarly-defined
-properties) and exposes a method to retrieve the HTML elements
-associated with it.
-
-The platform would then additionally expose an `AngularView`
-implementation to improve compatibility with existing
-representations, whose usage would something like:
-
-```js
-define(
- ["AngularView"],
- function (AngularView) {
- var template = "<span ng-click='...'>Hello world</span>";
- return new AngularView(template);
- }
-);
-```
-
-The interface exposed by a view is TBD, but should provide at
-least the following:
-
-* A way to get the HTML elements that are exposed by & managed
- by the view.
-* A `destroy` method to detach any listeners at the model level.
-
-Individual views are responsible for managing their resources,
-e.g. listening to domain objects for mutation. To keep DRY, the
-platform should include one or more view implementations that
-can be used/subclassed which handle common behavior(s).
-
-### Benefits
-
-* Using Angular API for views is no longer required.
-* Views become less-coupled to domain objects. Domain objects
- may be present in the `ViewContext`, but this also might
- just be a "view" of some totally different thing.
-* Helps clarify high-level concerns in the API (a View is now
- really more like a View in the MVC sense; although, not
- completely, so this gets double-booked as a detriment.)
-* Having a `ViewContext` that gets passed in allows views to
- be more "contextually aware," which is something that has
- been flagged previously as a UX desire.
-
-### Detriments
-
-* Becomes less clear how views relate to domain objects.
-* Adds another interface.
-* Leaves an open problem of how to distinguish views that
- a user can choose (Plot, Scrolling List) from views that
- are used more internally by the application (tree view.)
-* Views are still not Views in the MVC sense (in practice,
- the will likely be view-controller pairs.) We could call
- them widgets to disambiguate this.
-* Related to the above, even if we called these "widgets"
- it would still fail to enforce good MVC.
-
-## Wrap Angular Services
-
-Wrap Angular's services in a custom interfaces; e.g.
-replace `$http` with an `httpService` which exposes a useful
-subset of `$http`'s functionality.
-
-### Benefits
-
-* Removes a ubiquitous dependency on Angular.
-* Allows documentation for these features to be co-located
- and consistent with other documentation.
-* Facilitates replacing these with non-Angular versions
- in the future.
-
-### Detriments
-
-* Increases the number of interfaces in Open MCT. (Arguably,
- not really, since the same interfaces would exist if exposed
- by Angular.)
-
-## Bundle Declarations in JavaScript
-
-Replace `bundle.json` files (and bundle syntax generally) with
-an imperative form. There would instead be a `Bundle` interface
-which scripts can implement (perhaps assisted by a platform
-class.)
-
-The `bundles.json` file would then be replaced with a `bundles.js`
-or `Bundles.js` that would look something like:
-
-```js
-define(
- [
- 'platform/core/PlatformBundle',
- // ... etc ...
- 'platform/features/plot/PlotBundle'
- ],
- function () {
- return arguments;
- }
-);
-```
-
-Which could in turn be used by an initializer:
-
-```js
-define(
- ['./bundles', 'mct'],
- function (bundles, mct) {
- mct.initialize(bundles);
- }
-);
-```
-
-A `Bundle` would have a constructor that took some JSON object
-(a `BundleContext`, lets say) and would provide methods for
-application life-cycle events. Depending on other choices,
-a dependency injector could be passed in at some appropriate
-life-cycle call (e.g. initialize.)
-
-This would also allow for "composite bundles" which serve as
-proxies for multiple bundles. The `BundleContext` could contain
-(or later be amended to contain) filtering rules to ignore
-other bundles and so forth (this has been useful for administering
-Open MCT in subtly different configurations in the past.)
-
-### Benefits
-
-* Imperative; more explicit, less magic, more clear what is going on.
-* Having a hierarchy of "bundles" could make it easier to navigate
- (relevant groupings can be nested in a manner which is not
- currently well-supported.)
-* Lends itself naturally to a compilation step.
-* Nudges plugin authors to "do your initialization and registration
- in a specific place" instead of mixing in registration of features
- with their implementations.
-
-### Detriments
-
-* Introduces another interface.
-* Loses some of the convenience of having a declarative
- summary of components and their dependencies.
-
-## Pass around a dependency injector
-
-:warning: Note that this is incompatible with the
-[RequireJS as dependency injector](#requirejs-as-dependency-injector)
-proposal.
-
-Via some means (such as in a registration lifecycle event as
-described above) pass a dependency injector to plugins to allow
-for dependencies to be registered.
-
-For example:
-
-```js
-MyBundle.prototype.registration = function (architecture) {
- architecture.service('typeService').register(MyTypeService);
- architecture.extension('actions').register(
- [ 'foo' ],
- function (foo) { return new MyAction(foo); }
- );
-};
-```
-
-### Benefits
-
-* Ensures that registration occurs at an appropriate stage of
- application execution, avoiding start-up problems.
-* Makes registration explicit (generally easier to understand)
- rather than implicit.
-* Encapsulates dependency injection nicely.
-
-### Detriments
-
-* Increases number of interfaces to learn.
-* Syntax likely to be awkward, since in many cases we really
- want to be registering constructors.
-
-## Remove partial constructors
-
-Remove partial constructors; these are confusing. It is hard to
-recognize which constructor arguments are from dependencies, and
-which will be provided at run-time. Instead, it is the responsibility
-of whoever is introducing a component to manage these things
-separately.
-
-### Benefits
-
-* More clarity.
-
-### Detriments
-
-* Possibly results in redundant effort to manage this difference
- (other APIs may need to be adjusted accordingly.)
-
-## Rename Views to Applications
-
-Rename (internally to the application, not necessarily in UI or
-in user guide) what are currently called `views` to `applications`.
-
-### Benefits
-
-* Easier to understand. What is currently called a "view" is,
- in the MVC sense, a view-controller pair, usually with its own
- internal model for view state. Calling these "applications"
- would avoid this ambiguity/inconsistency.
-* Also provides an appropriate mindset for building these;
- particularly, sets the expectation that you'll want to decompose
- this "application" into smaller pieces. This nudges developers
- in appropriate directions (in contrast to `views`, which
- typically get implemented as templates with over-complicated
- "controllers".)
-
-### Detriments
-
-* Developer terminology falls out of sync with what is used in
- the user guide.
-
-## Provide Classes for Extensions
-
-As a general pattern, when introducing extension categories, provide
-classes with a standard implementation of these interfaces that
-plugin developers can `new` and register.
-
-For example, instead of declaring a type as:
-
-```json
-{
- "types": [{
- "key": "sometype",
- "glyph": "X",
- "etc": "..."
- }]
-}
-```
-
-You would register one as:
-
-```js
-// Assume we have gotten a reference to a type registry somehow
-typeRegistry.register(new Type({
- "key": "sometype",
- "glyph": "X",
- "etc": "..."
-}));
-```
-
-### Benefits
-
-* Easier to understand (less "magic").
-* Lends itself naturally to substitution of different implementations
- of the same interface.
-* Allows for run-time decisions about exactly what gets registered.
-
-### Detriments
-
-* Adds some modest boilerplate.
-* Provides more opportunity to "do it wrong."
-
-## Normalize naming conventions
-
-Adopt and obey the following naming conventions for AMD modules
-(and for injectable dependencies, which may end up being modules):
-
-* Use `UpperCamelCase` for classes.
-* Use `lowerCase` names for instances.
- * Use `someNoun` for object instances which implement some
- interface. The noun should match the implemented interface,
- when applicable.
- * `useSomeVerb` for functions.
-* Use `ALL_CAPS_WITH_UNDERSCORES` for other values, including
- "struct-like" objects (that is, where the object conceptually
- contains properties rather than methods.)
-
-### Benefits
-
-* Once familiar with the conventions, easier to understand what
- individual modules are.
-
-### Detriments
-
-* A little bit inflexible.
-
-## Expose no third-party APIs
-
-As a general practice, expose no third-party APIs as part of the
-platform.
-
-For cases where you do want to access third-party APIs directly
-from other scripts, this behavior should be "opt-in" instead of
-mandatory. For instance, to allow addition of Angular templates,
-an Angular-support bundle could be included which provides an
-`AngularView` class, a `controllerRegistry`, et cetera. Importantly,
-such a bundle would need to be kept separate from the platform API,
-or appropriately marked as non-platform in the API docs (an
-`@experimental` tag would be nice here if we feel like extending
-JSDoc.)
-
-### Benefits
-
-* Simplifies learning curve (only one API to learn.)
-* Reduces Angular dependency.
-* Avoids the problems of ubiquitous dependencies generally.
-
-### Detriments
-
-* Increases documentation burden.
-
-## Register Extensions as Instances instead of Constructors
-
-Register extensions as object instances instead of constructors.
-This allows for API flexibility w.r.t. constructor signatures
-(and avoids the need for partial constructors) and additionally
-makes it easier to provide platform implementations of extensions
-that can be used, subclassed, etc.
-
-For instance, instead of taking an `ActionContext` in its
-constructor, an `Action` would be instantiated once and would
-accept appropriate arguments to its methods:
-
-```js
-function SomeAction {
-}
-SomeAction.prototype.canHandle = function (actionContext) {
- // Check if we can handle this context
-};
-SomeAction.prototype.perform = function (actionContext) {
- // Perform this action, in this context
-};
-```
-
-### Benefits
-
-* Reduces scope of interfaces to understand (don't need to know
- what constructor signature to provide for compatibility.)
-
-### Detriments
-
-* Requires refactoring of various types; may result in some
- awkward APIs or extra factory interfaces.
-
-## Remove capability delegation
-
-The `delegation` capability has only been useful for the
-`telemetry` capability, but using both together creates
-some complexity to manage. In practice, these means that
-telemetry views need to go through `telemetryHandler` to
-get their telemetry, which in turn has an awkward API.
-
-This could be resolved by:
-
-* Removing `delegation` as a capability altogether.
-* Reworking `telemetry` capability API to account for
- the possibility of multiple telemetry-providing
- domain objects. (Perhaps just stick `domainObject`
- in as a field in each property of `TelemetryMetadata`?)
-* Move the behavior currently found in `telemetryHandler`
- into the `telemetry` capability itself (either the
- generic version, or a version specific to telemetry
- panels - probably want some distinct functionality
- for each.)
-
-### Benefits
-
-* Reduces number of interfaces.
-* Accounting for the possibility of multiple telemetry objects
- in the `telemetry` capability API means that views using
- this will be more immediately aware of this as a possibility.
-
-### Detriments
-
-* Increases complexity of `telemetry` capability's interface
- (although this could probably be minimized.)
-
-## Nomenclature Change
-
-Instead of presenting Open MCT as a "framework" or
-"platform", present it as an "extensible application."
-
-This is mostly a change for the developer guide. A
-"framework" and a "platform" layer would still be useful
-architecturally, but the plugin developer's mental model
-for this would then be inclined toward looking at defined
-extension points. The underlying extension mechanism could
-still be exposed to retain the overall expressive power of
-the application.
-
-This may subtly influence other design decisions in order
-to match the "extensible application" identity. On a certain
-level, this contradicts the proposal to
-[rename views to applications](#rename-views-to-applications).
-
-### Benefits
-
-* May avoid incurring some of the "framework aversion" that
- is common among JavaScript developers.
-* More accurately describes the application.
-
-### Detriments
-
-* May also be a deterrent to developers who prefer the more
- "green field" feel of developing applications on a useful
- platform.
-
-## Capabilities as Mixins
-
-Change the behavior of capabilities such that they act as
-mixins, adding additional methods to domain objects.
-Checking if a domain object has a `persistence` capability
-would instead be reduced to checking if it has a `persist`
-method.
-
-Mixins would be applied in priority order and filtered for
-applicability by policy.
-
-### Benefits
-
-* Replaces "capabilities" (which, as a concept, can be hard
- to grasp) with a more familiar "mixins" concept, which has
- been used more generally across many languages.
-* Reduces interface depth.
-
-### Detriments
-
-* Requires checking for the interface exposed by a domain
- object. Alternately, could use `instanceof`, but would
- need to take care to ensure that the prototype chain of
- the domain object is sufficient to do this (which may
- enforce awkward or non-obvious constraints on the way these
- mixins are implemented.)
-* May complicate documentation; understanding the interface
- of a given domain object requires visiting documentation
- for various mixins.
-
-## Remove Applies-To Methods
-
-Remove all `appliesTo` static methods and replace them with
-appropriate policy categories.
-
-### Benefits
-
-* Reduces sizes of interfaces. Handles filtering down sets
- of extensions in a single consistent way.
-
-### Detriments
-
-* Mixes formal applicability with policy; presently, `appliesTo`
- is useful for cases where a given extension cannot, even in
- principle, be applied in a given context (e.g. a domain object
- model is missing the properties which describe the behavior),
- whereas policy is useful for cases where applicability is
- being refined for business or usability reasons. Colocating
- the former with the extension itself has some benefits
- (exhibits better cohesion.)
- * This could be mitigated in the proposed approach by locating
- `appliesTo`-like policies in the same bundle as the relevant
- extension.
-
-## Revise Telemetry API
-
-Revise telemetry API such that:
-
-* `TelemetrySeries` is replaced with arrays of JavaScript objects
- with properties.
-* It is no longer necessary to use `telemetryHandler` (plays well
- with proposal to
- [remove capability delegation](#remove-capability delegation))
-* Change `request` call to take a callback, instead of returning
- a promise. This allows that callback to be invoked several
- times (e.g. for progressive loading, or to reflect changes from
- the time conductor.)
-
-Should also consider:
-
-* Merge `subscribe` functionality into `request`; that is, handle
- real-time data as just another thing that triggers the `request`
- callback.
-* Add a useful API to telemetry metadata, allowing things like
- formats to be retrieved directly from there.
-
-As a consequence of this, `request` would need to return an object
-representing the active request. This would need to be able to
-answer the following questions and provide the following behavior:
-
-* Has the request been fully filled? (For cases like progressive
- loading?)
-* What data has changed since the previous callback? (To support
- performance optimizations in plotting; e.g. append real-time
- data.)
-* Stop receiving updates for this request.
-* Potentially, provide utility methods for dealing with incoming
- data from the request.
-
-Corollary to this, some revision of `TelemetryMetadata` properties
-may be necessary to fully and usably describe the contents of
-a telemetry series.
-
-### Benefits
-
-* Reduces interface depth.
-* Reduces interface size (number of methods.)
-* Supports a broader range of behaviors (e.g. progressive loading)
- within the same interface.
-
-### Detriments
-
-* Merging with `subscribe` may lose the clarity/simplicity of the
- current API.
-
-## Allow Composite Services to Fail Gracefully
-
-Currently, when no providers are available for a composite service
-that is depended-upon, dependencies cannot be resolved and the
-application fails to initialize, with errors appearing in the
-developer console.
-
-This is acceptable behavior for truly unrecoverable missing
-dependencies, but in many cases it would be preferable to allow a
-given type of composite service to define some failure behavior
-when no service of an appropriate type is available.
-
-To address this:
-
-* Provide an interface (preferably
- [imperative](#bundle-Declarations-in-javascript))
- for declaring composite services, independent of any implementation
- of an aggregator/decorator/provider. This allows the framework
- layer to distinguish between unimplemented dependencies (which
- could have defined failover strategies) from undefined dependencies
- (which cannot.)
-* Provide a default strategy for service composition that picks
- the highest-priority provider, and logs an error (and fails to
- satisfy) if no providers have been defined.
-* Allow this aggregation strategy to be overridden, much as one
- can declare aggregators currently. However, these aggregators should
- get empty arrays when no providers have been registered (instead of
- being ignored), at which point they can decide how to handle this
- situation (graceful failure when it's possible, noisy errors when
- it is not.)
-
-### Benefits
-
-* Allows for improved robustness and fault tolerance.
-* Makes service declarations explicit, reducing "magic."
-
-### Detriments
-
-* Requires the inclusion of software units which define services,
- instead of inferring their existence (slight increase in amount
- of code that needs to be written.)
-* May result in harder-to-understand errors when overridden
- composition strategies do not failover well (that is, when they
- do need at least implementation, but fail to check for this.)
-
-## Plugins as Angular Modules
-
-Do away with the notion of bundles entirely; use Angular modules
-instead. Registering extensions or components of composite services
-would then be handled by configuring a provider; reusable classes
-could be exposed by the platform for these.
-
-Example (details are flexible, included for illustrative purposes):
-
-```javascript
-var mctEdit = angular.module('mct-edit', ['ng', 'mct']);
-
-// Expose a new extension category
-mctEdit.provider('actionRegistry', ExtensionCategoryProvider);
-
-// Expose a new extension
-mctEdit.config(['actionRegistryProvider', function (arp) {
- arp.register(EditPropertiesAction);
-}])
-
-return mctEdit;
-```
-
-Incompatible with proposal to
-(expose no third-party APIs)[#expose-no-third-party-apis]; Angular
-API would be ubiquitously exposed.
-
-This is a more specific variant of
-(Bundle Declarations in JavaScript)[#bundle-declarations-in-javascript].
-
-### Benefits
-
-* Removes a whole category of API (bundle definitions), reducing
- learning curve associated with the software.
-* Closer to Angular style, reducing disconnect between learning
- Angular and learning Open MCT (reducing burden of having
- to learn multiple paradigms.)
-* Clarifies "what can be found where" (albeit not perfectly)
- since you can look to module dependencies and follow back from there.
-
-### Detriments
-
-* Hardens dependency on Angular.
-* Increases depth of understanding required of Angular.
-* Increases amount of boilerplate (since a lot of this has
- been short-handed by existing framework layer.)
-
-## Contextual Injection
-
-For extensions that depend on certain instance-level run-time
-properties (e.g. actions or views which use objects and/or specific
-capabilities of those objects), declare these features as dependencies
-and expose them via dependency injection. (AngularJS does this for
-`$scope` in the context of controllers, for example.)
-
-A sketch of an implementation for this might look like:
-
-```js
-function ExtensionRegistry($injector, extensions, getLocals) {
- this.$injector = $injector;
- this.extensions = extensions;
- this.getLocals = getLocals;
-}
-ExtensionRegistry.prototype.get = function () {
- var $injector = this.$injector,
- locals = this.getLocals.apply(null, arguments);
- return this.extensions.filter(function (extension) {
- return depsSatisfiable(extension, $injector, locals);
- }).map(function (extension) {
- return $injector.instantiate(extension, locals);
- });
-};
-
-
-function ExtensionRegistryProvider(getLocals) {
- this.getLocals = getLocals || function () { return {}; };
- this.extensions = [];
-}
-ExtensionRegistryProvider.prototype.register = function (extension) {
- this.extensions.push(extension);
-};
-ExtensionRegistryProvider.prototype.$get = ['$injector', function ($injector) {
- return new ExtensionRegistry($injector, this.extensions, this.getLocals);
-}];
-```
-
-Extension registries which need to behave context-sensitively could
-subclass this to describe how these contextual dependencies are satisfied
-(for instance, by returning various capability properties in `getLocals`).
-
-Specific extensions could then declare dependencies as appropriate to the
-registry they are using:
-
-```js
-app.config(['actionRegistryProvider', function (arp) {
- arp.register(['contextCapability', 'domainObject', RemoveAction]);
-}]);
-```
-
-### Benefits
-
-* Allows contextual dependencies to be fulfilled in the same (or similar)
- manner as global dependencies, increasing overall consistency of API.
-* Clarifies dependencies of individual extensions (currently, extensions
- themselves or policies generally need to imperatively describe what
- dependencies will be used in order to filter down to applicable
- extensions.)
-* Factors out some redundant code from relevant extensions; actions,
- for instance, no longer need to interpret an `ActionContext` object.
- Instead, their constructors take inputs that are more relevant to
- their behavior.
-* Removes need for partial construction, as any arguments which would
- normally be appended after partialization can instead be declared as
- dependencies. Constructors in general become much less bound to the
- specifics of the platform.
-
-### Detriments
-
-* Slightly increases dependency on Angular; other dependency injectors
- may not offer comparable ways to specify dependencies non-globally.
-* Not clear (or will take effort to make clear) which dependencies are
- available for which extensions. Could be mitigated by standardizing
- descriptions of context across actions and views, but that may offer
- its own difficulties.
-* May seem counter-intuitive coming from "vanilla" AngularJS, where
- `$scope` is the only commonly-used context-sensitive dependency.
-
-## Add new abstractions for actions
-
-Originally suggested in
-[this comment](https://github.com/nasa/openmctweb/pull/69#issuecomment-156199991):
-
-> I think there are some grey areas with actions: are they all directly
-tied to user input? If so, why do they have any meaning in the back end?
-Maybe we should look at different abstractions for actions:
-
-> * `actions` - the basic implementation of an action, essentially a
- function declaration. for example, `copy` requires arguments of
- `object` and a `target` to place the object in. at this level,
- it is reusable in a CLI.
-> * `context menu actions` - has criteria for what it applies to.
- when it is visible, and defines how to get extra > input from a
- user to complete that action. UI concern only.
-> * `gesture-handler` - allows for mapping a `gesture` to an action,
- e.g. drag and drop for link. UI Concern only.
-
-> We could add context menu actions for domain objects which navigate
-to that object, without having to implement an action that has no real
-usage on a command line / backend.
-
-### Benefits
-
-* Clearly delineates concerns (UI versus model)
-
-### Detriments
-
-* Increases number of interfaces.
-
-## Add gesture handlers
-
-See [Add new abstractions for actions](#add-new-abstractions-for-actions);
-adding an intermediary between gestures and the actions that they
-trigger could be useful in separating concerns, and for more easily
-changing mappings in a mobile context.
-
-### Benefits
-
-* Clearly decouples UI concerns from the underlying model changes
- they initiate.
-* Simplifies and clarifies mobile support.
-
-### Detriments
-
-* Increases number of interfaces.
-
-# Decisions
-
-After review on Dec. 8, 2015, team consensus on these proposals is
-as follows:
-
-Proposal | @VWoeltjen | @larkin | @akhenry | Consensus
-----|:---:|:---:|:---:|:---:
-RequireJS as dependency injector | :-1: | :neutral_face: :question: | [:-1:](https://github.com/nasa/openmctweb/pull/69#discussion_r44349731) | [:question:](https://github.com/nasa/openmctweb/issues/461)
-Arbitrary HTML Views | :+1: | :+1: | | [:+1: <sup>1</sup>](https://github.com/nasa/openmctweb/issues/463)
-Wrap Angular Services | :-1: | [:-1:](https://github.com/nasa/openmctweb/pull/69#discussion_r43801221) | [:-1:](https://github.com/nasa/openmctweb/pull/69#discussion_r44355057) | :no_entry_sign:
-Bundle Declarations in JavaScript | :+1: | :neutral_face: :question: | | [:+1:](https://github.com/nasa/openmctweb/issues/450)
-Pass around a dependency injector | :-1: | :-1: | | :-1:
-Remove partial constructors | :+1: | :+1: | | [:+1:](https://github.com/nasa/openmctweb/issues/462)
-Rename Views to ~~Applications~~ | :+1: | :neutral_face: :question: | | [:+1: <sup>2</sup>](https://github.com/nasa/openmctweb/issues/463)
-Provide Classes for Extensions | :+1: | :+1: | | [:+1:](https://github.com/nasa/openmctweb/issues/462)
-Normalize naming conventions | :+1: | :+1: | | :+1:
-Expose no third-party APIs | :+1: &ast; | [:-1:](https://github.com/nasa/openmctweb/pull/69#discussion_r43801221) | [:+1:](https://github.com/nasa/openmctweb/pull/69#discussion_r43801221) &dagger; | :+1: <sup>3</sup>
-Register Extensions as Instances instead of Constructors | :+1: | :-1: | | [:+1:](https://github.com/nasa/openmctweb/issues/462)
-Remove capability delegation | :+1: | :+1: | | [:+1:](https://github.com/nasa/openmctweb/issues/463)
-Nomenclature Change | :+1: | [:+1:](https://github.com/nasa/openmctweb/issues/229#issuecomment-153453035) | | :white_check_mark: &Dagger;
-Capabilities as Mixins | | :+1: | [:+1:](https://github.com/nasa/openmctweb/pull/69#discussion_r44355473) | [:question: <sup>4</sup>](https://github.com/nasa/openmctweb/issues/463)
-Remove Applies-To Methods | | :-1: | | :-1:
-Revise Telemetry API | :+1: | :+1: | | [:+1: <sup>5</sup>](https://github.com/nasa/openmctweb/issues/463)
-Allow Composite Services to Fail Gracefully | :+1: | :-1: | | [:+1: <sup>6</sup>](https://github.com/nasa/openmctweb/issues/463)
-Plugins as Angular Modules | :+1: | :neutral_face: :question: | | [:question:](https://github.com/nasa/openmctweb/issues/461)
-Contextual Injection | | :-1: | | [:question:](https://github.com/nasa/openmctweb/issues/461)
-Add new abstractions for actions | [:-1:](https://github.com/nasa/openmctweb/pull/69#issuecomment-158172485) :question: | :+1: | | :-1:
-Add gesture handlers | :+1: | :+1: :question: | | [:+1:](https://github.com/nasa/openmctweb/issues/463)
-
-&ast; Excepting Angular APIs. Internally, continue to use code style
-where classes are declared separately from their registration, such
-that ubiquity of Angular dependency is minimized.
-
-&dagger; "I think we should limit the third party APIs we expose to
-one or two, but I worry it might be counterproductive to
-completely hide them."
-
-&Dagger; Some ambiguity about what to call ourselves if not a platform,
-but general agreement that "platform" is not a good term.
-More Detail on Pete's Opinions Here:
-https://github.com/nasa/openmctweb/blob/api-redesign/docs/src/design/proposals/APIRedesign_PeteRichards.md#notes-on-current-api-proposals
-
-<sup>1</sup> Needs to be designed carefully; don't want to do this with
-a complicated interface, needs to be significantly simpler than wrapping
-with an Angular directive would be.
-
-<sup>2</sup> Agree that we need a new name, but it should not be "application"
-
-<sup>3</sup> Don't want to expose (or require usage of) third-party
-APIs generally. Angular may be an exception in the sense that it is an
-API we presume to be present. Can use third-party APIs internally, but
-don't want to support them or be tied to them.
-
-<sup>4</sup> Want to have a separate spin-off discussion about
-capabilities. Want to consider several alternatives here.
-At minimum, though, mixins would be an improvement relative
-to how these are currently handled.
-
-<sup>5</sup> Agree we want to revise APIs, but this should
-be a larger spin-off.
-
-<sup>6</sup> Not necessarily as described, but expected to be a
-property of composite services in whatever formulation they
-take. Should not be default behavior.
-
-
-[Additional proposals](APIRedesign_PeteRichards.md) considered:
-
-Proposal | Consensus
-------|------
-Imperative component registries | [:+1:](https://github.com/nasa/openmctweb/issues/462)
-Get rid of "extension category" concept. | [:+1:](https://github.com/nasa/openmctweb/issues/462)
-Reduce number and depth of extension points | :+1:
-Composite services should not be the default | [:question:](https://github.com/nasa/openmctweb/issues/463)
-Get rid of views, representations, and templates. | [:+1: <sup>1</sup>](https://github.com/nasa/openmctweb/issues/463)
-More angular: for all services | [:question:](https://github.com/nasa/openmctweb/issues/461)
-Less angular: only for views | [:question:](https://github.com/nasa/openmctweb/issues/461)
-Use systemjs for module loading | [:+1: <sup>2</sup>](https://github.com/nasa/openmctweb/issues/459)
-Use gulp or grunt for standard tooling | [:+1:](https://github.com/nasa/openmctweb/issues/459)
-Package openmctweb as single versioned file. | [:+1:](https://github.com/nasa/openmctweb/issues/458)
-Refresh on navigation | [:+1: <sup>3</sup>](https://github.com/nasa/openmctweb/issues/463)
-Move persistence adapter to promise rejection. | [:+1:](https://github.com/nasa/openmctweb/issues/463)
-Remove bulk requests from providers | [:+1: <sup>4</sup>](https://github.com/nasa/openmctweb/issues/463)
-
-<sup>1</sup> Need to agree upon details at design-time, but
-basic premise is agreed-upon - want to replace
-views/representations/templates with a common abstraction
-(and hoist out the non-commonalities to other places as appropriate)
-
-<sup>2</sup> Beneficial but not strictly necessary (may be
-lower-effort alternatives); should prioritize accordingly during planning
-
-<sup>3</sup> Some effort will be required to make all of the state
-that needs to persist among route changes actually be persistent.
-Will want to address this at design-time (will want to look at
-libraries to simplify this, for instance)
-
-<sup>4</sup> Maybe not all providers, but anywhere there is not a
-strong case for building batching into the API we should prefer
-simplicity. (Want to pay specific attention to telemetry here.)
diff --git a/docs/src/design/proposals/APIRedesign_PeteRichards.md b/docs/src/design/proposals/APIRedesign_PeteRichards.md
deleted file mode 100644
index 6e3a94310..000000000
--- a/docs/src/design/proposals/APIRedesign_PeteRichards.md
+++ /dev/null
@@ -1,251 +0,0 @@
-<!-- START doctoc generated TOC please keep comment here to allow auto update -->
-<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
-**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
-
-- [Reducing interface depth (the bundle.json version)](#reducing-interface-depth-the-bundlejson-version)
- - [Imperative component registries](#imperative-component-registries)
- - [Get rid of "extension category" concept.](#get-rid-of-extension-category-concept)
- - [Reduce number and depth of extension points](#reduce-number-and-depth-of-extension-points)
- - [Composite services should not be the default](#composite-services-should-not-be-the-default)
- - [Get rid of views, representations, and templates.](#get-rid-of-views-representations-and-templates)
-- [Reducing interface depth (The angular discussion)](#reducing-interface-depth-the-angular-discussion)
- - [More angular: for all services](#more-angular-for-all-services)
- - [Less angular: only for views](#less-angular-only-for-views)
-- [Standard packaging and build system](#standard-packaging-and-build-system)
- - [Use systemjs for module loading](#use-systemjs-for-module-loading)
- - [Use gulp or grunt for standard tooling](#use-gulp-or-grunt-for-standard-tooling)
- - [Package openmctweb as single versioned file.](#package-openmctweb-as-single-versioned-file)
-- [Misc Improvements](#misc-improvements)
- - [Refresh on navigation](#refresh-on-navigation)
- - [Move persistence adapter to promise rejection.](#move-persistence-adapter-to-promise-rejection)
- - [Remove bulk requests from providers](#remove-bulk-requests-from-providers)
-- [Notes on current API proposals:](#notes-on-current-api-proposals)
-- [[1] Footnote: The angular debacle](#1-footnote-the-angular-debacle)
- - ["Do or do not, there is no try"](#do-or-do-not-there-is-no-try)
- - [A lack of commitment](#a-lack-of-commitment)
- - [Commitment is good!](#commitment-is-good)
-
-<!-- END doctoc generated TOC please keep comment here to allow auto update -->
-
-
-# Reducing interface depth (the bundle.json version)
-
-## Imperative component registries
-
-Transition component registries to javascript, get rid of bundle.json and bundles.json. Prescribe a method for application configuration, but allow flexibility in how application configuration is defined.
-
-Register components in an imperative fashion, see angularApp.factory, angularApp.controller, etc. Alternatively, implement our own application object with new registries and it's own form of registering objects.
-
-## Get rid of "extension category" concept.
-
-The concept of an "extension category" is itself an extraneous concept-- an extra layer of interface depth, an extra thing to learn before you can say "hello world". Extension points should be clearly supported and documented with whatever interfaces make sense. Developers who wish to add something that is conceptually equivalent to an extension category can do so directly, in the manner that suites their needs, without us forcing a common method on them.
-
-## Reduce number and depth of extension points
-
-Clearly specify supported extension points (e.g. persistence, model providers, telemetry providers, routes, time systems), but don't claim that the system has a clear and perfect repeatable solution for unknown extension types. New extension categories can be implemented in whatever way makes sense, without prescribing "the one and only system for managing extensions".
-
-The underlying problem here is we are predicting needs for extension points where none exist-- if we try and design the extension system before we know how it is used, we design the wrong thing and have to rewrite it later.
-
-## Composite services should not be the default
-
-Understanding composite services, and describing services as composite services can confuse developers. Aggregators are implemented once and forgotten, while decorators tend to be hacky, brittle solutions that are generally needed to avoid circular imports. While composite services are a useful construct, it reduces interface depth to implement them as registries + typed providers.
-
-You can write a provider (provides "thing x" for "inputs y") with a simple interface. A provider has two or more methods:
-* a method which takes "inputs y" and returns True if it knows how to provide "thing x", false otherwise.
-* one or more methods which provide "thing x" for objects of "inputs y".
-
-Actually checking whether a provider can respond to a request before asking it to do work allows for faster failure and clearer errors when no providers match the request.
-
-## Get rid of views, representations, and templates.
-
-Templates are an implementation detail that should be handled by module loaders. Views and representations become "components," and a new concept, "routes", is used to exposing specific views to end users.
-
-`components` - building blocks for views, have clear inputs and outputs, and can be coupled to other components when it makes sense. (e.g. parent-child components such as menu and menu item), but should have ZERO knowledge of our data models or telemetry apis. They should define data models that enable them to do their job well while still being easy to test.
-
-`routes` - a view type for a given domain object, e.g. a plot, table, display layout, etc. Can be described as "whatever shows in the main screen when you are viewing an object." Handle loading of data from a domain object and passing that data to the view components. Routes should support editing as it makes sense in their own context.
-
-To facilitate testing:
-
-* routes should be testable without having to test the actual view.
-* components should be independently testable with zero knowledge of our data models or telemetry APIs.
-
-Component code should be organized side by side, such as:
-
-```
-app
-|- components
- |- productDetail
- | |- productDetail.js
- | |- productDetail.css
- | |- productDetail.html
- | |- productDetailSpec.js
- |- productList
- |- checkout
- |- wishlist
-```
-
-Components are not always reusable, and we shouldn't be overly concerned with making them so. If components are heavily reused, they should either be moved to a platform feature (e.g. notifications, indicators), or broken off as an external dependency (e.g. publish mct-plot as mct-plot.js).
-
-
-# Reducing interface depth (The angular discussion)
-
-Two options here: use more angular, use less angular. Wrapping angular methods does not reduce interface depth and must be avoided.
-
-The primary issue with angular is duplications of concerns-- both angular and the openmctweb platform implement the same tools side by side and it can be hard to comprehend-- it increases interface depth. For other concerns, see footnotes[1].
-
-Wrapping angular methods for non-view related code is confusing to developers because of the random constraints angular places on these items-- developers ultimately have to understand both angular DI and our framework. For example, it's not possible to name the topic service "topicService" because angular expects Services to be implemented by Providers, which is different than our expectation.
-
-To reduce interface depth, we can replace our own provider and registry patterns with angular patterns, or we can only utilize angular view logic, and only use our own DI patterns.
-
-## More angular: for all services
-
-Increasing our commitment to angular would mean using more of the angular factories, services, etc, and less of our home grown tools. We'd implement our services and extension points as angular providers, and make them configurable via app.config.
-
-As an example, registering a specific type of model provider in angular would look like:
-
-```javascript
-mct.provider('model', modelProvider() { /* implementation */});
-
-mct.config(['modelProvider', function (modelProvider) {
- modelProvider.providers.push(RootModelProvider);
-}]);
-```
-
-## Less angular: only for views
-
-If we wish to use less angular, I would recommend discontinuing use of all angular components that are not view related-- services, factories, $http, etc, and implementing them in our own paradigm. Otherwise, we end up with layered interfaces-- one of the goals we would like to avoid.
-
-
-# Standard packaging and build system
-
-Standardize the packaging and build system, and completely separate the core platform from deployments. Prescribe a starting point for deployments, but allow flexibility.
-
-## Use systemjs for module loading
-
-Allow developers to use whatever module loading system they'd like to use, while still supporting all standard cases. We should also use this system for loading assets (css, scss, html templates), which makes it easier to implement a single file deployment using standard build tooling.
-
-## Use gulp or grunt for standard tooling
-
-Using gulp or grunt as a task runner would bring us in line with standard web developer workflows and help standardize rendering, deployment, and packaging. Additional tools can be added to the workflow at low cost, simplifying the setup of developer environments.
-
-Gulp and grunt provide useful developer tooling such as live reload, automatic scss/less/etc compilation, and ease of extensibility for standard production build processes. They're key in decoupling code.
-
-## Package openmctweb as single versioned file.
-
-Deployments should depend on a specific version of openmctweb, but otherwise be allowed to have their own deployment and development toolsets.
-
-Customizations and deployments of openmctweb should not use the same build tooling as the core platform; instead they should be free to use their own build tools as they wish. (We would provide a template for an application, based on our experience with warp-for-rp and vista)
-
-Installation and utilization of openmctweb should be as simple as downloading the js file, including it in your own html page, and then initializing an app and running it. If a developer would prefer, they could use bower or npm to handle installation.
-
-Then, if we're using imperative methods for extending the application we can use the following for basic customization:
-
-```html
-<script src="//localhost/openmctweb.js"></script>
-<script>
-// can configure from object
-var myApp = new OpenMCTWeb({
- persistence: {
- providers: [
- {
- type: 'elastic',
- uri: 'http://someElasticHost/'
- } // ...
- ]
- }
-});
-
-// alternative configurations
-myApp.persistence.addProvider(MyPersistenceAdapter);
-myApp.model.addProvider(someProviderObject);
-
-// Removing via method
-myApp.persistence.removeProvider('some method for removing functionality');
-// directly mutating providers
-myApp.persistence.providers = [ThisProviderStandsAlone];
-//
-myApp.run();
-</script>
-```
-
-This packaging reduces the complexity of managing multiple deployed versions, and also allows us to provide users with incredibly simple tutorials-- they can use whatever tooling they like. For instance, a hello world tutorial may take the option of "exposing a new object in the tree".
-
-```javascript
-var myApp = new OpenMCTWeb();
-myApp.roots.addRoot({
- id: 'myRoot',
- name: 'Hello World!',
-});
-myApp.routes.setDefault('myRoot');
-myApp.run();
-```
-
-# Misc Improvements
-
-## Refresh on navigation
-In cases where navigation events change the entire screen, we should be using routes and location changes to navigate between objects. We should be using href for all navigation events.
-
-At the same time, navigating should refresh state of every visible object. A properly configured persistence store will handle caching with standard cache headers and 304 not modified responses, which will provide good performance of object reloads, while helping us ensure that objects are always in sync between clients.
-
-View state (say, the expanded tree nodes) should not be tied to caching of data-- it should be something we intentionally persist and restore with each navigation. Data (such as object definitions) should be reloaded from server as necessary to restore state.
-
-## Move persistence adapter to promise rejection.
-Simple: reject on fail, resolve on success.
-
-## Remove bulk requests from providers
-
-Aggregators can request multiple things at once, but individual providers should only have to implement handling at the level of a single request. Each provider can implement it's own internal batching, but it should support making requests at a finer level of detail.
-
-Excessive wrapping of code with $q.all causes additional digest cycles and decreased performance.
-
-For example, instead of every telemetry provider responding to a given telemetry request, aggregators should route each request to the first provider that can fulfill that request.
-
-
-# Notes on current API proposals:
-
-* [RequireJS for Dependency Injection](https://github.com/nasa/openmctweb/blob/api-redesign/docs/src/design/proposals/APIRedesign.md#requirejs-as-dependency-injector): requires other topics to be discussed first.
-* [Arbitrary HTML Views](https://github.com/nasa/openmctweb/blob/api-redesign/docs/src/design/proposals/APIRedesign.md#arbitrary-html-views): think there is a place for it, requires other topics to be discussed first.
-* [Wrap Angular Services](https://github.com/nasa/openmctweb/blob/api-redesign/docs/src/design/proposals/APIRedesign.md#wrap-angular-services): No, this is bad.
-* [Bundle definitions in Javascript](https://github.com/nasa/openmctweb/blob/api-redesign/docs/src/design/proposals/APIRedesign.md#bundle-declarations-in-javascript): Points to a solution, but ultimately requires more discussion.
-* [pass around a dependency injector](https://github.com/nasa/openmctweb/blob/api-redesign/docs/src/design/proposals/APIRedesign.md#pass-around-a-dependency-injector): No.
-* [remove partial constructors](https://github.com/nasa/openmctweb/blob/api-redesign/docs/src/design/proposals/APIRedesign.md#remove-partial-constructors): Yes, this should be superseded by another proposal though. The entire concept was a messy solution to dependency injection issues caused by declarative syntax.
-* [Rename views to applications](https://github.com/nasa/openmctweb/blob/api-redesign/docs/src/design/proposals/APIRedesign.md#rename-views-to-applications): Points to a problem that needs to be solved but I think the name is bad.
-* [Provide classes for extensions](https://github.com/nasa/openmctweb/blob/api-redesign/docs/src/design/proposals/APIRedesign.md#provide-classes-for-extensions): Yes, in specific places
-* [Normalize naming conventions](https://github.com/nasa/openmctweb/blob/api-redesign/docs/src/design/proposals/APIRedesign.md#normalize-naming-conventions): Yes.
-* [Expose no third-party APIs](https://github.com/nasa/openmctweb/blob/api-redesign/docs/src/design/proposals/APIRedesign.md#expose-no-third-party-apis): Completely disagree, points to a real problem with poor angular integration.
-* [Register Extensions as Instances instead of Constructors](https://github.com/nasa/openmctweb/blob/api-redesign/docs/src/design/proposals/APIRedesign.md#register-extensions-as-instances-instead-of-constructors): Superseded by the fact that we should not hope to implement a generic construct.
-* [Remove capability delegation](https://github.com/nasa/openmctweb/blob/api-redesign/docs/src/design/proposals/APIRedesign.md#remove-capability-delegation): Yes.
-* [Nomenclature Change](https://github.com/nasa/openmctweb/blob/api-redesign/docs/src/design/proposals/APIRedesign.md#nomenclature-change): Yes, hope to discuss the implications of this more clearly in other proposals.
-* [Capabilities as mixins](https://github.com/nasa/openmctweb/blob/api-redesign/docs/src/design/proposals/APIRedesign.md#capabilities-as-mixins): Yes.
-* [Remove appliesTo methods](https://github.com/nasa/openmctweb/blob/api-redesign/docs/src/design/proposals/APIRedesign.md#remove-applies-to-methods): No-- I think some level of this is necessary. I think a more holistic approach to policy is needed. it's a rather complicated system.
-* [Revise telemetry API](https://github.com/nasa/openmctweb/blob/api-redesign/docs/src/design/proposals/APIRedesign.md#revise-telemetry-api): If we can rough out and agree to the specifics, then Yes. Needs discussion.
-* [Allow composite services to fail gracefully](https://github.com/nasa/openmctweb/blob/api-redesign/docs/src/design/proposals/APIRedesign.md#allow-composite-services-to-fail-gracefully): No. As mentioned above, I think composite services themselves should be eliminated for a more purpose bound tool.
-* [Plugins as angular modules](https://github.com/nasa/openmctweb/blob/api-redesign/docs/src/design/proposals/APIRedesign.md#plugins-as-angular-modules): Should we decide to embrace Angular completely, I would support this. Otherwise, no.
-* [Contextual Injection](https://github.com/nasa/openmctweb/blob/api-redesign/docs/src/design/proposals/APIRedesign.md#contextual-injection): No, don't see a need.
-* [Add New Abstractions for Actions](https://github.com/nasa/openmctweb/blob/api-redesign/docs/src/design/proposals/APIRedesign.md#add-new-abstractions-for-actions): Worth a discussion.
-* [Add gesture handlers](https://github.com/nasa/openmctweb/blob/api-redesign/docs/src/design/proposals/APIRedesign.md#add-gesture-handlers): Yes if we can agree on details. We need a platform implementation that is easy to use, but we should not reinvent the wheel.
-
-
-
-# [1] Footnote: The angular debacle
-
-## "Do or do not, there is no try"
-
-A commonly voiced concern of embracing angular is the possibility of becoming dependent on a third party framework. This concern is itself detrimental-- if we're afraid of becoming dependent on a third party framework, then we will do a bad job of using the framework, and inevitably will want to stop using it.
-
-If we're using a framework, we need to use it fully, or not use it at all.
-
-## A lack of commitment
-
-A number of the concerns we heard from developers and interns can be attributed to the tenuous relationship between the OpenMCTWeb platform and angular. We claimed to be angular, but we weren't really angular. Instead, we are caught between our incomplete framework paradigm and the angular paradigm. In many cases we reinvented the wheel or worked around functionality that angular provides, and ended up in a more confusing state.
-
-## Commitment is good!
-
-We could just be an application that is built with angular.
-
-An application that is modular and extensible not because it reinvents tools for providing modularity and extensibility, but because it reuses existing tools for modularity and extensibility.
-
-There are benefits to buying into the angular paradigm: shift documentation burden to external project, engage a larger talent pool available both as voluntary open source contributors and as experienced developers for hire, and gain access to an ecosystem of tools that we can use to increase the speed of development.
-
-There are negatives too: Angular is a monolith, it has performance concerns, and an unclear future. If we can't live with it, we should look at alternatives.
-
diff --git a/docs/src/design/proposals/ImperativePlugins.md b/docs/src/design/proposals/ImperativePlugins.md
deleted file mode 100644
index 1fbb83b1b..000000000
--- a/docs/src/design/proposals/ImperativePlugins.md
+++ /dev/null
@@ -1,164 +0,0 @@
-# Imperative Plugins
-
-This is a design proposal for handling
-[bundle declarations in JavaScript](
-APIRedesign.md#bundle-declarations-in-javascript).
-
-## Developer Use Cases
-
-Developers will want to use bundles/plugins to (in rough order
-of occurrence):
-
-1. Add new extension instances.
-2. Use existing services
-3. Add new service implementations.
-4. Decorate service implementations.
-5. Decorate extension instances.
-6. Add new types of services.
-7. Add new extension categories.
-
-Notably, bullets 4 and 5 above are currently handled implicitly,
-which has been cited as a source of confusion.
-
-## Interfaces
-
-Two base classes may be used to satisfy these use cases:
-
- * The `CompositeServiceFactory` provides composite service instances.
- Decorators may be added; the approach used for compositing may be
- modified; and individual services may be registered to support compositing.
- * The `ExtensionRegistry` allows for the simpler case where what is desired
- is an array of all instances of some kind of thing within the system.
-
-Note that additional developer use cases may be supported by using the
-more general-purpose `Registry`
-
-```nomnoml
-[Factory.<T, V>
- |
- - factoryFn : function (V) : T
- |
- + decorate(decoratorFn : function (T, V) : T, options? : RegistrationOptions)
-]-:>[function (V) : T]
-
-[RegistrationOptions |
- + priority : number or string
-]
-
-[Registry.<T, V>
- |
- - compositorFn : function (Array.<T>) : V
- |
- + register(item : T, options? : RegistrationOptions)
- + composite(compositorFn : function (Array.<T>) : V, options? : RegistrationOptions)
-]-:>[Factory.<V, Void>]
-[Factory.<V, Void>]-:>[Factory.<T, V>]
-
-[ExtensionRegistry.<T>]-:>[Registry.<T, Array.<T>>]
-[Registry.<T, Array.<T>>]-:>[Registry.<T, V>]
-
-[CompositeServiceFactory.<T>]-:>[Registry.<T, T>]
-[Registry.<T, T>]-:>[Registry.<T, V>]
-```
-
-## Examples
-
-### 1. Add new extension instances.
-
-```js
-// Instance-style registration
-mct.types.register(new mct.Type({
- key: "timeline",
- name: "Timeline",
- description: "A container for activities ordered in time."
-});
-
-// Factory-style registration
-mct.actions.register(function (domainObject) {
- return new RemoveAction(domainObject);
-}, { priority: 200 });
-```
-
-### 2. Use existing services
-
-```js
-mct.actions.register(function (domainObject) {
- var dialogService = mct.ui.dialogServiceFactory();
- return new PropertiesAction(dialogService, domainObject);
-});
-```
-
-### 3. Add new service implementations
-
-```js
-// Instance-style registration
-mct.persistenceServiceFactory.register(new LocalPersistenceService());
-
-// Factory-style registration
-mct.persistenceServiceFactory.register(function () {
- var $http = angular.injector(['ng']).get('$http');
- return new LocalPersistenceService($http);
-});
-```
-
-### 4. Decorate service implementations
-
-```js
-mct.modelServiceFactory.decorate(function (modelService) {
- return new CachingModelDecorator(modelService);
-}, { priority: 100 });
-```
-
-### 5. Decorate extension instances
-
-```js
-mct.capabilities.decorate(function (capabilities) {
- return capabilities.map(decorateIfApplicable);
-});
-```
-
-This use case is not well-supported by these API changes. The most
-common case for decoration is capabilities, which are under reconsideration;
-should consider handling decoration of capabilities in a different way.
-
-### 6. Add new types of services
-
-```js
-myModule.myServiceFactory = new mct.CompositeServiceFactory();
-
-// In cases where a custom composition strategy is desired
-myModule.myServiceFactory.composite(function (services) {
- return new MyServiceCompositor(services);
-});
-```
-
-### 7. Add new extension categories.
-
-```js
-myModule.hamburgers = new mct.ExtensionRegistry();
-```
-
-## Evaluation
-
-### Benefits
-
-* Encourages separation of registration from declaration (individual
- components are decoupled from the manner in which they are added
- to the architecture.)
-* Minimizes "magic." Dependencies are acquired, managed, and exposed
- using plain-old-JavaScript without any dependency injector present
- to obfuscate what is happening.
-* Offers comparable expressive power to existing APIs; can still
- extend the behavior of platform components in a variety of ways.
-* Does not force or limit formalisms to use;
-
-### Detriments
-
-* Does not encourage separation of dependency acquisition from
- declaration; that is, it would be quite natural using this API
- to acquire references to services during the constructor call
- to an extension or service. But, passing these in as constructor
- arguments is preferred (to separate implementation from architecture.)
-* Adds (negligible?) boilerplate relative to declarative syntax.
-* Relies on factories, increasing number of interfaces to be concerned
- with. \ No newline at end of file
diff --git a/docs/src/design/proposals/Roles.md b/docs/src/design/proposals/Roles.md
deleted file mode 100644
index 6148d4756..000000000
--- a/docs/src/design/proposals/Roles.md
+++ /dev/null
@@ -1,138 +0,0 @@
-# Roles
-
-Roles are presented as an alternative formulation to capabilities
-(dynamic behavior associated with individual domain objects.)
-
-Specific goals here:
-
-* Dependencies of individual scripts should be clear.
-* Domain objects should be able to selectively exhibit a wide
- variety of behaviors.
-
-## Developer Use Cases
-
-1. Checking for the existence of behavior.
-2. Using behavior.
-3. Augmenting existing behaviors.
-4. Overriding existing behaviors.
-5. Adding new behaviors.
-
-## Overview of Proposed Solution
-
-Remove `getCapability` from domain objects; add roles as external
-services which can be applied to domain objects.
-
-## Interfaces
-
-```nomnoml
-[Factory.<T, V>
- |
- - factoryFn : function (V) : T
- |
- + decorate(decoratorFn : function (T, V) : T, options? : RegistrationOptions)
-]-:>[function (V) : T]
-
-[RegistrationOptions |
- + priority : number or string
-]<:-[RoleOptions |
- + validate : function (DomainObject) : boolean
-]
-
-[Role.<T> |
- + validate(domainObject : DomainObject) : boolean
- + decorate(decoratorFn : function (T, V) : T, options? : RoleOptions)
-]-:>[Factory.<T, DomainObject>]
-[Factory.<T, DomainObject>]-:>[Factory.<T, V>]
-```
-
-## Examples
-
-### 1. Checking for the existence of behavior
-
-```js
-function PlotViewPolicy(telemetryRole) {
- this.telemetryRole = telemetryRole;
-}
-PlotViewPolicy.prototype.allow = function (view, domainObject) {
- return this.telemetryRole.validate(domainObject);
-};
-```
-
-### 2. Using behavior
-
-```js
-PropertiesAction.prototype.perform = function () {
- var mutation = this.mutationRole(this.domainObject);
- return this.showDialog.then(function (newModel) {
- return mutation.mutate(function () {
- return newModel;
- });
- });
-};
-```
-
-### 3. Augmenting existing behaviors
-
-```js
-// Non-Angular style
-mct.roles.persistenceRole.decorate(function (persistence) {
- return new DecoratedPersistence(persistence);
-});
-
-// Angular style
-myModule.decorate('persistenceRole', ['$delegate', function ($delegate) {
- return new DecoratedPersistence(persistence);
-}]);
-```
-
-### 4. Overriding existing behaviors
-
-```js
-// Non-Angular style
-mct.roles.persistenceRole.decorate(function (persistence, domainObject) {
- return domainObject.getModel().type === 'someType' ?
- new DifferentPersistence(domainObject) :
- persistence;
-}, {
- validate: function (domainObject, next) {
- return domainObject.getModel().type === 'someType' || next();
- }
-});
-```
-
-### 5. Adding new behaviors
-
-```js
-function FooRole() {
- mct.Role.apply(this, [function (domainObject) {
- return new Foo(domainObject);
- }]);
-}
-
-FooRole.prototype = Object.create(mct.Role.prototype);
-
-FooRole.prototype.validate = function (domainObject) {
- return domainObject.getModel().type === 'some-type';
-};
-
-//
-myModule.roles.fooRole = new FooRole();
-```
-
-
-## Evaluation
-
-### Benefits
-
-* Simplifies/standardizes augmentation or replacement of behavior associated
- with specific domain objects.
-* Minimizes number of abstractions; roles are just factories.
-* Clarifies dependencies; roles used must be declared/acquired in the
- same manner as services.
-
-### Detriments
-
-* Externalizes functionality which is conceptually associated with a
- domain object.
-* Relies on factories, increasing number of interfaces to be concerned
- with. \ No newline at end of file
diff --git a/docs/src/guide/index.md b/docs/src/guide/index.md
deleted file mode 100644
index a74c55e01..000000000
--- a/docs/src/guide/index.md
+++ /dev/null
@@ -1,2456 +0,0 @@
-# Open MCT Developer Guide
-Victor Woeltjen
-
-[victor.woeltjen@nasa.gov](mailto:victor.woeltjen@nasa.gov)
-
-September 23, 2015
-Document Version 1.1
-
-Date | Version | Summary of Changes | Author
-------------------- | --------- | ------------------------- | ---------------
-April 29, 2015 | 0 | Initial Draft | Victor Woeltjen
-May 12, 2015 | 0.1 | | Victor Woeltjen
-June 4, 2015 | 1.0 | Name Changes | Victor Woeltjen
-October 4, 2015 | 1.1 | Conversion to MarkDown | Andrew Henry
-April 5, 2016 | 1.2 | Added Mct-table directive | Andrew Henry
-
-# Introduction
-The purpose of this guide is to familiarize software developers with the Open
-MCT Web platform.
-
-## What is Open MCT
-Open MCT is a platform for building user interface and display tools,
-developed at the NASA Ames Research Center in collaboration with teams at the
-Jet Propulsion Laboratory. It is written in HTML5, CSS3, and JavaScript, using
-[AngularJS](http://www.angularjs.org) as a framework. Its intended use is to
-create single-page web applications which integrate data and behavior from a
-variety of sources and domains.
-
-Open MCT has been developed to support the remote operation of space
-vehicles, so some of its features are specific to that task; however, it is
-flexible enough to be adapted to a variety of other application domains where a
-display tool oriented toward browsing, composing, and visualizing would be
-useful.
-
-Open MCT provides:
-
-* A common user interface paradigm which can be applied to a variety of domains
-and tasks. Open MCT is more than a widget toolkit - it provides a standard
-tree-on-the-left, view-on-the-right browsing environment which you customize by
-adding new browsable object types, visualizations, and back-end adapters.
-* A plugin framework and an extensible API for introducing new application
-features of a variety of types.
-* A set of general-purpose object types and visualizations, as well as some
-visualizations and infrastructure specific to telemetry display.
-
-## Client-Server Relationship
-Open MCT is client software - it runs entirely in the user's web browser. As
-such, it is largely 'server agnostic'; any web server capable of serving files
-from paths is capable of providing Open MCT.
-
-While Open MCT can be configured to run as a standalone client, this is
-rarely very useful. Instead, it is intended to be used as a display and
-interaction layer for information obtained from a variety of back-end services.
-Doing so requires authoring or utilizing adapter plugins which allow Open MCT
-Web to interact with these services.
-
-Typically, the pattern here is to provide a known interface that Open MCT
-can utilize, and implement it such that it interacts with whatever back-end
-provides the relevant information. Examples of back-ends that can be utilized in
-this fashion include databases for the persistence of user-created objects, or
-sources of telemetry data.
-
-See the [Architecture Guide](../architecture/index.md#Overview) for information
-on the client-server relationship.
-
-## Developing with Open MCT
-Building applications with Open MCT typically means authoring and utilizing
-a set of plugins which provide application-specific details about how Open MCT
-Web should behave.
-
-### Technologies
-
-Open MCT sources are written in JavaScript, with a number of configuration
-files written in JSON. Displayable components are written in HTML5 and CSS3.
-Open MCT is built using [AngularJS](http://www.angularjs.org) from Google. A
-good understanding of Angular is recommended for developers working with Open
-MCT Web.
-
-### Forking
-Open MCT does not currently have a single stand-alone artifact that can be
-used as a library. Instead, the recommended approach for creating a new
-application is to start by forking/branching Open MCT, and then adding new
-features from there. Put another way, Open MCT's source structure is built
-to serve as a template for specific applications.
-
-Forking in this manner should not require that you edit Open MCT's sources.
-The preferred approach is to create a new directory (peer to `index.html`) for
-the new application, then add new bundles (as described in the Framework
-chapter) within that directory.
-
-To initially clone the Open MCT repository:
-`git clone <repository URL> <local repo directory> -b open-master`
-
-To create a fork to begin working on a new application using Open MCT:
-
- cd <local repo directory>
- git checkout open-master
- git checkout -b <new branch name>
-
-As a convention used internally, applications built using Open MCT have
-master branch names with an identifying prefix. For instance, if building an
-application called 'Foo', the last statement above would look like:
-
- git checkout -b foo-master
-
-This convention is not enforced or understood by Open MCT in any way; it is
-mentioned here as a more general recommendation.
-
-# Overview
-
-Open MCT is implemented as a framework component which manages a set of
-other components. These components, called _bundles_, act as containers to group
-sets of related functionality; individual units of functionality are expressed
-within these bundles as _extensions_.
-
-Extensions declare dependencies on other extensions (either individually or
-categorically), and the framework provides actual extension instances at
-run-time to satisfy these declared dependency. This dependency injection
-approach allows software components which have been authored separately (e.g. as
-plugins) but to collaborate at run-time.
-
-Open MCT's framework layer is implemented on top of AngularJS's [dependency
-injection mechanism](https://docs.angularjs.org/guide/di) and is modelled after
-[OSGi](hhttp://www.osgi.org/) and its [Declarative Services component model](http://wiki.osgi.org/wiki/Declarative_Services).
-In particular, this is where the term _bundle_ comes from.
-
-## Framework Overview
-
-The framework's role in the application is to manage connections between
-bundles. All application-specific behavior is provided by individual bundles, or
-as the result of their collaboration.
-
-The framework is described in more detail in the [Framework Overview](../architecture/framework.md#overview) of the
-architecture guide.
-
-### Tiers
-While all bundles in a running Open MCT instance are effectively peers, it
-is useful to think of them as a tiered architecture, where each tier adds more
-specificity to the application.
-```nomnoml
-#direction: down
-[Plugins (Features external to OpenMCTWeb) *Bundle]->[<frame>OpenMCTWeb |
-[Application (Plots, layouts, ElasticSearch wrapper) *Bundle]->[Platform (Core API, common UI, infrastructure) *Bundle]
-[Platform (Core API, common UI, infrastructure) *Bundle]->[Framework (RequireJS, AngularJS, bundle loader)]]
-```
-
-* __Framework__ : This tier is responsible for wiring together the set of
-configured components (called _bundles_) together to instantiate the running
-application. It is responsible for mediating between AngularJS (in particular,
-its dependency injection mechanism) and RequireJS (to load scripts at run-time.)
-It additionally interprets bundle definitions (see explanation below, as well as
-further detail in the Framework chapter.) At this tier, we are at our most
-general: We know only that we are a plugin-based application.
-* __Platform__: Components in the Platform tier describe both the general user
-interface and corresponding developer-facing interfaces of Open MCT. This
-tier provides the general infrastructure for applications. It is less general
-than the framework tier, insofar as this tier introduces a specific user
-interface paradigm, but it is still non-specific as to what useful features
-will be provided. Although they can be removed or replaced easily, bundles
-provided by the Platform tier generally should not be thought of as optional.
-* __Application__: The application tier consists of components which utilize the
-infrastructure provided by the Platform to provide functionality which will (or
-could) be useful to specific applications built using Open MCT. These
-include adapters to specific persistence back-ends (such as ElasticSearch or
-CouchDB) as well as bundles which describe more user-facing features (such as
-_Plot_ views for visualizing time series data, or _Layout_ objects for
-display-building.) Bundles from this tier can be added or removed without
-compromising basic application functionality, with the caveat that at least one
-persistence adapter needs to be present.
-* __Plugins__: Conceptually, this tier is not so different from the application
-tier; it consists of bundles describing new features, back-end adapters, that
-are specific to the application being built on Open MCT. It is described as
-a separate tier here because it has one important distinction from the
-application tier: It consists of bundles that are not included with the platform
-(either authored anew for the specific application, or obtained from elsewhere.)
-
-Note that bundles in any tier can go off and consult back-end services. In
-practice, this responsibility is handled at the Application and/or Plugin tiers;
-Open MCT is built to be server-agnostic, so any back-end is considered an
-application-specific detail.
-
-## Platform Overview
-
-The "tiered" architecture described in the preceding text describes a way of
-thinking of and categorizing software components of a Open MCT application,
-as well as the framework layer's role in mediating between these components.
-Once the framework layer has wired these software components together, however,
-the application's logical architecture emerges.
-
-An overview of the logical architecture of the platform is given in the
-[Platform Architecture](../architecture/platform.md#platform-architecture)
-section of the Platform guide
-
-### Web Services
-
-As mentioned in the Introduction, Open MCT is a platform single-page
-applications which runs entirely in the browser. Most applications will want to
-additionally interact with server-side resources, to (for example) read
-telemetry data or store user-created objects. This interaction is handled by
-individual bundles using APIs which are supported in browser (such as
-`XMLHttpRequest`, typically wrapped by Angular's `$http`.)
-
-```nomnoml
-#direction: right
-[Web Service #1] <- [Web Browser]
-[Web Service #2] <- [Web Browser]
-[Web Service #3] <- [Web Browser]
-[<package> Web Browser |
- [<package> Open MCT |
- [Plugin Bundle #1]-->[Core API]
- [Core API]<--[Plugin Bundle #2]
- [Platform Bundle #1]-->[Core API]
- [Platform Bundle #2]-->[Core API]
- [Platform Bundle #3]-->[Core API]
- [Core API]<--[Platform Bundle #4]
- [Core API]<--[Platform Bundle #5]
- [Core API]<--[Plugin Bundle #3]
- ]
- [Open MCT] ->[Browser APIs]
-]
-```
-
-This architectural approach ensures a loose coupling between applications built
-using Open MCT and the backends which support them.
-
-### Glossary
-
-Certain terms are used throughout Open MCT with consistent meanings or
-conventions. Other developer documentation, particularly in-line documentation,
-may presume an understanding of these terms.
-
-* __bundle__: A bundle is a removable, reusable grouping of software elements.
-The application is composed of bundles. Plug-ins are bundles.
-* __capability__: A JavaScript object which exposes dynamic behavior or
-non-persistent state associated with a domain object.
-* __category__: A machine-readable identifier for a group that something may
-belong to.
-* __composition__: In the context of a domain object, this refers to the set of
-other domain objects that compose or are contained by that object. A domain
-object's composition is the set of domain objects that should appear immediately
- beneath it in a tree hierarchy. A domain object's composition is described in
-its model as an array of identifiers; its composition capability provides a
-means to retrieve the actual domain object instances associated with these
-identifiers asynchronously.
-* __description__: When used as an object property, this refers to the human-
-readable description of a thing; usually a single sentence or short paragraph.
-(Most often used in the context of extensions, domain object models, or other
-similar application-specific objects.)
-* __domain object__: A meaningful object to the user; a distinct thing in the
-work support by Open MCT. Anything that appears in the left-hand tree is a
-domain object.
-* __extension__: An extension is a unit of functionality exposed to the platform
-in a declarative fashion by a bundle. The term 'extension category' is used to
-distinguish types of extensions from specific extension instances.
-* __id__: A string which uniquely identifies a domain object.
-* __key__: When used as an object property, this refers to the machine-readable
-identifier for a specific thing in a set of things. (Most often used in the
-context of extensions or other similar application-specific object sets.) This
-term is chosen to avoid attaching ambiguous meanings to 'id'.
-* __model__: The persistent state associated with a domain object. A domain
-object's model is a JavaScript object which can be converted to JSON without
-losing information (that is, it contains no methods.)
-* __name__: When used as an object property, this refers to the human-readable
-name for a thing. (Most often used in the context of extensions, domain object
-models, or other similar application-specific objects.)
-* __navigation__: Refers to the current state of the application with respect to
-the user's expressed interest in a specific domain object; e.g. when a user
-clicks on a domain object in the tree, they are navigating to it, and it is
-thereafter considered the navigated object (until the user makes another such
-choice.) This term is used to distinguish navigation from selection, which
-occurs in an editing context.
-* __space__: A machine-readable name used to identify a persistence store.
-Interactions with persistence with generally involve a space parameter in some
-form, to distinguish multiple persistence stores from one another (for cases
-where there are multiple valid persistence locations available.)
-* __source__: A machine-readable name used to identify a source of telemetry
-data. Similar to "space", this allows multiple telemetry sources to operate
-side-by-side without conflicting.
-
-# Framework
-
-Open MCT is built on the [AngularJS framework]( http://www.angularjs.org ). A
-good understanding of that framework is recommended.
-
-Open MCT adds an extra layer on top of AngularJS to (a) generalize its
-dependency injection mechanism slightly, particularly to handle many-to-one
-relationships; and (b) handle script loading. Combined, these features become a
-plugin mechanism.
-
-This framework layer operates on two key concepts:
-
-* __Bundle:__ A bundle is a collection of related functionality that can be
-added to the application as a group. More concretely, a bundle is a directory
-containing a JSON file declaring its contents, as well as JavaScript sources,
-HTML templates, and other resources used to support that functionality. (The
-term bundle is borrowed from [OSGi](http://www.osgi.org/) - which has also
-inspired many of the concepts used in the framework layer. A familiarity with
-OSGi, particularly Declarative Services, may be useful when working with Open
-MCT Web.)
-* __Extension:__ An extension is an individual unit of functionality. Extensions
-are collected together in bundles, and may interact with other extensions.
-
-The framework layer, loaded and initiated from `index.html`, is the main point
-of entry for an application built on Open MCT. It is responsible for wiring
-together the application at run time (much of this responsibility is actually
-delegated to Angular); at a high-level, the framework does this by proceeding
-through four stages:
-
-1. __Loading definitions:__ JSON declarations are loaded for all bundles which
-will constitute the application, and wrapped in a useful API for subsequent
-stages.
-2. __Resolving extensions:__ Any scripts which provide implementations for
-extensions exposed by bundles are loaded, using Require.
-3. __Registering extensions__ Resolved extensions are registered with Angular,
-such that they can be used by the application at run-time. This stage includes
-both registration of Angular built-ins (directives, controllers, routes,
-constants, and services) as well as registration of non-Angular extensions.
-4. __Bootstrapping__ The Angular application is bootstrapped; at that point,
-Angular takes over and populates the body of the page using the extensions that
-have been registered.
-
-## Bundles
-
-The basic configurable unit of Open MCT is the _bundle_. This term has been
-used a bit already; now we'll get to a more formal definition.
-
-A bundle is a directory which contains:
-
-* A bundle definition; a file named `bundle.json`.
-* Subdirectories for sources, resources, and tests.
-* Optionally, a `README.md` Markdown file describing its contents (this is not
-used by Open MCT in any way, but it's a helpful convention to follow.)
-
-The bundle definition is the main point of entry for the bundle. The framework
-looks at this to determine which components need to be loaded and how they
-interact.
-
-A plugin in Open MCT is a bundle. The platform itself is also decomposed
-into bundles, each of which provides some category of functionality. The
-difference between a _bundle_ and a _plugin_ is purely a matter of the intended
-use; a plugin is just a bundle that is meant to be easily added or removed. When
-developing, it is typically more useful to think in terms of bundles.
-
-### Configuring Active Bundles
-
-To decide which bundles should be loaded, the framework loads a file named
-`bundles.json` (peer to the `index.html` file which serves the application) to
-determine which bundles should be loaded. This file should contain a single JSON
-array of strings, where each is the path to a bundle. These paths should not
-include bundle.json (this is implicit) or a trailing slash.
-
-For instance, if `bundles.json` contained:
-
- [
- "example/builtins",
- "example/extensions"
- ]
-
-...then the Open MCT framework would look for bundle definitions at
-`example/builtins/bundle.json` and `example/extensions/bundle.json`, relative
-to the path of `index.html`. No other bundles would be loaded.
-
-### Bundle Definition
-
-A bundle definition (the `bundle.json` file located within a bundle) contains a
-description of the bundle itself, as well as the information exposed by the
-bundle.
-
-This definition is expressed as a single JSON object with the following
-properties (all of which are optional, falling back to reasonable defaults):
-
-* `key`: A machine-readable name for the bundle. (Currently used only in
-logging.)
-* `name`: A human-readable name for the bundle. (Also only used in logging.)
-* `sources`: Names a directory in which source scripts (which will implement
-extensions) are located. Defaults to 'src'
-* `resources`: Names a directory in which resource files (such as HTML templates,
-images, CS files, and other non-JavaScript files needed by this bundle) are
-located. Defaults to 'res'
-* `libraries`: Names a directory in which third-party libraries are located.
-Defaults to 'lib'
-* `configuration`: A bundle's configuration object, which should be formatted as
-would be passed to require.config (see [RequireJS documentation](http://requirejs.org/docs/api.html ) );
-note that only paths and shim have been tested.
-* `extensions`: An object containing key-value pairs, where keys are extension
-categories, and values are extension definitions. See the section on Extensions
-for more information.
-
-For example, the bundle definition for example/policy looks like:
-
- {
- "name": "Example Policy",
- "description": "Provides an example of using policies.",
- "sources": "src",
- "extensions": {
- "policies": [
- {
- "implementation": "ExamplePolicy.js",
- "category": "action"
- }
- ]
- }
- }
-
-### Bundle Directory Structure
-
-In addition to the directories defined in the bundle definition, a bundle will
-typically contain other directories not used at run-time. Additionally, some
-useful development scripts (such as the command line build and the test suite)
-expect this directory structure to be in use, and may ignore options chosen by
-`bundle.json`. It is recommended that the directory structure described below be
-used for new bundles.
-
-* `src`: Contains JavaScript sources for this bundle. May contain additional
-subdirectories to organize these sources; typically, these subdirectories are
-named to correspond to the extension categories they contain and/or support, but
-this is only a convention.
-* `res`: Contains other files needed by this bundle, such as HTML templates. May
-contain additional subdirectories to organize these sources.
-* `lib`: Contains JavaScript sources from third-party libraries. These are
-separated from bundle sources in order to ignore them during code style checking
-from the command line build.
-* `test`: Contains JavaScript sources implementing [Jasmine](http://jasmine.github.io/)
-tests, as well as a file named `suite.json` describing which files to test.
-Should have the same folder structure as the `src` directory; see the section on
-automated testing for more information.
-
-For example, the directory structure for bundle `platform/commonUI/dialog` looks
-like:
-
- Platform
- |
- |-commonUI
- |
- +-dialog
- |
- |-res
- |
- |-src
- |
- |-test
- |
- |-bundle.json
- |
- +-README.md
-
-## Extensions
-
-While bundles provide groupings of related behaviors, the individual units of
-behavior are called extensions.
-
-Extensions belong to categories; an extension category is the machine-readable
-identifier used to identify groups of extensions. In the `extensions` property
-of a bundle definition, the keys are extension categories and the values are
-arrays of extension definitions.
-
-### General Extensions
-
-Extensions are intended as a general-purpose mechanism for adding new types of
-functionality to Open MCT.
-
-An extension category is registered with Angular under the name of the
-extension, plus a suffix of two square brackets; so, an Angular service (or,
-generally, any other extension) can access the full set of registered
-extensions, from all bundles, by including this string (e.g. `types[]` to get
-all type definitions) in a dependency declaration.
-
-As a convention, extension categories are given single-word, plural nouns for
-names within Open MCT (e.g. `types`.) This convention is not enforced by the
-platform in any way. For extension categories introduced by external plugins, it
-is recommended to prefix the extension category with a vendor identifier (or
-similar) followed by a dot, to avoid collisions.
-
-### Extension Definitions
-
-The properties used in extension definitions are typically unique to each
-category of extension; a few properties have standard interpretations by the
-platform.
-
-* `implementation`: Identifies a JavaScript source file (in the sources
-folder) which implements this extension. This JavaScript file is expected to
-contain an AMD module (see http://requirejs.org/docs/whyamd.html#amd ) which
-gives as its result a single constructor function.
-* `depends`: An array of dependencies needed by this extension; these will be
-passed on to Angular's [dependency injector](https://docs.angularjs.org/guide/di ) .
-By default, this is treated as an empty array. Note that depends does not make
-sense without `implementation` (since these dependencies will be passed to the
-implementation when it is instantiated.)
-* `priority`: A number or string indicating the priority order (see below) of
-this extension instance. Before an extension category is registered with
-AngularJS, the extensions of this category from all bundles will be concatenated
-into a single array, and then sorted by priority.
-
-Extensions do not need to have an implementation. If no implementation is
-provided, consumers of the extension category will receive the extension
-definition as a plain JavaScript object. Otherwise, they will receive the
-partialized (see below) constructor for that implementation, which will
-additionally have all properties from the extension definition attached.
-
-#### Partial Construction
-
-In general, extensions are intended to be implemented as constructor functions,
-which will be used elsewhere to instantiate new objects of that type. However,
-the Angular-supported method for dependency injection is (effectively)
-constructor-style injection; so, both declared dependencies and run-time
-arguments are competing for space in a constructor's arguments.
-
-To resolve this, the Open MCT framework registers extension instances in a
-partially constructed form. That is, the constructor exposed by the extension's
-implementation is effectively decomposed into two calls; the first takes the
-dependencies, and returns the constructor in its second form, which takes the
-remaining arguments.
-
-This means that, when writing implementations, the constructor function should
-be written to include all declared dependencies, followed by all run-time
-arguments. When using extensions, only the run-time arguments need to be
-provided.
-
-#### Priority
-
-Within each extension category, registration occurs in priority order. An
-extension's priority may be specified as a `priority` property in its extension
-definition; this may be a number, or a symbolic string. Extensions are
-registered in reverse order (highest-priority first), and symbolic strings are
-mapped to the numeric values as follows:
-
-* `fallback`: Negative infinity. Used for extensions that are not intended for
-use (that is, they are meant to be overridden) but are present as an option of
-last resort.
-* `default`: `-100`. Used for extensions that are expected to be overridden, but
-need a useful default.
-* `none`: `0`. Also used if no priority is specified, or if an unknown or
-malformed priority is specified.
-* `optional`: `100`. Used for extensions that are meant to be used, but may be
-overridden.
-* `preferred`: `1000`. Used for extensions that are specifically intended to be
-used, but still may be overridden in principle.
-* `mandatory`: Positive infinity. Used when an extension should definitely not
-be overridden.
-
-These symbolic names are chosen to support usage where many extensions may
-satisfy a given need, but only one may be used; in this case, as a convention it
-should be the lowest-ordered (highest-priority) extensions available. In other
-cases, a full set (or multi-element subset) of extensions may be desired, with a
-specific ordering; in these cases, it is preferable to specify priority
-numerically when declaring extensions, and to understand that extensions will be
-sorted according to these conventions when using them.
-
-### Angular Built-ins
-
-Several entities supported Angular are expressed and managed as extensions in
-Open MCT. Specifically, these extension categories are _directives_,
-_controllers_, _services_, _constants_, _runs_, and _routes_.
-
-#### Angular Directives
-
-New [directives]( https://docs.angularjs.org/guide/directive ) may be
-registered as extensions of the directives category. Implementations of
-directives in this category should take only dependencies as arguments, and
-should return a directive definition object.
-
-The directive's name should be provided as a key property of its extension
-definition, in camel-case format.
-
-#### Angular Controllers
-
-New [controllers]( https://docs.angularjs.org/guide/controller ) may be registered
-as extensions of the controllers category. The implementation is registered
-directly as the controller; its only constructor arguments are its declared
-dependencies.
-
-The directive's identifier should be provided as a key property of its extension
-definition.
-
-
-#### Angular Services
-
-New [services](https://docs.angularjs.org/guide/services ) may be registered as
-extensions of the services category. The implementation is registered via a
-[service call]( https://docs.angularjs.org/api/auto/service/$provide#service ), so
-it will be instantiated with the new operator.
-
-#### Angular Constants
-
-Constant values may be registered as extensions of the [ constants category](https://docs.angularjs.org/api/ng/type/angular.Module#constant ).
-These extensions have no implementation; instead, they should contain a property
- key , which is the name under which the constant will be registered, and a
-property value , which is the constant value that will be registered.
-
-#### Angular Runs
-
-In some cases, you want to register code to run as soon as the application
-starts; these can be registered as extensions of the [ runs category](https://docs.angularjs.org/api/ng/type/angular.Module#run ).
-Implementations registered in this category will be invoked (with their declared
-dependencies) when the Open MCT application first starts. (Note that, in
-this case, the implementation is better thought of as just a function, as
-opposed to a constructor function.)
-
-#### Angular Routes
-
-Extensions of category `routes` will be registered with Angular's [route provider](https://docs.angularjs.org/api/ngRoute/provider/$routeProvider ).
-Extensions of this category have no implementations, and need only two
-properties in their definition:
-
-* `when`: The value that will be passed as the path argument to `$routeProvider.when`;
-specifically, the string that will appear in the trailing
-part of the URL corresponding to this route. This property may be omitted, in
-which case this extension instance will be treated as the default route.
-* `templateUrl`: A path to the template to render for this route. Specified as a
-path relative to the bundle's resource directory (`res` by default.)
-
-### Composite Services
-
-Composite services are described in the [Composite Services](../architecture/framework.md#composite-services)
-section of the framework guide.
-
-A component should include the following properties in its extension definition:
-
-* `provides`: The symbolic identifier for the service that will be composed. The
- fully-composed service will be registered with Angular under this name.
-* `type`: One of `provider`, `aggregator` or `decorator` (as above)
-
-In addition to any declared dependencies, _aggregators_ and _decorators_ both
-receive one more argument (immediately following declared dependencies) that is
-provided by the framework. For an aggregator, this will be an array of all
-providers of the same service (that is, with matching `provides` properties);
-for a decorator, this will be whichever provider, decorator, or aggregator is
-next in the sequence of decorators.
-
-Services exposed by the Open MCT platform are often declared as composite
-services, as this form is open for a variety of common modifications.
-
-# Core API
-
-Most of Open MCT's relevant API is provided and/or mediated by the
-framework; that is, much of developing for Open MCT is a matter of adding
-extensions which access other parts of the platform by means of dependency
-injection.
-
-The core bundle (`platform/core`) introduces a few additional object types meant
-to be passed along by other services.
-
-## Domain Objects
-
-Domain objects are the most fundamental component of Open MCT's information
-model. A domain object is some distinct thing relevant to a user's workflow,
-such as a telemetry channel, display, or similar. Open MCT is a tool for
-viewing, browsing, manipulating, and otherwise interacting with a graph of
-domain objects.
-
-A domain object should be conceived of as the union of the following:
-
-* __Identifier__: A machine-readable string that uniquely identifies the domain
-object within this application instance.
-* __Model__: The persistent state of the domain object. A domain object's model
-is a JavaScript object that can be losslessly converted to JSON.
-* __Capabilities__: Dynamic behavior associated with the domain object.
-Capabilities are JavaScript objects which provide additional methods for
-interacting with the domain objects which expose those capabilities. Not all
-domain objects expose all capabilities.
-
-At run-time, a domain object has the following interface:
-
-* `getId()`: Get the identifier for this domain object.
-* `getModel()`: Get the plain state associated with this domain object. This
-will return a JavaScript object that can be losslessly converted to JSON. Note
-that the model returned here can be modified directly but should not be;
-instead, use the mutation capability.
-* `getCapability(key)`: Get the specified capability associated with this domain
-object. This will return a JavaScript object whose interface is specific to the
-type of capability being requested. If the requested capability is not exposed
-by this domain object, this will return undefined .
-* `hasCapability(key)`: Shorthand for checking if a domain object exposes the
-requested capability.
-* `useCapability(key, arguments )`: Shorthand for
-`getCapability(key).invoke(arguments)`, with additional checking between calls.
-If the provided capability has no invoke method, the return value here functions
-as `getCapability` including returning `undefined` if the capability is not
-exposed.
-
-### Identifier Syntax
-
-For most purposes, a domain object identifier can be treated as a purely
-symbolic string; these are typically generated by Open MCT and plug-ins
-should rarely be concerned with its internal structure.
-
-A domain object identifier has one or two parts, separated by a colon.
-
-* If two parts are present, the part before the colon refers to the space
- in which the domain object resides. This may be a persistence space or
- a purely symbolic space recognized by a specific model provider. The
- part after the colon is the key to use when looking up the domain object
- model within that space.
-* If only one part is present, the domain object has no space specified,
- and may presume to reside in the application-configured default space
- defined by the `PERSISTENCE_SPACE` constant.
-* Both the key and the space identifier may consist of any combination
- of alphanumeric characters, underscores, dashes, and periods.
-
-Some examples:
-
-* A domain object with the identifier `foo:xyz` would have its model
- loaded using key `xyz` from persistence space `foo`.
-* A domain object with the identifier `bar` would have its model loaded
- using key `bar` from the space identified by the `PERSISTENCE_SPACE`
- constant.
-
-```bnf
-<identifier> ::= <space> ":" <key> | <key>
-<space> ::= <id char>+
-<key> ::= <id char>+
-<id char> ::= <letter> | <digit> | "-" | "." | "_"
-```
-
-## Domain Object Actions
-
-An `Action` is behavior that can be performed upon/using a `DomainObject`. An
-Action has the following interface:
-
-* `perform()`: Do this action. For example, if one had an instance of a
-`RemoveAction` invoking its perform method would cause the domain object which
-exposed it to be removed from its container.
-* `getMetadata()`: Get metadata associated with this action. Returns an object
-containing:
- * `name`: Human-readable name.
- * `description`: Human-readable summary of this action.
- * `glyph`: Single character to be displayed in Open MCT's icon font set.
- * `context`: The context in which this action is being performed (see below)
-
-Action instances are typically obtained via a domain object's `action`
-capability.
-
-### Action Contexts
-
-An action context is a JavaScript object with the following properties:
-
-* `domainObject`: The domain object being acted upon.
-* `selectedObject`: Optional; the selection at the time of action (e.g. the
-dragged object in a drag-and-drop operation.)
-
-## Telemetry
-
-Telemetry series data in Open MCT is represented by a common interface, and
-packaged in a consistent manner to facilitate passing telemetry updates around
-multiple visualizations.
-
-### Telemetry Requests
-
-A telemetry request is a JavaScript object containing the following properties:
-
-* `source`: A machine-readable identifier for the source of this telemetry. This
-is useful when multiple distinct data sources are in use side-by-side.
-* `key`: A machine-readable identifier for a unique series of telemetry within
-that source.
-* _Note: This API is still under development; additional properties, such as
-start and end time, should be present in future versions of Open MCT._
-
-Additional properties may be included in telemetry requests which have specific
-interpretations for specific sources.
-
-### Telemetry Responses
-
-When returned from the `telemetryService` (see [Telemetry Services](#telemetry-service)
-section), telemetry series data will be packaged in a `source -> key -> TelemetrySeries`
-fashion. That is, telemetry is passed in an object containing key-value pairs.
-Keys identify telemetry sources; values are objects containing additional
-key-value pairs. In this object, keys identify individual telemetry series (and
-match they `key` property from corresponding requests) and values are
-`TelemetrySeries` objects (see below.)
-
-### Telemetry Series
-
-A telemetry series is a specific sequence of data, typically associated with a
-specific instrument. Telemetry is modeled as an ordered sequence of domain and
-range values, where domain values must be non-decreasing but range values do
-not. (Typically, domain values are interpreted as UTC timestamps in milliseconds
-relative to the UNIX epoch.) A series must have at least one domain and one
-range, and may have more than one.
-
-Telemetry series data in Open MCT is expressed via the following
-`TelemetrySeries` interface:
-
-* `getPointCount()`: Returns the number of unique points/samples in this series.
-* `getDomainValue(index, [domain])`: Get the domain value at the specified index .
-If a second domain argument is provided, this is taken as a string identifier
-indicating which domain option (of, presumably, multiple) should be returned.
-* `getRangeValue(index, [range])`: Get the domain value at the specified index .
-If a second range argument is provided, this is taken as a string identifier
-indicating which range option (of, presumably, multiple) should be returned.
-
-### Telemetry Metadata
-
-Domain objects which have associated telemetry also expose metadata about that
-telemetry; this is retrievable via the `getMetadata()` of the telemetry
-capability. This will return a single JavaScript object containing the following
-properties:
-
-* `source`: The machine-readable identifier for the source of telemetry data for
-this object.
-* `key`: The machine-readable identifier for the individual telemetry series.
-* `domains`: An array of supported domains (see TelemetrySeries above.) Each
-domain should be expressed as an object which includes:
- * `key`: Machine-readable identifier for this domain, as will be passed into
- a getDomainValue(index, domain) call.
- * `name`: Human-readable name for this domain.
-* `ranges`: An array of supported ranges; same format as domains .
-
-Note that this metadata is also used as the prototype for telemetry requests
-made using this capability.
-
-## Types
-A domain object's type is represented as a Type object, which has the following
-interface:
-
-* `getKey()`: Get the machine-readable identifier for this type.
-* `getName()`: Get the human-readable name for this type.
-* `getDescription()`: Get a human-readable summary of this type.
-* `getGlyph()`: Get the single character to be rendered as an icon for this type
-in Open MCT's custom font set.
-* `getInitialModel()`: Get a domain object model that represents the initial
-state (before user specification of properties) for domain objects of this type.
-* `getDefinition()`: Get the extension definition for this type, as a JavaScript
-object.
-* `instanceOf(type)`: Check if this type is (or inherits from) a specified type .
-This type can be either a string, in which case it is taken to be that type's
- key , or it may be a `Type` instance.
-* `hasFeature(feature)`: Returns a boolean value indicating whether or not this
-type supports the specified feature, which is a symbolic string.
-* `getProperties()`: Get all properties associated with this type, expressed as
-an array of `TypeProperty` instances.
-
-### Type Features
-
-Features of a domain object type are expressed as symbolic string identifiers.
-They are defined in practice by usage; currently, the Open MCT platform only
-uses the creation feature to determine which domain object types should appear
-in the Create menu.
-
-### Type Properties
-
-Types declare the user-editable properties of their domain object instances in
-order to allow the forms which appear in the __Create__ and __Edit Properties__
-dialogs to be generated by the platform. A `TypeProperty` has the following interface:
-
-* `getValue(model)`: Get the current value for this property, as it appears in
-the provided domain object model.
-* `setValue(model, value)`: Set a new value for this property in the provided
-domain object model .
-* `getDefinition()`: Get the raw definition for this property as a JavaScript
-object (as it was declared in this type's extension definition.)
-
-# Extension Categories
-
-The information in this section is focused on registering new extensions of
-specific types; it does not contain a catalog of the extension instances of
-these categories provided by the platform. Relevant summaries there are provided
-in subsequent sections.
-
-## Actions Category
-
-An action is a thing that can be done to or using a domain object, typically as
-initiated by the user.
-
-An action's implementation:
-
-* Should take a single `context` argument in its constructor. (See Action
-Contexts, under Core API.)
-* Should provide a method `perform` which causes the behavior associated with
-the action to occur.
-* May provide a method `getMetadata` which provides metadata associated with
-the action. If omitted, one will be provided by the platform which includes
-metadata from the action's extension definition.
-* May provide a static method `appliesTo(context)` (that is, a function
-available as a property of the implementation's constructor itself), which will
-be used by the platform to filter out actions from contexts in which they are
-inherently inapplicable.
-
-An action's bundle definition (and/or `getMetadata()` return value) may include:
-
-* `category`: A string or array of strings identifying which category or
-categories an action falls into; used to determine when an action is displayed.
-Categories supported by the platform include:
- * `contextual`: Actions in a context menu.
- * `view-control`: Actions triggered by buttons in the top-right of Browse
- view.
-* `key`: A machine-readable identifier for this action.
-* `name`: A human-readable name for this action (e.g. to show in a menu)
-* `description`: A human-readable summary of the behavior of this action.
-* `glyph`: A single character which will be rendered in Open MCT's custom
-font set as an icon for this action.
-
-## Capabilities Category
-
-Capabilities are exposed by domain objects (e.g. via the `getCapability` method)
-but most commonly originate as extensions of this category.
-
-Extension definitions for capabilities should include both an implementation,
-and a property named key whose value should be a string used as a
-machine-readable identifier for that capability, e.g. when passed as the
-argument to a domain object's `getCapability(key)` call.
-
-A capability's implementation should have methods specific to that capability;
-that is, there is no common format for capability implementations, aside from
-support for invocation via the `useCapability` shorthand.
-
-A capability's implementation will take a single argument (in addition to any
-declared dependencies), which is the domain object that will expose that
-capability.
-
-A capability's implementation may also expose a static method `appliesTo(model)`
-which should return a boolean value, and will be used by the platform to filter
-down capabilities to those which should be exposed by specific domain objects,
-based on their domain object models.
-
-## Containers Category
-
-Containers provide options for the `mct-container` directive.
-
-The definition for an extension in the `containers` category should include:
-
-* `key`: An identifier for the container.
-* `template`: An Angular template for the container, including an
- `ng-transclude` where contained content should go.
-* `attributes`: An array of attribute names. The values associated with
- these attributes will be exposed in the template's scope under the
- name provided by the `alias` property.
-* `alias`: The property name in scope under which attributes will be
- exposed. Optional; defaults to "container".
-
-Note that `templateUrl` is not supported for `containers`.
-
-## Controls Category
-
-Controls provide options for the `mct-control` directive.
-
-These standard control types are included in the forms bundle:
-
-* `textfield`: A text input to enter plain text.
-* `numberfield`: A text input to enter numbers.
-* `select`: A drop-down list of options.
-* `checkbox`: A box which may be checked/unchecked.
-* `color`: A color picker.
-* `button`: A button.
-* `datetime`: An input for UTC date/time entry; gives result as a UNIX
-timestamp, in milliseconds since start of 1970, UTC.
-* `composite`: A control parenting an array of other controls.
-* `menu-button`: A drop-down list of items supporting custom behavior
-on click.
-* `dialog-button`: A button which opens a dialog allowing a single property
-to be edited.
-* `radio`: A radio button.
-
-New controls may be added as extensions of the controls category. Extensions of
-this category have two properties:
-
-* `key`: The symbolic name for this control (matched against the control field
-in rows of the form structure).
-* `templateUrl`: The URL to the control's Angular template, relative to the
-resources directory of the bundle which exposes the extension.
-
-Within the template for a control, the following variables will be included in
-scope:
-
-* `ngModel`: The model where form input will be stored. Notably we also need to
-look at field (see below) to determine which field in the model should be
-modified.
-* `ngRequired`: True if input is required.
-* `ngPattern`: The pattern to match against (for text entry)
-* `ngBlur`: A function that may be invoked to evaluate the expression
- associated with the `ng-blur` attribute associated with the control.
- * This should be called when the control has lost focus; for controls
- which simply wrap or augment `input` elements, this should be fired
- on `blur` events associated with those elements, while more complex
- custom controls may fire this at the end of more specific interactions.
-* `options`: The options for this control, as passed from the `options` property
-of an individual row definition.
-* `field`: Name of the field in `ngModel` which will hold the value for this
-control.
-
-## Gestures Category
-
-A _gesture_ is a user action which can be taken upon a representation of a
-domain object.
-
-Examples of gestures included in the platform are:
-
-* `drag`: For representations that can be used to initiate drag-and-drop
-composition.
-* `drop`: For representations that can be drop targets for drag-and-drop
-composition.
-* `menu`: For representations that can be used to popup a context menu.
-
-Gesture definitions have a property `key` which is used as a machine-readable
-identifier for the gesture (e.g. `drag`, `drop`, `menu` above.)
-
-A gesture's implementation is instantiated once per representation that uses the
-gesture. This class will receive the jqLite-wrapped `mct-representation` element
-and the domain object being represented as arguments, and should do any
-necessary "wiring" (e.g. listening for events) during its constructor call. The
-gesture's implementation may also expose an optional `destroy()` method which
-will be called when the gesture should be removed, to avoid memory leaks by way
-of unremoved listeners.
-
-## Indicators Category
-
-An indicator is an element that should appear in the status area at the bottom
-of a running Open MCT client instance.
-
-### Standard Indicators
-
-Indicators which wish to appear in the common form of an icon-text pair should
-provide implementations with the following methods:
-
-* `getText()`: Provides the human-readable text that will be displayed for this
-indicator.
-* `getGlyph()`: Provides a single-character string that will be displayed as an
-icon in Open MCT's custom font set.
-* `getDescription()`: Provides a human-readable summary of the current state of
-this indicator; will be displayed in a tooltip on hover.
-* `getClass()`: Get a CSS class that will be applied to this indicator.
-* `getTextClass()`: Get a CSS class that will be applied to this indicator's
-text portion.
-* `getGlyphClass()`: Get a CSS class that will be applied to this indicator's
-icon portion.
-* `configure()`: If present, a configuration icon will appear to the right of
-this indicator, and clicking it will invoke this method.
-
-Note that all methods are optional, and are called directly from an Angular
-template, so they should be appropriate to run during digest cycles.
-
-### Custom Indicators
-
-Indicators which wish to have an arbitrary appearance (instead of following the
-icon-text convention commonly used) may specify a `template` property in their
-extension definition. The value of this property will be used as the `key` for
-an `mct-include` directive (so should refer to an extension of category
- templates .) This template will be rendered to the status area. Indicators of
-this variety do not need to provide an implementation.
-
-## Licenses Category
-
-The extension category `licenses` can be used to add entries into the 'Licensing
-information' page, reachable from Open MCT's About dialog.
-
-Licenses may have the following properties, all of which are strings:
-
-* `name`: Human-readable name of the licensed component. (e.g. 'AngularJS'.)
-* `version`: Human-readable version of the licensed component. (e.g. '1.2.26'.)
-* `description`: Human-readable summary of the component.
-* `author`: Name or names of entities to which authorship should be attributed.
-* `copyright`: Copyright text to display for this component.
-* `link`: URL to full license text.
-
-## Policies Category
-
-Policies are used to handle decisions made using Open MCT's `policyService`;
-examples of these decisions are determining the applicability of certain
-actions, or checking whether or not a domain object of one type can contain a
-domain object of a different type. See the section on the Policies for an
-overview of Open MCT's policy model.
-
-A policy's extension definition should include:
-
-* `category`: The machine-readable identifier for the type of policy decision
-being supported here. For a list of categories supported by the platform, see
-the section on Policies. Plugins may introduce and utilize additional policy
-categories not in that list.
-* `message`: Optional; a human-readable message describing the policy, intended
-for display in situations where this specific policy has disallowed something.
-
-A policy's implementation should include a single method, `allow(candidate,
-context)`. The specific types used for `candidate` and `context` vary by policy
-category; in general, what is being asked is 'is this candidate allowed in this
-context?' This method should return a boolean value.
-
-Open MCT's policy model requires consensus; a policy decision is allowed
-when and only when all policies choose to allow it. As such, policies should
-generally be written to reject a certain case, and allow (by returning `true`)
-anything else.
-
-## Representations Category
-
-A representation is an Angular template used to display a domain object. The
-`representations` extension category is used to add options for the
-`mct-representation` directive.
-
-A representation definition should include the following properties:
-
-* `key`: The machine-readable name which identifies the representation.
-* `templateUrl`: The path to the representation's Angular template. This path is
-relative to the bundle's resources directory.
-* `uses`: Optional; an array of capability names. Indicates that this
-representation intends to use those capabilities of a domain object (via a
-`useCapability` call), and expects to find the latest results of that
-`useCapability` call in the scope of the presented template (under the same name
-as the capability itself.) Note that, if `useCapability` returns a promise, this
-will be resolved before being placed in the representation's scope.
-* `gestures`: An array of keys identifying gestures (see the `gestures`
-extension category) which should be available upon this representation. Examples
-of gestures include `drag` (for representations that should act as draggable
-sources for drag-drop operations) and `menu` (for representations which should
-show a domain-object-specific context menu on right-click.)
-
-### Representation Scope
-
-While _representations_ do not have implementations, per se, they do refer to
-Angular templates which need to interact with information (e.g. the domain
-object being represented) provided by the platform. This information is passed
-in through the template's scope, such that simple representations may be created
-by providing only templates. (More complex representations will need controllers
-which are referenced from templates. See https://docs.angularjs.org/guide/controller
-for more information on controllers in Angular.)
-
-A representation's scope will contain:
-
-* `domainObject`: The represented domain object.
-* `model`: The domain object's model.
-* `configuration`: An object containing configuration information for this
-representation (an empty object if there is no saved configuration.) The
-contents of this object are managed entirely by the view/representation which
-receives it.
-* `representation`: An empty object, useful as a 'scratch pad' for
-representation state.
-* `ngModel`: An object passed through the ng-model attribute of the
-`mct-representation` , if any.
-* `parameters`: An object passed through the parameters attribute of the
-`mct-representation`, if any.
-* Any capabilities requested by the uses property of the representation
-definition.
-
-## Representers Category
-
-The `representers` extension category is used to add additional behavior to the
-`mct-representation` directive. This extension category is intended primarily
-for use internal to the platform.
-
-Unlike _representations_, which describe specific ways to represent domain
-objects, _representers_ are used to modify or augment the process of
-representing domain objects in general. For example, support for the _gestures_
-extension category is added by a _representer_.
-
-A representer needs only provide an implementation. When an `mct-representation`
-is linked (see https://docs.angularjs.org/guide/directive ) or when the
-domain object being represented changes, a new _representer_ of each declared
-type is instantiated. The constructor arguments for a _representer_ are the same
-as the arguments to the link function in an Angular directive: `scope` the
-Angular scope for this representation; `element` the jqLite-wrapped
-`mct-representation` element, and `attrs` a set of key-value pairs of that
-element's attributes. _Representers_ may wish to populate the scope, attach
-event listeners to the element, etc.
-
-This implementation must provide a single method, `destroy()`, which will be
-invoked when the representer is no longer needed.
-
-## Roots Category
-
-The extension category `roots` is used to provide root-level domain object
-models. Root-level domain objects appear at the top-level of the tree hierarchy.
-For example, the _My Items_ folder is added as an extension of this category.
-
-Extensions of this category should have the following properties:
-
-* `id`: The machine-readable identifier for the domain object being exposed.
-* `model`: The model, as a JSON object, for the domain object being exposed.
-
-## Stylesheets Category
-
-The stylesheets extension category is used to add CSS files to style the
-application. Extension definitions for this category should include one
-property:
-
-* `stylesheetUrl`: Path and filename, including extension, for the stylesheet to
-include. This path is relative to the bundle's resources folder (by default,
-`res`)
-* `theme`: Optional; if present, this stylesheet will only be included if this
-value matches the `THEME` constant.
-
-To control the order of CSS files, use priority (see the section on Extension
-Definitions above.)
-
-## Templates Category
-
-The `templates` extension category is used to expose Angular templates under
-symbolic identifiers. These can then be utilized using the `mct-include`
-directive, which behaves similarly to `ng-include` except that it uses these
-symbolic identifiers instead of paths.
-
-A template's extension definition should include the following properties:
-
-* `key`: The machine-readable name which identifies this template, matched
-against the value given to the key attribute of the `mct-include` directive.
-* `templateUrl`: The path to the relevant Angular template. This path is
-relative to the bundle's resources directory.
-
-Note that, when multiple templates are present with the same key , the one with
-the highest priority will be used from `mct-include`. This behavior can be used
-to override templates exposed by the platform (to change the logo which appears
-in the bottom right, for instance.)
-
-Templates do not have implementations.
-
-## Types Category
-
-The types extension category describes types of domain objects which may
-appear within Open MCT.
-
-A type's extension definition should have the following properties:
-
-* `key`: The machine-readable identifier for this domain object type. Will be
-stored to and matched against the type property of domain object models.
-* `name`: The human-readable name for this domain object type.
-* `description`: A human-readable summary of this domain object type.
-* `glyph`: A single character to be rendered as an icon in Open MCT's custom
-font set.
-* `model`: A domain object model, used as the initial state for created domain
-objects of this type (before any properties are specified.)
-* `features`: Optional; an array of strings describing features of this domain
-object type. Currently, only creation is recognized by the platform; this is
-used to determine that this type should appear in the Create menu. More
-generally, this is used to support the `hasFeature(...)` method of the type
-capability.
-* `properties`: An array describing individual properties of this domain object
-(as should appear in the _Create_ or the _Edit Properties_ dialog.) Each
-property is described by an object containing the following properties:
- * `control`: The key of the control (see `mct-control` and the `controls`
- [extension category](#controls-category)) to use for editing this property.
- * `property`: A string which will be used as the name of the property in the
- domain object's model that the value for this property should be stored
- under. If this value should be stored in an object nested within the domain
- object model, then property should be specified as an array of strings
- identifying these nested objects and, finally, the property itself.
- * other properties as appropriate for a control of this type (each
- property's definition will also be passed in as the structure for its
- control.) See documentation of mct-form for more detail on these
- properties.
-
-Types do not have implementations.
-
-## Versions Category
-The versions extension category is used to introduce line items in Open MCT
-Web's About dialog. These should have the following properties:
-
-* `name`: The name of this line item, as should appear in the left-hand side of
-the list of version information in the About dialog.
-* `value`: The value which should appear to the right of the name in the About
-dialog.
-
-To control the ordering of line items within the About dialog, use `priority`.
-(See section on [Extensions](#extensions) above.)
-
-This extension category does not have implementations.
-
-## Views Category
-
-The views extension category is used to determine which options appear to the
-user as available views of domain objects of specific types. A view's extension
-definition has the same properties as a representation (and views can be
-utilized via `mct-representation`); additionally:
-
-* `name`: The human-readable name for this view type.
-* description : A human-readable summary of this view type.
-* `glyph`: A single character to be rendered as an icon in Open MCT's custom
-font set.
-* `type`: Optional; if present, this representation is only applicable for
-domain object's of this type.
-* `needs`: Optional array of strings; if present, this representation is only
-applicable for domain objects which have the capabilities identified by these
-strings.
-* `delegation`: Optional boolean, intended to be used in conjunction with
-`needs`; if present, allow required capabilities to be satisfied by means of
-capability delegation. (See [Delegation](#delegation-capability))
-* `toolbar`: Optional; a definition for the toolbar which may appear in a
-toolbar when using this view in Edit mode. This should be specified as a
-structure for mct-toolbar , with additional properties available for each item in
-that toolbar:
- * `property`: A property name. This will refer to a property in the view's
- current selection; that property on the selected object will be modifiable
- as the `ng-model` of the displayed control in the toolbar. If the value of
- the property is a function, it will be used as a getter-setter (called with
- no arguments to use as a getter, called with a value to use as a setter.)
- * `method`: A method to invoke (again, on the selected object) from the
- toolbar control. Useful particularly for buttons (which don't edit a single
- property, necessarily.)
-
-### View Scope
-
-Views do not have implementations, but do get the same properties in scope that
-are provided for `representations`.
-
-When a view is in Edit mode, this scope will additionally contain:
-
-* `commit()`: A function which can be invoked to mark any changes to the view's
- configuration as ready to persist.
-* `selection`: An object representing the current selection state.
-
-#### Selection State
-
-A view's selection state is, conceptually, a set of JavaScript objects. The
-presence of methods/properties on these objects determine which toolbar controls
-are visible, and what state they manage and/or behavior they invoke.
-
-This set may contain up to two different objects: The _view proxy_, which is
-used to make changes to the view as a whole, and the _selected object_, which is
-used to represent some state within the view. (Future versions of Open MCT
-may support multiple selected objects.)
-
-The `selection` object made available during Edit mode has the following
-methods:
-
-* `proxy([object])`: Get (or set, if called with an argument) the current view
-proxy.
-* `select(object)`: Make this object the selected object.
-* `deselect()`: Clear the currently selected object.
-* `get()`: Get the currently selected object. Returns undefined if there is no
-currently selected object.
-* `selected(object)`: Check if the JavaScript object is currently in the
-selection set. Returns true if the object is either the currently selected
-object, or the current view proxy.
-* `all()`: Get an array of all objects in the selection state. Will include
-either or both of the view proxy and selected object.
-
-## Workers Category
-
-The `workers` extension category allows scripts to be run as web workers
-using the `workerService`.
-
-An extension of this category has no implementation. The following properties
-are supported:
-
-* `key`: A symbolic string used to identify this worker.
-* `workerUrl`: The path, relative to this bundle's `src` folder, where
- this worker's source code resides.
-* `shared`: Optional; a boolean flag which, if true, indicates that this
- worker should be instantiated as a
- [`SharedWorker`](https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker/SharedWorker).
- Default value is `false`.
-
-# Directives
-
-Open MCT defines several Angular directives that are intended for use both
-internally within the platform, and by plugins.
-
-## Container
-
-The `mct-container` is similar to the `mct-include` directive insofar as it allows
-templates to be referenced by symbolic keys instead of by URL. Unlike
-`mct-include` it supports transclusion.
-
-Unlike `mct-include` `mct-container` accepts a key as a plain string attribute,
-instead of as an Angular expression.
-
-## Control
-
-The `mct-control` directive is used to display user input elements. Several
-controls are included with the platform to wrap default input types. This
-directive is primarily intended for internal use by the `mct-form` and
-`mct-toolbar` directives.
-
-When using `mct-control` the attributes `ng-model` `ng-disabled`
-`ng-required` and `ng-pattern` may also be used. These have the usual meaning
-(as they would for an input element) except for `ng-model`; when used, it will
-actually be `ngModel[field]` (see below) that is two-way bound by this control.
-This allows `mct-control` elements to more easily delegate to other
-`mct-control` instances, and also facilitates usage for generated forms.
-
-This directive supports the following additional attributes, all specified as
-Angular expressions:
-
-* `key`: A machine-readable identifier for the specific type of control to
-display.
-* `options`: A set of options to display in this control.
-* `structure`: In practice, contains the definition object which describes this
-form row or toolbar item. Used to pass additional control-specific parameters.
-* `field`: The field in the `ngModel` under which to read/store the property
-associated with this control.
-
-## Drag
-
-The `mct-drag` directive is used to support drag-based gestures on HTML
-elements. Note that this is not 'drag' in the 'drag-and-drop' sense, but 'drag'
-in the more general 'mouse down, mouse move, mouse up' sense.
-
-This takes the form of three attributes:
-
-* `mct-drag`: An Angular expression to evaluate during drag movement.
-* `mct-drag-down`: An Angular expression to evaluate when the drag starts.
-* `mct-drag-up`: An Angular expression to evaluate when the drag ends.
-
-In each case, a variable `delta` will be provided to the expression; this is a
-two-element array or the horizontal and vertical pixel offset of the current
-mouse position relative to the mouse position where dragging began.
-
-## Form
-
-The `mct-form` directive is used to generate forms using a declarative structure,
-and to gather back user input. It is applicable at the element level and
-supports the following attributes:
-
-* `ng-model`: The object which should contain the full form input. Individual
-fields in this model are bound to individual controls; the names used for these
-fields are provided in the form structure (see below).
-* `structure`: The structure of the form; e.g. sections, rows, their names, and
-so forth. The value of this attribute should be an Angular expression.
-* `name`: The name in the containing scope under which to publish form
-"meta-state", e.g. `$valid` `$dirty` etc. This is as the behavior of `ng-form`.
-Passed as plain text in the attribute.
-
-### Form Structure
-
-Forms in Open MCT have a common structure to permit consistent display. A
-form is broken down into sections, which will be displayed in groups; each
-section is broken down into rows, each of which provides a control for a single
-property. Input from this form is two-way bound to the object passed via
-`ng-model`.
-
-A form's structure is represented by a JavaScript object in the following form:
-
- {
- "name": ... title to display for the form, as a string ...,
- "sections": [
- {
- "name": ... title to display for the section ...,
- "rows": [
- {
- "name": ... title to display for this row ...,
- "control": ... symbolic key for the control ...,
- "key": ... field name in ng-model ...
- "pattern": ... optional, reg exp to match against ...
- "required": ... optional boolean ...
- "options": [
- "name": ... name to display (e.g. in a select) ...,
- "value": ... value to store in the model ...
- ]
- },
- ... and other rows ...
- ]
- },
- ... and other sections ...
- ]
- }
-
-Note that `pattern` may be specified as a string, to simplify storing for
-structures as JSON when necessary. The string should be given in a form
-appropriate to pass to a `RegExp` constructor.
-
-### Form Controls
-
-A few standard control types are included in the platform/forms bundle:
-
-* `textfield`: An area to enter plain text.
-* `select`: A drop-down list of options.
-* `checkbox`: A box which may be checked/unchecked.
-* `color`: A color picker.
-* `button`: A button.
-* `datetime`: An input for UTC date/time entry; gives result as a UNIX
-timestamp, in milliseconds since start of 1970, UTC.
-
-## Include
-
-The `mct-include` directive is similar to ng-include , except that it takes a
-symbolic identifier for a template instead of a URL. Additionally, templates
-included via mct-include will have an isolated scope.
-
-The directive should be used at the element level and supports the following
-attributes, all of which are specified as Angular expressions:
-
-* `key`: Machine-readable identifier for the template (of extension category
-templates ) to be displayed.
-* `ng-model`: _Optional_; will be passed into the template's scope as `ngModel`.
-Intended usage is for two-way bound user input.
-* `parameters`: _Optional_; will be passed into the template's scope as
-parameters. Intended usage is for template-specific display parameters.
-
-## Representation
-
-The `mct-representation` directive is used to include templates which
-specifically represent domain objects. Usage is similar to `mct-include`.
-
-The directive should be used at the element level and supports the following
-attributes, all of which are specified as Angular expressions:
-
-* `key`: Machine-readable identifier for the representation (of extension
-category _representations_ or _views_ ) to be displayed.
-* `mct-object`: The domain object being represented.
-* `ng-model`: Optional; will be passed into the template's scope as `ngModel`.
-Intended usage is for two-way bound user input.
-* `parameters`: Optional; will be passed into the template's scope as
-parameters . Intended usage is for template-specific display parameters.
-
-## Resize
-
-The `mct-resize` directive is used to monitor the size of an HTML element. It is
-specified as an attribute whose value is an Angular expression that will be
-evaluated when the size of the HTML element changes. This expression will be
-provided a single variable, `bounds` which is an object containing two
-properties, `width` and `height` describing the size in pixels of the element.
-
-When using this directive, an attribute `mct-resize-interval` may optionally be
-provided. Its value is an Angular expression describing the number of
-milliseconds to wait before next checking the size of the HTML element; this
-expression is evaluated when the directive is linked and reevaluated whenever
-the size is checked.
-
-## Scroll
-
-The `mct-scroll-x` and `mct-scroll-y` directives are used to both monitor and
-control the horizontal and vertical scroll bar state of an element,
-respectively. They are intended to be used as attributes whose values are
-assignable Angular expressions which two-way bind to the scroll bar state.
-
-## Toolbar
-
-The `mct-toolbar` directive is used to generate toolbars using a declarative
-structure, and to gather back user input. It is applicable at the element level
-and supports the following attributes:
-
-* `ng-model`: The object which should contain the full toolbar input. Individual
-fields in this model are bound to individual controls; the names used for these
-fields are provided in the form structure (see below).
-* `structure`: The structure of the toolbar; e.g. sections, rows, their names, and
-so forth. The value of this attribute should be an Angular expression.
-* `name`: The name in the containing scope under which to publish form
-"meta-state", e.g. `$valid`, `$dirty` etc. This is as the behavior of
-`ng-form`. Passed as plain text in the attribute.
-
-Toolbars support the same control options as forms.
-
-### Toolbar Structure
-
-A toolbar's structure is defined similarly to forms, except instead of rows
-there are items .
-
- {
- "name": ... title to display for the form, as a string ...,
- "sections": [
- {
- "name": ... title to display for the section ...,
- "items": [
- {
- "name": ... title to display for this row ...,
- "control": ... symbolic key for the control ...,
- "key": ... field name in ng-model ...
- "pattern": ... optional, reg exp to match against ...
- "required": ... optional boolean ...
- "options": [
- "name": ... name to display (e.g. in a select) ...,
- "value": ... value to store in the model ...
- ],
- "disabled": ... true if control should be disabled ...
- "size": ... size of the control (for textfields) ...
- "click": ... function to invoke (for buttons) ...
- "glyph": ... glyph to display (for buttons) ...
- "text": ... text within control (for buttons) ...
- },
- ... and other rows ...
- ]
- },
- ... and other sections ...
- ]
- }
-
-## Table
-
-The `mct-table` directive provides a generic table component, with optional
-sorting and filtering capabilities. The table can be pre-populated with data
-by setting the `rows` parameter, and it can be updated in real-time using the
-`add:row` and `remove:row` broadcast events. The table will expand to occupy
-100% of the size of its containing element. The table is highly optimized for
-very large data sets.
-
-### Events
-
-The table supports two events for notifying that the rows have changed. For
-performance reasons, the table does not monitor the content of `rows`
-constantly.
-
-* `add:row`: A `$broadcast` event that will notify the table that a new row
-has been added to the table.
-
-eg. The code below adds a new row, and alerts the table using the `add:row`
-event. Sorting and filtering will be applied automatically by the table component.
-
-```
-$scope.rows.push(newRow);
-$scope.$broadcast('add:row', $scope.rows.length-1);
-```
-
-* `remove:row`: A `$broadcast` event that will notify the table that a row
-should be removed from the table.
-
-eg. The code below removes a row from the rows array, and then alerts the table
-to its removal.
-
-```
-$scope.rows.slice(5, 1);
-$scope.$broadcast('remove:row', 5);
-```
-
-### Parameters
-
-* `headers`: An array of string values which will constitute the column titles
- that appear at the top of the table. Corresponding values are specified in
- the rows using the header title provided here.
-* `rows`: An array of objects containing row values. Each element in the
-array must be an associative array, where the key corresponds to a column header.
-* `enableFilter`: A boolean that if true, will enable searching and result
-filtering. When enabled, each column will have a text input field that can be
-used to filter the table rows in real time.
-* `enableSort`: A boolean determining whether rows can be sorted. If true,
-sorting will be enabled allowing sorting by clicking on column headers. Only
-one column may be sorted at a time.
-* `autoScroll`: A boolean value that if true, will cause the table to automatically
-scroll to the bottom as new data arrives. Auto-scroll can be disengaged manually
-by scrolling away from the bottom of the table, and can also be enabled manually
-by scrolling to the bottom of the table rows.
-
-# Services
-
-The Open MCT platform provides a variety of services which can be retrieved
-and utilized via dependency injection. These services fall into two categories:
-
-* _Composite Services_ are defined by a set of components extensions; plugins may
-introduce additional components with matching interfaces to extend or augment
-the functionality of the composed service. (See the Framework section on
-Composite Services.)
-* _Other services_ which are defined as standalone service objects; these can be
-utilized by plugins but are not intended to be modified or augmented.
-
-## Composite Type Services
-
-This section describes the composite services exposed by Open MCT,
-specifically focusing on their interface and contract.
-
-In many cases, the platform will include a provider for a service which consumes
-a specific extension category; for instance, the `actionService` depends on
-`actions[]` and will expose available actions based on the rules defined for
-that extension category.
-
-In these cases, it will usually be simpler to add a new extension of a given
-category (e.g. of category `actions`) even when the same behavior could be
-introduced by a service component (e.g. an extension of category `components`
-where `provides` is `actionService` and `type` is `provider`.)
-
-Occasionally, the extension category does not provide enough expressive power to
-achieve a desired result. For instance, the Create menu is populated with
-`create` actions, where one such action exists for each creatable type. Since
-the framework does not provide a declarative means to introduce a new action per
-type declaratively, the platform implements this explicitly in an `actionService`
-component of type `provider`. Plugins may use a similar approach when the normal
-extension mechanism is insufficient to achieve a desired result.
-
-### Action Service
-
-The [Action Service](../architecture/platform.md#action-service)
-(`actionService`)
-provides `Action` instances which are applicable in specific contexts. See Core
-API for additional notes on the interface for actions. The `actionService` has
-the following interface:
-
-* `getActions(context)`: Returns an array of Action objects which are applicable
-in the specified action context.
-
-### Capability Service
-
-The [Capability Service](../architecture/platform.md#capability-service)
-(`capabilityService`)
-provides constructors for capabilities which will be exposed for a given domain
-object.
-
-The capabilityService has the following interface:
-
-* `getCapabilities(model)`: Returns a an object containing key-value pairs,
-representing capabilities which should be exposed by the domain object with this
-model. Keys in this object are the capability keys (as used in a
-`getCapability(...)` call) and values are either:
- * Functions, in which case they will be used as constructors, which will
- receive the domain object instance to which the capability applies as their
- sole argument.The resulting object will be provided as the result of a
- domain object's `getCapability(...)` call. Note that these instances are cached
- by each object, but may be recreated when an object is mutated.
- * Other objects, which will be used directly as the result of a domain
- object's `getCapability(...)` call.
-
-### Dialog Service
-
-The `dialogService` provides a means for requesting user input via a modal
-dialog. It has the following interface:
-
-* `getUserInput(formStructure, formState)`: Prompt the user to fill out a form.
-The first argument describes the form's structure (as will be passed to
- mct-form ) while the second argument contains the initial state of that form.
-This returns a Promise for the state of the form after the user has filled it
-in; this promise will be rejected if the user cancels input.
-* `getUserChoice(dialogStructure)`: Prompt the user to make a single choice from
-a set of options, which (in the platform implementation) will be expressed as
-buttons in the displayed dialog. Returns a Promise for the user's choice, which
-will be rejected if the user cancels input.
-
-### Dialog Structure
-
-The object passed as the `dialogStructure` to `getUserChoice` should have the
-following properties:
-
-* `title`: The title to display at the top of the dialog.
-* `hint`: Short message to display below the title.
-* `template`: Identifying key (as will be passed to mct-include ) for the
-template which will be used to populate the inner area of the dialog.
-* `model`: Model to pass in the ng-model attribute of mct-include .
-* `parameters`: Parameters to pass in the parameters attribute of mct-include .
-* `options`: An array of options describing each button at the bottom. Each
-option may have the following properties:
- * `name`: Human-readable name to display in the button.
- * `key`: Machine-readable key, to pass as the result of the resolved promise
- when clicked.
- * `description`: Description to show in tooltip on hover.
-
-### Domain Object Service
-
-The [Object Service](../architecture/platform.md#object-service) (`objectService`)
-provides domain object instances. It has the following interface:
-
-* `getObjects(ids)`: For the provided array of domain object identifiers,
-returns a Promise for an object containing key-value pairs, where keys are
-domain object identifiers and values are corresponding DomainObject instances.
-Note that the result may contain a superset or subset of the objects requested.
-
-### Gesture Service
-
-The `gestureService` is used to attach gestures (see extension category gestures)
-to representations. It has the following interface:
-
-* `attachGestures(element, domainObject, keys)`: Attach gestures specified by
-the provided gesture keys (an array of strings) to this jqLite-wrapped HTML
-element , which represents the specified domainObject . Returns an object with a
-single method `destroy()`, to be invoked when it is time to detach these
-gestures.
-
-### Model Service
-
-The [Model Service](../architecture/platform.md#model-service) (`modelService`)
-provides domain object models. It has the following interface:
-
-* `getModels(ids)`: For the provided array of domain object identifiers, returns
-a Promise for an object containing key-value pairs, where keys are domain object
-identifiers and values are corresponding domain object models. Note that the
-result may contain a superset or subset of the models requested.
-
-### Persistence Service
-
-The [Persistence Service](../architecture/platform.md#persistence-service) (`persistenceService`)
-provides the ability to load/store JavaScript objects
-(presumably serializing/deserializing to JSON in the process.) This is used
-primarily to store domain object models. It has the following interface:
-
-* `listSpaces()`: Returns a Promise for an array of strings identifying the
-different persistence spaces this service supports. Spaces are intended to be
-used to distinguish between different underlying persistence stores, to allow
-these to live side by side.
-* `listObjects()`: Returns a Promise for an array of strings identifying all
-documents stored in this persistence service.
-* `createObject(space, key, value)`: Create a new document in the specified
-persistence space , identified by the specified key , the contents of which shall
-match the specified value . Returns a promise that will be rejected if creation
-fails.
-* `readObject(space, key)`: Read an existing document in the specified
-persistence space , identified by the specified key . Returns a promise for the
-specified document; this promise will resolve to undefined if the document does
-not exist.
-* `updateObject(space, key, value)`: Update an existing document in the
-specified persistence space , identified by the specified key , such that its
-contents match the specified value . Returns a promise that will be rejected if
-the update fails.
-* `deleteObject(space, key)`: Delete an existing document from the specified
-persistence space , identified by the specified key . Returns a promise which will
-be rejected if deletion fails.
-
-### Policy Service
-
-The [Policy Service](../architecture/platform.md#policy-service) (`policyService`)
-may be used to determine whether or not certain behaviors are
-allowed within the application. It has the following interface:
-
-* `allow(category, candidate, context, [callback])`: Check if this decision
-should be allowed. Returns a boolean. Its arguments are interpreted as:
- * `category`: A string identifying which kind of decision is being made. See
- the [section on Categories](#policy-categories) for categories supported by
- the platform; plugins may define and utilize policies of additional
- categories, as well.
- * `candidate`: An object representing the thing which shall or shall not be
- allowed. Usually, this will be an instance of an extension of the category
- defined above. This does need to be the case; additional policies which are
- not specific to any extension may also be defined and consulted using unique
- category identifiers. In this case, the type of the object delivered for the
- candidate may be unique to the policy type.
- * `context`: An object representing the context in which the decision is
- occurring. Its contents are specific to each policy category.
- * `callback`: Optional; a function to call if the policy decision is rejected.
- This function will be called with the message string (which may be
- undefined) of whichever individual policy caused the operation to fail.
-
-### Telemetry Service
-
-The [Telemetry Service](../architecture/platform.md#telemetry-service) (`telemetryService`)
-is used to acquire telemetry data. See the section on
-Telemetry in Core API for more information on how both the arguments and
-responses of this service are structured.
-
-When acquiring telemetry for display, it is recommended that the
-`telemetryHandler` service be used instead of this service. The
-`telemetryHandler` has additional support for subscribing to and requesting
-telemetry data associated with domain objects or groups of domain objects. See
-the [Other Services](#other-services) section for more information.
-
-The `telemetryService` has the following interface:
-
-* `requestTelemetry(requests)`: Issue a request for telemetry, matching the
-specified telemetry requests . Returns a _ Promise _ for a telemetry response
-object.
-* `subscribe(callback, requests)`: Subscribe to real-time updates for telemetry,
-matching the specified `requests`. The specified `callback` will be invoked with
-telemetry response objects as they become available. This method returns a
-function which can be invoked to terminate the subscription.
-
-### Type Service
-
-The [Type Service](../architecture/platform.md#type-service) (`typeService`) exposes
-domain object types. It has the following interface:
-
-* `listTypes()`: Returns all domain object types supported in the application,
-as an array of `Type` instances.
-* `getType(key)`: Returns the `Type` instance identified by the provided key, or
-undefined if no such type exists.
-
-### View Service
-
-The [View Service](../architecture/platform.md#view-service) (`viewService`) exposes
-definitions for views of domain objects. It has the following interface:
-
-* `getViews(domainObject)`: Get an array of extension definitions of category
-`views` which are valid and applicable to the specified `domainObject`.
-
-## Other Services
-
-### Drag and Drop
-
-The `dndService` provides information about the content of an active
-drag-and-drop gesture within the application. It is intended to complement the
-`DataTransfer` API of HTML5 drag-and-drop, by providing access to non-serialized
-JavaScript objects being dragged, as well as by permitting inspection during
-drag (which is normally prohibited by browsers for security reasons.)
-
-The `dndService` has the following methods:
-
-* `setData(key, value)`: Set drag data associated with a given type, specified
-by the `key` argument.
-* `getData(key)`: Get drag data associated with a given type, specified by the
-`key` argument.
-* `removeData(key)`: Clear drag data associated with a given type, specified by
-the `key` argument.
-
-### Navigation
-
-The _Navigation_ service provides information about the current navigation state
-of the application; that is, which object is the user currently viewing? This
-service merely tracks this state and notifies listeners; it does not take
-immediate action when navigation changes, although its listeners might.
-
-The `navigationService` has the following methods:
-
-* `getNavigation()`: Get the current navigation state. Returns a `DomainObject`.
-* `setNavigation(domainObject)`: Set the current navigation state. Returns a
-`DomainObject`.
-* `addListener(callback)`: Listen for changes in navigation state. The provided
-`callback` should be a `Function` which takes a single `DomainObject` as an
-argument.
-* `removeListener(callback)`: Stop listening for changes in navigation state.
-The provided `callback` should be a `Function` which has previously been passed
-to addListener .
-
-### Now
-
-The service now is a function which acts as a simple wrapper for `Date.now()`.
-It is present mainly so that this functionality may be more easily mocked in
-tests for scripts which use the current time.
-
-### Telemetry Formatter
-
-The _Telemetry Formatter_ is a utility for formatting domain and range values
-read from a telemetry series.
-
-`telemetryFormatter` has the following methods:
-
-* `formatDomainValue(value)`: Format the provided domain value (which will be
-assumed to be a timestamp) for display; returns a string.
-* `formatRangeValue(value)`: Format the provided range value (a number) for
-display; returns a string.
-
-### Telemetry Handler
-
-The _Telemetry Handler_ is a utility for retrieving telemetry data associated
-with domain objects; it is particularly useful for dealing with cases where the
-telemetry capability is delegated to contained objects (as occurs
-in _Telemetry Panels_.)
-
-The `telemetryHandler` has the following methods:
-
-* `handle(domainObject, callback, [lossless])`: Subscribe to and issue future
-requests for telemetry associated with the provided `domainObject`, invoking the
-provided callback function when streaming data becomes available. Returns a
-`TelemetryHandle` (see below.)
-
-#### Telemetry Handle
-
-A TelemetryHandle has the following methods:
-
-* `getTelemetryObjects()`: Get the domain objects (as a `DomainObject[]`) that
-have a telemetry capability and are being handled here. Note that these are
-looked up asynchronously, so this method may return an empty array if the
-initial lookup is not yet completed.
-* `promiseTelemetryObjects()`: As `getTelemetryObjects()`, but returns a Promise
-that will be fulfilled when the lookup is complete.
-* `unsubscribe()`: Unsubscribe to streaming telemetry updates associated with
-this handle.
-* `getDomainValue(domainObject)`: Get the most recent domain value received via
-a streaming update for the specified `domainObject`.
-* `getRangeValue(domainObject)`: Get the most recent range value received via a
-streaming update for the specified `domainObject`.
-* `getMetadata()`: Get metadata (as reported by the `getMetadata()` method of a
-telemetry capability) associated with telemetry-providing domain objects.
-Returns an array, which is in the same order as getTelemetryObjects() .
-* `request(request, callback)`: Issue a new request for historical telemetry
-data. The provided callback will be invoked when new data becomes available,
-which may occur multiple times (e.g. if there are multiple domain objects.) It
-will be invoked with the DomainObject for which a new series is available, and
-the TelemetrySeries itself, in that order.
-* `getSeries(domainObject)`: Get the latest `TelemetrySeries` (as resulted from
-a previous `request(...)` call) available for this domain object.
-
-### Worker Service
-
-The `workerService` may be used to run web workers defined via the
-`workers` extension category. It has the following method:
-
-* `run(key)`: Run the worker identified by the provided `key`. Returns
- a `Worker` (or `SharedWorker`, if the specified worker is defined
- as a shared worker); if the `key` is unknown, returns `undefined`.
-
-# Models
-Domain object models in Open MCT are JavaScript objects describing the
-persistent state of the domain objects they describe. Their contents include a
-mix of commonly understood metadata attributes; attributes which are recognized
-by and/or determine the applicability of specific extensions; and properties
-specific to given types.
-
-## General Metadata
-
-Some properties of domain object models have a ubiquitous meaning through Open
-MCT Web and can be utilized directly:
-
-* `name`: The human-readable name of the domain object.
-
-## Extension-specific Properties
-
-Other properties of domain object models have specific meaning imposed by other
-extensions within the Open MCT platform.
-
-### Capability-specific Properties
-
-Some properties either trigger the presence/absence of certain capabilities, or
-are managed by specific capabilities:
-
-* `composition`: An array of domain object identifiers that represents the
-contents of this domain object (e.g. as will appear in the tree hierarchy.)
-Understood by the composition capability; the presence or absence of this
-property determines the presence or absence of that capability.
-* `modified`: The timestamp (in milliseconds since the UNIX epoch) of the last
-modification made to this domain object. Managed by the mutation capability.
-* `persisted`: The timestamp (in milliseconds since the UNIX epoch) of the last
-time when changes to this domain object were persisted. Managed by the
- persistence capability.
-* `relationships`: An object containing key-value pairs, where keys are symbolic
-identifiers for relationship types, and values are arrays of domain object
-identifiers. Used by the relationship capability; the presence or absence of
-this property determines the presence or absence of that capability.
-* `telemetry`: An object which serves as a template for telemetry requests
-associated with this domain object (e.g. specifying `source` and `key`; see
-Telemetry Requests under Core API.) Used by the telemetry capability; the
-presence or absence of this property determines the presence or absence of that
-capability.
-* `type`: A string identifying the type of this domain object. Used by the `type`
-capability.
-
-### View Configurations
-
-Persistent configurations for specific views of domain objects are stored in the
-domain object model under the property configurations . This is an object
-containing key-value pairs, where keys identify the view, and values are objects
-containing view-specific (and view-managed) configuration properties.
-
-## Modifying Models
-When interacting with a domain object's model, it is possible to make
-modifications to it directly. __Don't!__ These changes may not be properly detected
-by the platform, meaning that other representations of the domain object may not
-be updated, changes may not be saved at the expected times, and generally, that
-unexpected behavior may occur. Instead, use the `mutation` capability.
-
-# Capabilities
-
-Dynamic behavior associated with a domain object is expressed as capabilities. A
-capability is a JavaScript object with an interface that is specific to the type
-of capability in use.
-
-Often, there is a relationship between capabilities and services. For instance,
-there is an action capability and an actionService , and there is a telemetry
-capability as well as a `telemetryService`. Typically, the pattern here is that
-the capability will utilize the service for the specific domain object.
-
-When interacting with domain objects, it is generally preferable to use a
-capability instead of a service when the option is available. Capability
-interfaces are typically easier to use and/or more powerful in these situations.
-Additionally, this usage provides a more robust substitutability mechanism; for
-instance, one could configure a plugin such that it provided a totally new
-implementation of a given capability which might not invoke the underlying
-service, while user code which interacts with capabilities remains indifferent
-to this detail.
-
-## Action Capability
-
-The `action` capability is present for all domain objects. It allows applicable
-`Action` instances to be retrieved and performed for specific domain objects.
-
-For example:
- `domainObject.getCapability("action").perform("navigate"); `
- ...will initiate a navigate action upon the domain object, if an action with
- key "navigate" is defined.
-
-This capability has the following interface:
-* `getActions(context)`: Get the actions that are applicable in the specified
-action `context`; the capability will fill in the `domainObject` field of this
-context if necessary. If context is specified as a string, they will instead be
-used as the `key` of the action context. Returns an array of `Action` instances.
-* `perform(context)`: Perform an action. This will find and perform the first
-matching action available for the specified action context , filling in the
-`domainObject` field as necessary. If `context` is specified as a string, they
-will instead be used as the `key` of the action context. Returns a `Promise` for
-the result of the action that was performed, or `undefined` if no matching action
-was found.
-
-## Composition Capability
-
-The `composition` capability provides access to domain objects that are
-contained by this domain object. While the `composition` property of a domain
-object's model describes these contents (by their identifiers), the
-`composition` capability provides a means to load the corresponding
-`DomainObject` instances in the same order. The absence of this property in the
-model will result in the absence of this capability in the domain object.
-
-This capability has the following interface:
-
-* `invoke()`: Returns a `Promise` for an array of `DomainObject` instances.
-
-## Delegation Capability
-
-The delegation capability is used to communicate the intent of a domain object
-to delegate responsibilities, which would normally handled by other
-capabilities, to the domain objects in its composition.
-
-This capability has the following interface:
-
-* `getDelegates(key)`: Returns a Promise for an array of DomainObject instances,
-to which this domain object wishes to delegate the capability with the specified
-key .
-* `invoke(key)`: Alias of getDelegates(key) .
-* `doesDelegate(key)`: Returns true if the domain object does delegate the
-capability with the specified key .
-
-The platform implementation of the delegation capability inspects the domain
-object's type definition for a property delegates , whose value is an array of
-strings describing which capabilities domain objects of that type wish to
-delegate. If this property is not present, the delegation capability will not be
-present in domain objects of that type.
-
-## Editor Capability
-
-The editor capability is meant primarily for internal use by Edit mode, and
-helps to manage the behavior associated with exiting _Edit_ mode via _Save_ or
-_Cancel_. Its interface is not intended for general use. However,
-`domainObject.hasCapability(editor)` is a useful way of determining whether or
-not we are looking at an object in _Edit_ mode.
-
-## Mutation Capability
-
-The `mutation` capability provides a means by which the contents of a domain
-object's model can be modified. This capability is provided by the platform for
-all domain objects, and has the following interface:
-
-* `mutate(mutator, [timestamp])`: Modify the domain object's model using the
-specified `mutator` function. After changes are made, the `modified` property of
-the model will be updated with the specified `timestamp` if one was provided,
-or with the current system time.
-* `invoke(...)`: Alias of `mutate`.
-
-Changes to domain object models should only be made via the `mutation`
-capability; other platform behavior is likely to break (either by exhibiting
-undesired behavior, or failing to exhibit desired behavior) if models are
-modified by other means.
-
-### Mutator Function
-
-The mutator argument above is a function which will receive a cloned copy of the
-domain object's model as a single argument. It may return:
-
-* A `Promise` in which case the resolved value of the promise will be used to
-determine which of the following forms is used.
-* Boolean `false` in which case the mutation is cancelled.
-* A JavaScript object, in which case this object will be used as the new model
-for this domain object.
-* No value (or, equivalently, `undefined`), in which case the cloned copy
-(including any changes made in place by the mutator function) will be used as
-the new domain object model.
-
-## Persistence Capability
-
-The persistence capability provides a mean for interacting with the underlying
-persistence service which stores this domain object's model. It has the
-following interface:
-
-* `persist()`: Store the local version of this domain object, including any
-changes, to the persistence store. Returns a Promise for a boolean value, which
-will be true when the object was successfully persisted.
-* `refresh()`: Replace this domain object's model with the most recent version
-from persistence. Returns a Promise which will resolve when the change has
-completed.
-* `getSpace()`: Return the string which identifies the persistence space which
-stores this domain object.
-
-## Relationship Capability
-
-The relationship capability provides a means for accessing other domain objects
-with which this domain object has some typed relationship. It has the following
-interface:
-
-* `listRelationships()`: List all types of relationships exposed by this object.
-Returns an array of strings identifying the types of relationships.
-* `getRelatedObjects(relationship)`: Get all domain objects to which this domain
-object has the specified type of relationship, which is a string identifier
-(as above.) Returns a `Promise` for an array of `DomainObject` instances.
-
-The platform implementation of the `relationship` capability is present for domain
-objects which has a `relationships` property in their model, whose value is an
-object containing key-value pairs, where keys are strings identifying
-relationship types, and values are arrays of domain object identifiers.
-
-## Status Capability
-
-The `status` capability provides a way to flag domain objects as possessing
-certain states, represented as simple strings. These states, in turn, are
-reflected on `mct-representation` elements as classes (prefixed with
-`s-status-`.) The `status` capability has the following interface:
-
-* `get()`: Returns an array of all status strings that currently apply
- to this object.
-* `set(status, state)`: Adds or removes a status flag to this domain object.
- The `status` argument is the string to set; `state` is a boolean
- indicating whether this status should be included (true) or removed (false).
-* `listen(callback)`: Listen for changes in status. The provided `callback`
- will be invoked with an array of all current status strings whenever status
- changes.
-
-Plug-ins may add and/or recognize arbitrary status flags. Flags defined
-and/or supported by the platform are:
-
- Status | CSS Class | Meaning
------------|--------------------|-----------------------------------
-`editing` | `s-status-editing` | Domain object is being edited.
-`pending` | `s-status-pending` | Domain object is partially loaded.
-
-
-## Telemetry Capability
-
-The telemetry capability provides a means for accessing telemetry data
-associated with a domain object. It has the following interface:
-
-* `requestData([request])`: Request telemetry data for this specific domain
-object, using telemetry request parameters from the specified request if
-provided. This capability will fill in telemetry request properties as-needed
-for this domain object. Returns a `Promise` for a `TelemetrySeries`.
-* `subscribe(callback, [request])`: Subscribe to telemetry data updates for
-this specific domain object, using telemetry request parameters from the
-specified request if provided. This capability will fill in telemetry request
-properties as-needed for this domain object. The specified callback will be
-invoked with TelemetrySeries instances as they arrive. Returns a function which
-can be invoked to terminate the subscription, or undefined if no subscription
-could be obtained.
-* `getMetadata()`: Get metadata associated with this domain object's telemetry.
-
-The platform implementation of the `telemetry` capability is present for domain
-objects which has a `telemetry` property in their model and/or type definition;
-this object will serve as a template for telemetry requests made using this
-object, and will also be returned by `getMetadata()` above.
-
-## Type Capability
-The `type` capability exposes information about the domain object's type. It has
-the same interface as `Type`; see Core API.
-
-## View Capability
-
-The `view` capability exposes views which are applicable to a given domain
-object. It has the following interface:
-
-* `invoke()`: Returns an array of extension definitions for views which are
-applicable for this domain object.
-
-# Actions
-
-Actions are reusable processes/behaviors performed by users within the system,
-typically upon domain objects.
-
-## Action Categories
-
-The platform understands the following action categories (specifiable as the
-`category` parameter of an action's extension definition.)
-
-* `contextual`: Appears in context menus.
-* `view-control`: Appears in top-right area of view (as buttons) in Browse mode
-
-## Platform Actions
-The platform defines certain actions which can be utilized by way of a domain
-object's `action` capability. Unless otherwise specified, these act upon (and
-modify) the object described by the `domainObject` property of the action's
-context.
-
-* `cancel`: Cancel the current editing action (invoked from Edit mode.)
-* `compose`: Place an object in another object's composition. The object to be
-added should be provided as the `selectedObject` of the action context.
-* `edit`: Start editing an object (enter Edit mode.)
-* `fullscreen`: Enter full screen mode.
-* `navigate`: Make this object the focus of navigation (e.g. highlight it within
-the tree, display a view of it to the right.)
-* `properties`: Show the 'Edit Properties' dialog.
-* `remove`: Remove this domain object from its parent's composition. (The
-parent, in this case, is whichever other domain object exposed this object by
-way of its `composition` capability.)
-* `save`: Save changes (invoked from Edit mode.)
-* `window`: Open this object in a new window.
-
-# Policies
-
-Policies are consulted to determine when certain behavior in Open MCT is
-allowed. Policy questions are assigned to certain categories, which broadly
-describe the type of decision being made; within each category, policies have a
-candidate (the thing which may or may not be allowed) and, optionally, a context
-(describing, generally, the context in which the decision is occurring.)
-
-The types of objects passed for 'candidate' and 'context' vary by category;
-these types are documented below.
-
-## Policy Categories
-
-The platform understands the following policy categories (specifiable as the
-`category` parameter of an policy's extension definition.)
-
-* `action`: Determines whether or not a given action is allowable. The candidate
-argument here is an Action; the context is its action context object.
-* `composition`: Determines whether or not a given domain object(first argument, `parent`) can contain a candidate child object (second argument, `child`).
-* `view`: Determines whether or not a view is applicable for a domain object.
-The candidate argument is the view's extension definition; the context argument
-is the `DomainObject` to be viewed.
-
-# Build-Test-Deploy
-Open MCT is designed to support a broad variety of build and deployment
-options. The sources can be deployed in the same directory structure used during
-development. A few utilities are included to support development processes.
-
-## Command-line Build
-
-Open MCT is built using [`npm`](http://npmjs.com/)
-and [`gulp`](http://gulpjs.com/).
-
-To install build dependencies (only needs to be run once):
-
-`npm install`
-
-To build:
-
-`npm run prepare`
-
-This will compile and minify JavaScript sources, as well as copy over assets.
-The contents of the `dist` folder will contain a runnable Open MCT
-instance (e.g. by starting an HTTP server in that directory), including:
-
-* A `main.js` file containing Open MCT source code.
-* Various assets in the `example` and `platform` directories.
-* An `index.html` that runs Open MCT in its default configuration.
-
-Additional `gulp` tasks are defined in [the gulpfile](gulpfile.js).
-
-Note that an internet connection is required to run this build, in order to
-download build dependencies.
-
-## Test Suite
-
-Open MCT uses [Jasmine 1.3](http://jasmine.github.io/) and
-[Karma](http://karma-runner.github.io) for automated testing.
-
-The test suite is configured to load any scripts ending with `Spec.js` found
-in the `src` hierarchy. Full configuration details are found in
-`karma.conf.js`. By convention, unit test scripts should be located
-alongside the units that they test; for example, `src/foo/Bar.js` would be
-tested by `src/foo/BarSpec.js`. (For legacy reasons, some existing tests may
-be located in separate `test` folders near the units they test, but the
-naming convention is otherwise the same.)
-
-Tests are written as AMD modules which depend (at minimum) upon the
-unit under test. For example, `src/foo/BarSpec.js` could look like:
-
- /*global define,Promise,describe,it,expect,beforeEach*/
-
- define(
- ["./Bar"],
- function (Bar) {
- "use strict";
-
- describe("Bar", function () {
- it("does something", function () {
- var bar = new Bar();
- expect(controller.baz()).toEqual("foo");
- });
- });
- }
- );
-
-
-## Code Coverage
-
-In addition to running tests, the test runner will also capture code coverage
-information using [Blanket.JS](http://blanketjs.org/) and display this at the
-bottom of the screen. Currently, only statement coverage is displayed.
-
-## Deployment
-Open MCT is built to be flexible in terms of the deployment strategies it
-supports. In order to run in the browser, Open MCT needs:
-
-1. HTTP access to sources/resources for the framework, platform, and all active
-bundles.
-2. Access to any external services utilized by active bundles. (This means that
-external services need to support HTTP or some other web-accessible interface,
-like WebSockets.)
-
-Any HTTP server capable of serving flat files is sufficient for the first point.
-The command-line build also packages Open MCT into a `.war` file for easier
-deployment on containers such as Apache Tomcat.
-
-The second point may be less flexible, as it depends upon the specific services
-to be utilized by Open MCT. Because of this, it is often the set of external
-services (and the manner in which they are exposed) that determine how to deploy
-Open MCT.
-
-One important constraint to consider in this context is the browser's same
-origin policy. If external services are not on the same apparent host and port
-as the client (from the perspective of the browser) then access may be
-disallowed. There are two workarounds if this occurs:
-
-* Make the external service appear to be on the same host/port, either by
-actually deploying it there, or by proxying requests to it.
-* Enable CORS (cross-origin resource sharing) on the external service. This is
-only possible if the external service can be configured to support CORS. Care
-should be exercised if choosing this option to ensure that the chosen
-configuration does not create a security vulnerability.
-
-Examples of deployment strategies (and the conditions under which they make the
-most sense) include:
-
-* If the external services that Open MCT will utilize are all running on
-[Apache Tomcat](https://tomcat.apache.org/), then it makes sense to run Open
-MCT Web from the same Tomcat instance as a separate web application. The
-`.war` artifact produced by the command line build facilitates this deployment
-option. (See https://tomcat.apache.org/tomcat-8.0-doc/deployer-howto.html for
-general information on deploying in Tomcat.)
-* If a variety of external services will be running from a variety of
-hosts/ports, then it may make sense to use a web server that supports proxying,
-such as the [Apache HTTP Server](http://httpd.apache.org/). In this
-configuration, the HTTP server would be configured to proxy (or reverse proxy)
-requests at specific paths to the various external services, while providing
-Open MCT as flat files from a different path.
-* If a single server component is being developed to handle all server-side
-needs of an Open MCT instance, it can make sense to serve Open MCT (as
-flat files) from the same component using an embedded HTTP server such as
-[Nancy](http://nancyfx.org/).
-* If no external services are needed (or if the 'external services' will just
-be generating flat files to read) it makes sense to utilize a lightweight flat
-file HTTP server such as [Lighttpd](http://www.lighttpd.net/). In this
-configuration, Open MCT sources/resources would be placed at one path, while
-the files generated by the external service are placed at another path.
-* If all external services support CORS, it may make sense to have an HTTP
-server that is solely responsible for making Open MCT sources/resources
-available, and to have Open MCT contact these external services directly.
-Again, lightweight HTTP servers such as [Lighttpd](http://www.lighttpd.net/)
-are useful in this circumstance. The downside of this option is that additional
-configuration effort is required, both to enable CORS on the external services,
-and to ensure that Open MCT can correctly locate these services.
-
-Another important consideration is authentication. By design, Open MCT does
-not handle user authentication. Instead, this should typically be treated as a
-deployment-time concern, where authentication is handled by the HTTP server
-which provides Open MCT, or an external access management system.
-
-### Configuration
-In most of the deployment options above, some level of configuration is likely
-to be needed or desirable to make sure that bundles can reach the external
-services they need to reach. Most commonly this means providing the path or URL
-to an external service.
-
-Configurable parameters within Open MCT are specified via constants
-(literally, as extensions of the `constants` category) and accessed via
-dependency injection by the scripts which need them. Reasonable defaults for
-these constants are provided in the bundle where they are used. Plugins are
-encouraged to follow the same pattern.
-
-Constants may be specified in any bundle; if multiple constants are specified
-with the same `key` the highest-priority one will be used. This allows default
-values to be overridden by specifying constants with higher priority.
-
-This permits at least three configuration approaches:
-
-* Modify the constants defined in their original bundles when deploying. This is
-generally undesirable due to the amount of manual work required and potential
-for error, but is viable if there are a small number of constants to change.
-* Add a separate configuration bundle which overrides the values of these
-constants. This is particularly appropriate when multiple configurations (e.g.
-development, test, production) need to be managed easily; these can be swapped
-quickly by changing the set of active bundles in bundles.json.
-* Deploy Open MCT and its external services in such a fashion that the
-default paths to reach external services are all correct.
-
-### Configuration Constants
-
-The following constants have global significance:
-* `PERSISTENCE_SPACE`: The space in which domain objects should be persisted
- (or read from) when not otherwise specified. Typically this will not need
- to be overridden by other bundles, but persistence adapters may wish to
- consume this constant in order to provide persistence for that space.
-
-The following configuration constants are recognized by Open MCT bundles:
-* Common UI elements - `platform/commonUI/general`
- * `THEME`: A string identifying the current theme symbolically. Individual
- stylesheets (the `stylesheets` extension category) may specify an optional
- `theme` property which will be matched against this before inclusion.
-* CouchDB adapter - `platform/persistence/couch`
- * `COUCHDB_PATH`: URL or path to the CouchDB database to be used for domain
- object persistence. Should not include a trailing slash.
-* ElasticSearch adapter - `platform/persistence/elastic`
- * `ELASTIC_ROOT`: URL or path to the ElasticSearch instance to be used for
- domain object persistence. Should not include a trailing slash.
- * `ELASTIC_PATH`: Path relative to the ElasticSearch instance where domain
- object models should be persisted. Should take the form `<index>/<type>`.
diff --git a/docs/src/index.md b/docs/src/index.md
index a09c2f823..3166ae601 100644
--- a/docs/src/index.md
+++ b/docs/src/index.md
@@ -22,16 +22,5 @@
* The [Development Process](process/) document describes the
Open MCT software development cycle.
-## Legacy Documentation
-
-As we transition to a new API, the following documentation for the old API
-(which is supported during the transition) may be useful as well:
-
- * The [Architecture Overview](architecture/) describes the concepts used
- throughout Open MCT, and gives a high level overview of the platform's design.
-
- * The [Developer's Guide](guide/) goes into more detail about how to use the
- platform and the functionality that it provides.
-
* The [Tutorials](https://github.com/nasa/openmct-tutorial) give examples of extending the platform to add
functionality, and integrate with data sources.
diff --git a/docs/src/process/index.md b/docs/src/process/index.md
index fc8f7e6e9..ae781b0de 100644
--- a/docs/src/process/index.md
+++ b/docs/src/process/index.md
@@ -7,9 +7,5 @@ documents:
process points are repeated during development.
* The [Version Guide](version.md) describes version numbering for
Open MCT (both semantics and process.)
-* Testing is described in two documents:
- * The [Test Plan](testing/plan.md) summarizes the approaches used
- to test Open MCT.
- * The [Test Procedures](testing/procedures.md) document what
- specific tests are performed to verify correctness, and how
- they should be carried out.
+* The [Test Plan](testing/plan.md) summarizes the approaches used
+ to test Open MCT.
diff --git a/docs/src/process/testing/procedures.md b/docs/src/process/testing/procedures.md
deleted file mode 100644
index 5ccf0def5..000000000
--- a/docs/src/process/testing/procedures.md
+++ /dev/null
@@ -1,169 +0,0 @@
-# Test Procedures
-
-## Introduction
-
-This document is intended to be used:
-
-* By testers, to verify that Open MCT behaves as specified.
-* By the development team, to document new test cases and to provide
- guidance on how to author these.
-
-## Writing Procedures
-
-### Template
-
-Procedures for individual tests should use the following template,
-adapted from [https://swehb.nasa.gov/display/7150/SWE-114]().
-
-Property | Value
----------------|---------------------------------------------------------------
-Test ID |
-Relevant reqs. |
-Prerequisites |
-Test input |
-Instructions |
-Expectation |
-Eval. criteria |
-
-For multi-line descriptions, use an asterisk or similar indicator to refer
-to a longer-form description below.
-
-#### Example Procedure - Edit a Layout
-
-Property | Value
----------------|---------------------------------------------------------------
-Test ID | MCT-TEST-000X - Edit a layout
-Relevant reqs. | MCT-EDIT-000Y
-Prerequisites | Create a layout, as in MCT-TEST-000Z
-Test input | Domain object database XYZ
-Instructions | See below &ast;
-Expectation | Change to editing context &dagger;
-Eval. criteria | Visual inspection
-
-&ast; Follow the following steps:
-
-1. Verify that the created layout is currently navigated-to,
- as in MCT-TEST-00ZZ.
-2. Click the Edit button, identified by a pencil icon and the text "Edit"
- displayed on hover.
-
-&dagger; Right-hand viewing area should be surrounded by a dashed
-blue border when a domain object is being edited.
-
-### Guidelines
-
-Test procedures should be written assuming minimal prior knowledge of the
-application: Non-standard terms should only be used when they are documented
-in [the glossary](#glossary), and shorthands used for user actions should
-be accompanied by useful references to test procedures describing those
-actions (when available) or descriptions in user documentation.
-
-Test cases should be narrow in scope; if a list of steps is excessively
-long (or must be written vaguely to be kept short) it should be broken
-down into multiple tests which reference one another.
-
-All requirements satisfied by Open MCT should be verifiable using
-one or more test procedures.
-
-## Glossary
-
-This section will contain terms used in test procedures. This may link to
-a common glossary, to avoid replication of content.
-
-## Procedures
-
-This section will contain specific test procedures. Presently, procedures
-are placeholders describing general patterns for setting up and conducting
-testing.
-
-### User Testing Setup
-
-These procedures describes a general pattern for setting up for user
-testing. Specific deployments should customize this pattern with
-relevant data and any additional steps necessary.
-
-Property | Value
----------------|---------------------------------------------------------------
-Test ID | MCT-TEST-SETUP0 - User Testing Setup
-Relevant reqs. | TBD
-Prerequisites | Build of relevant components
-Test input | Exemplary database; exemplary telemetry data set
-Instructions | See below
-Expectation | Able to load application in a web browser (Google Chrome)
-Eval. criteria | Visual inspection
-
-Instructions:
-
-1. Start telemetry server.
-2. Start ElasticSearch.
-3. Restore database snapshot to ElasticSearch.
-4. Start telemetry playback.
-5. Start HTTP server for client sources.
-
-### User Test Procedures
-
-Specific user test cases have not yet been authored. In their absence,
-user testing is conducted by:
-
-* Reviewing the text of issues from the issue tracker to understand the
- desired behavior, and exercising this behavior in the running application.
- (For instance, by following steps to reproduce from the original issue.)
- * Issues which appear to be resolved should be marked as such with comments
- on the original issue (e.g. "verified during user testing MM/DD/YYYY".)
- * Issues which appear not to have been resolved should be reopened with an
- explanation of what unexpected behavior has been observed.
- * In cases where an issue appears resolved as-worded but other related
- undesirable behavior is observed during testing, a new issue should be
- opened, and linked to from a comment in the original issues.
-* General usage of new features and/or existing features which have undergone
- recent changes. Defects or problems with usability should be documented
- by filing issues in the issue tracker.
-* Open-ended testing to discover defects, identify usability issues, and
- generate feature requests.
-
-### Long-Duration Testing
-
-The purpose of long-duration testing is to identify performance issues
-and/or other defects which are sensitive to the amount of time the
-application is kept running. (Memory leaks, for instance.)
-
-Property | Value
----------------|---------------------------------------------------------------
-Test ID | MCT-TEST-LDT0 - Long-duration Testing
-Relevant reqs. | TBD
-Prerequisites | MCT-TEST-SETUP0
-Test input | (As for test setup.)
-Instructions | See "Instructions" below &ast;
-Expectation | See "Expectations" below &dagger;
-Eval. criteria | Visual inspection
-
-&ast; Instructions:
-
-1. Start `top` or a similar tool to measure CPU usage and memory utilization.
-2. Open several user-created displays (as many as would be realistically
- opened during actual usage in a stressing case) in some combination of
- separate tabs and windows (approximately as many tabs-per-window as
- total windows.)
-3. Ensure that playback data is set to run continuously for at least 24 hours
- (e.g. on a loop.)
-4. Record CPU usage and memory utilization.
-5. In at least one tab, try some general user interface gestures and make
- notes about the subjective experience of using the application. (Particularly,
- the degree of responsiveness.)
-6. Leave client displays open for 24 hours.
-7. Record CPU usage and memory utilization again.
-8. Make additional notes about the subjective experience of using the
- application (again, particularly responsiveness.)
-9. Check logs for any unexpected warnings or errors.
-
-&dagger; Expectations:
-
-* At the end of the test, CPU usage and memory usage should both be similar
- to their levels at the start of the test.
-* At the end of the test, subjective usage of the application should not
- be observably different from the way it was at the start of the test.
- (In particular, responsiveness should not decrease.)
-* Logs should not contain any unexpected warnings or errors ("expected"
- warnings or errors are those that have been documented and prioritized
- as known issues, or those that are explained by transient conditions
- external to the software, such as network outages.)
diff --git a/e2e/.eslintrc.js b/e2e/.eslintrc.js
index 78d37c29a..3388deb35 100644
--- a/e2e/.eslintrc.js
+++ b/e2e/.eslintrc.js
@@ -1,4 +1,12 @@
/* eslint-disable no-undef */
module.exports = {
- "extends": ["plugin:playwright/playwright-test"]
+ "extends": ["plugin:playwright/playwright-test"],
+ "overrides": [
+ {
+ "files": ["tests/visual/*.spec.js"],
+ "rules": {
+ "playwright/no-wait-for-timeout": "off"
+ }
+ }
+ ]
};
diff --git a/e2e/.percy.yml b/e2e/.percy.yml
new file mode 100644
index 000000000..fc3ff095d
--- /dev/null
+++ b/e2e/.percy.yml
@@ -0,0 +1,5 @@
+version: 2
+snapshot:
+ widths: [1024, 2000]
+ min-height: 1440 # px
+
diff --git a/e2e/fixtures.js b/e2e/fixtures.js
new file mode 100644
index 000000000..ce46de5bb
--- /dev/null
+++ b/e2e/fixtures.js
@@ -0,0 +1,69 @@
+/* This file extends the base functionality of the playwright test framework to enable
+ * code coverage instrumentation, console log error detection and working with a 3rd
+ * party Chrome-as-a-service extension called Browserless.
+ */
+
+const base = require('@playwright/test');
+const { expect } = require('@playwright/test');
+const fs = require('fs');
+const path = require('path');
+const { v4: uuid } = require('uuid');
+
+/**
+ * Takes a `ConsoleMessage` and returns a formatted string
+ * @param {import('@playwright/test').ConsoleMessage} msg
+ * @returns {String} formatted string with message type, text, url, and line and column numbers
+ */
+function consoleMessageToString(msg) {
+ const { url, lineNumber, columnNumber } = msg.location();
+
+ return `[${msg.type()}] ${msg.text()}
+ at (${url} ${lineNumber}:${columnNumber})`;
+}
+
+//The following is based on https://github.com/mxschmitt/playwright-test-coverage
+// eslint-disable-next-line no-undef
+const istanbulCLIOutput = path.join(process.cwd(), '.nyc_output');
+
+// eslint-disable-next-line no-undef
+exports.test = base.test.extend({
+ //The following is based on https://github.com/mxschmitt/playwright-test-coverage
+ context: async ({ context }, use) => {
+ await context.addInitScript(() =>
+ window.addEventListener('beforeunload', () =>
+ (window).collectIstanbulCoverage(JSON.stringify((window).__coverage__))
+ )
+ );
+ await fs.promises.mkdir(istanbulCLIOutput, { recursive: true });
+ await context.exposeFunction('collectIstanbulCoverage', (coverageJSON) => {
+ if (coverageJSON) {
+ fs.writeFileSync(path.join(istanbulCLIOutput, `playwright_coverage_${uuid()}.json`), coverageJSON);
+ }
+ });
+ await use(context);
+ for (const page of context.pages()) {
+ await page.evaluate(() => (window).collectIstanbulCoverage(JSON.stringify((window).__coverage__)));
+ }
+ },
+ page: async ({ baseURL, page }, use) => {
+ const messages = [];
+ page.on('console', (msg) => messages.push(msg));
+ await use(page);
+ messages.forEach(
+ msg => expect.soft(msg.type(), `Console error detected: ${consoleMessageToString(msg)}`).not.toEqual('error')
+ );
+ },
+ browser: async ({ playwright, browser }, use, workerInfo) => {
+ // Use browserless if configured
+ if (workerInfo.project.name.match(/browserless/)) {
+ const vBrowser = await playwright.chromium.connectOverCDP({
+ endpointURL: 'ws://localhost:3003'
+ });
+ await use(vBrowser);
+ } else {
+ // Use Local Browser for testing.
+ await use(browser);
+ }
+ }
+});
+
diff --git a/e2e/playwright-ci.config.js b/e2e/playwright-ci.config.js
index a6d0d62a3..a0139f56b 100644
--- a/e2e/playwright-ci.config.js
+++ b/e2e/playwright-ci.config.js
@@ -2,30 +2,78 @@
// playwright.config.js
// @ts-check
+// eslint-disable-next-line no-unused-vars
+const { devices } = require('@playwright/test');
+const MAX_FAILURES = 5;
+const NUM_WORKERS = 2;
+
/** @type {import('@playwright/test').PlaywrightTestConfig} */
const config = {
- retries: 2,
+ retries: 3, //Retries 3 times for a total of 4. When running sharded and with maxFailures = 5, this should ensure that flake is managed without failing the full suite
testDir: 'tests',
- timeout: 90 * 1000,
+ testIgnore: '**/*.perf.spec.js', //Ignore performance tests and define in playwright-perfromance.config.js
+ timeout: 60 * 1000,
webServer: {
- command: 'npm run start',
- port: 8080,
+ command: 'cross-env NODE_ENV=test npm run start',
+ url: 'http://localhost:8080/#',
timeout: 200 * 1000,
- reuseExistingServer: !process.env.CI
+ reuseExistingServer: false
},
+ maxFailures: MAX_FAILURES, //Limits failures to 5 to reduce CI Waste
+ workers: NUM_WORKERS, //Limit to 2 for CircleCI Agent
use: {
- browserName: "chromium",
baseURL: 'http://localhost:8080/',
headless: true,
ignoreHTTPSErrors: true,
- screenshot: 'on',
- trace: 'on',
- video: 'on'
+ screenshot: 'only-on-failure',
+ trace: 'on-first-retry',
+ video: 'off'
},
+ projects: [
+ {
+ name: 'chrome',
+ use: {
+ browserName: 'chromium'
+ }
+ },
+ {
+ name: 'MMOC',
+ testMatch: '**/*.e2e.spec.js', // only run e2e tests
+ grepInvert: /@snapshot/,
+ use: {
+ browserName: 'chromium',
+ viewport: {
+ width: 2560,
+ height: 1440
+ }
+ }
+ },
+ {
+ name: 'firefox',
+ testMatch: '**/*.e2e.spec.js', // only run e2e tests
+ grepInvert: /@snapshot/,
+ use: {
+ browserName: 'firefox'
+ }
+ },
+ {
+ name: 'chrome-beta', //Only Chrome Beta is available on ubuntu -- not chrome canary
+ testMatch: '**/*.e2e.spec.js', // only run e2e tests
+ grepInvert: /@snapshot/,
+ use: {
+ browserName: 'chromium',
+ channel: 'chrome-beta'
+ }
+ }
+ ],
reporter: [
['list'],
+ ['html', {
+ open: 'never',
+ outputFolder: '../html-test-results' //Must be in different location due to https://github.com/microsoft/playwright/issues/12840
+ }],
['junit', { outputFile: 'test-results/results.xml' }],
- ['allure-playwright']
+ ['github']
]
};
diff --git a/e2e/playwright-local.config.js b/e2e/playwright-local.config.js
index 82b7231a1..d79c702b1 100644
--- a/e2e/playwright-local.config.js
+++ b/e2e/playwright-local.config.js
@@ -2,29 +2,102 @@
// playwright.config.js
// @ts-check
+// eslint-disable-next-line no-unused-vars
+const { devices } = require('@playwright/test');
+
/** @type {import('@playwright/test').PlaywrightTestConfig} */
const config = {
retries: 0,
testDir: 'tests',
+ testIgnore: '**/*.perf.spec.js',
timeout: 30 * 1000,
webServer: {
- command: 'npm run start',
- port: 8080,
+ command: 'cross-env NODE_ENV=test npm run start',
+ url: 'http://localhost:8080/#',
timeout: 120 * 1000,
- reuseExistingServer: !process.env.CI
+ reuseExistingServer: true
},
+ workers: 1,
use: {
browserName: "chromium",
baseURL: 'http://localhost:8080/',
headless: false,
ignoreHTTPSErrors: true,
- screenshot: 'on',
- trace: 'on',
- video: 'on'
+ screenshot: 'only-on-failure',
+ trace: 'retain-on-failure',
+ video: 'off'
},
+ projects: [
+ {
+ name: 'chrome',
+ use: {
+ browserName: 'chromium'
+ }
+ },
+ {
+ name: 'MMOC',
+ testMatch: '**/*.e2e.spec.js', // only run e2e tests
+ grepInvert: /@snapshot/,
+ use: {
+ browserName: 'chromium',
+ viewport: {
+ width: 2560,
+ height: 1440
+ }
+ }
+ },
+ {
+ name: 'safari',
+ testMatch: '**/*.e2e.spec.js', // only run e2e tests
+ grep: /@ipad/, // only run ipad tests due to this bug https://github.com/microsoft/playwright/issues/8340
+ grepInvert: /@snapshot/,
+ use: {
+ browserName: 'webkit'
+ }
+ },
+ {
+ name: 'firefox',
+ testMatch: '**/*.e2e.spec.js', // only run e2e tests
+ grepInvert: /@snapshot/,
+ use: {
+ browserName: 'firefox'
+ }
+ },
+ {
+ name: 'canary',
+ testMatch: '**/*.e2e.spec.js', // only run e2e tests
+ grepInvert: /@snapshot/,
+ use: {
+ browserName: 'chromium',
+ channel: 'chrome-canary' //Note this is not available in ubuntu/CircleCI
+ }
+ },
+ {
+ name: 'chrome-beta',
+ testMatch: '**/*.e2e.spec.js', // only run e2e tests
+ grepInvert: /@snapshot/,
+ use: {
+ browserName: 'chromium',
+ channel: 'chrome-beta'
+ }
+ },
+ {
+ name: 'ipad',
+ testMatch: '**/*.e2e.spec.js', // only run e2e tests
+ grep: /@ipad/,
+ grepInvert: /@snapshot/,
+ use: {
+ browserName: 'webkit',
+ ...devices['iPad (gen 7) landscape'] // Complete List https://github.com/microsoft/playwright/blob/main/packages/playwright-core/src/server/deviceDescriptorsSource.json
+ }
+ }
+ ],
reporter: [
['list'],
- ['allure-playwright']
+ ['html', {
+ open: 'on-failure',
+ outputFolder: '../html-test-results' //Must be in different location due to https://github.com/microsoft/playwright/issues/12840
+ }]
]
};
diff --git a/e2e/playwright-performance.config.js b/e2e/playwright-performance.config.js
new file mode 100644
index 000000000..de79304f1
--- /dev/null
+++ b/e2e/playwright-performance.config.js
@@ -0,0 +1,43 @@
+/* eslint-disable no-undef */
+// playwright.config.js
+// @ts-check
+
+const CI = process.env.CI === 'true';
+
+/** @type {import('@playwright/test').PlaywrightTestConfig} */
+const config = {
+ retries: 1, //Only for debugging purposes because trace is enabled only on first retry
+ testDir: 'tests/performance/',
+ timeout: 60 * 1000,
+ workers: 1, //Only run in serial with 1 worker
+ webServer: {
+ command: 'cross-env NODE_ENV=test npm run start',
+ url: 'http://localhost:8080/#',
+ timeout: 200 * 1000,
+ reuseExistingServer: !CI
+ },
+ use: {
+ browserName: "chromium",
+ baseURL: 'http://localhost:8080/',
+ headless: CI, //Only if running locally
+ ignoreHTTPSErrors: true,
+ screenshot: 'off',
+ trace: 'on-first-retry',
+ video: 'off'
+ },
+ projects: [
+ {
+ name: 'chrome',
+ use: {
+ browserName: 'chromium'
+ }
+ }
+ ],
+ reporter: [
+ ['list'],
+ ['junit', { outputFile: 'test-results/results.xml' }],
+ ['json', { outputFile: 'test-results/results.json' }]
+ ]
+};
+
+module.exports = config;
diff --git a/e2e/playwright-visual.config.js b/e2e/playwright-visual.config.js
index 7f6df513f..55570b049 100644
--- a/e2e/playwright-visual.config.js
+++ b/e2e/playwright-visual.config.js
@@ -4,29 +4,28 @@
/** @type {import('@playwright/test').PlaywrightTestConfig} */
const config = {
- retries: 0,
- testDir: 'tests',
+ retries: 0, // visual tests should never retry due to snapshot comparison errors
+ testDir: 'tests/visual',
timeout: 90 * 1000,
- workers: 1,
+ workers: 1, // visual tests should never run in parallel due to test pollution
webServer: {
- command: 'npm run start',
- port: 8080,
+ command: 'cross-env NODE_ENV=test npm run start',
+ url: 'http://localhost:8080/#',
timeout: 200 * 1000,
reuseExistingServer: !process.env.CI
},
use: {
browserName: "chromium",
baseURL: 'http://localhost:8080/',
- headless: true,
+ headless: true, // this needs to remain headless to avoid visual changes due to GPU
ignoreHTTPSErrors: true,
screenshot: 'on',
trace: 'off',
- video: 'on'
+ video: 'off'
},
reporter: [
['list'],
- ['junit', { outputFile: 'test-results/results.xml' }],
- ['allure-playwright']
+ ['junit', { outputFile: 'test-results/results.xml' }]
]
};
diff --git a/e2e/test-data/PerformanceDisplayLayout.json b/e2e/test-data/PerformanceDisplayLayout.json
new file mode 100644
index 000000000..de81d7b4c
--- /dev/null
+++ b/e2e/test-data/PerformanceDisplayLayout.json
@@ -0,0 +1 @@
+{"openmct":{"b3cee102-86dd-4c0a-8eec-4d5d276f8691":{"identifier":{"key":"b3cee102-86dd-4c0a-8eec-4d5d276f8691","namespace":""},"name":"Performance Display Layout","type":"layout","composition":[{"key":"9666e7b4-be0c-47a5-94b8-99accad7155e","namespace":""}],"configuration":{"items":[{"width":32,"height":18,"x":12,"y":9,"identifier":{"key":"9666e7b4-be0c-47a5-94b8-99accad7155e","namespace":""},"hasFrame":true,"fontSize":"default","font":"default","type":"subobject-view","id":"23ca351d-a67d-46aa-a762-290eb742d2f1"}],"layoutGrid":[10,10]},"modified":1654299875432,"location":"mine","persisted":1654299878751},"9666e7b4-be0c-47a5-94b8-99accad7155e":{"identifier":{"key":"9666e7b4-be0c-47a5-94b8-99accad7155e","namespace":""},"name":"Performance Example Imagery","type":"example.imagery","configuration":{"imageLocation":"","imageLoadDelayInMilliSeconds":20000,"imageSamples":[],"layers":[{"source":"dist/imagery/example-imagery-layer-16x9.png","name":"16:9","visible":false},{"source":"dist/imagery/example-imagery-layer-safe.png","name":"Safe","visible":false},{"source":"dist/imagery/example-imagery-layer-scale.png","name":"Scale","visible":false}]},"telemetry":{"values":[{"name":"Name","key":"name"},{"name":"Time","key":"utc","format":"utc","hints":{"domain":2}},{"name":"Local Time","key":"local","format":"local-format","hints":{"domain":1}},{"name":"Image","key":"url","format":"image","hints":{"image":1},"layers":[{"source":"dist/imagery/example-imagery-layer-16x9.png","name":"16:9"},{"source":"dist/imagery/example-imagery-layer-safe.png","name":"Safe"},{"source":"dist/imagery/example-imagery-layer-scale.png","name":"Scale"}]},{"name":"Image Download Name","key":"imageDownloadName","format":"imageDownloadName","hints":{"imageDownloadName":1}}]},"modified":1654299840077,"location":"b3cee102-86dd-4c0a-8eec-4d5d276f8691","persisted":1654299840078}},"rootId":"b3cee102-86dd-4c0a-8eec-4d5d276f8691"} \ No newline at end of file
diff --git a/e2e/test-data/PerformanceNotebook.json b/e2e/test-data/PerformanceNotebook.json
new file mode 100644
index 000000000..ae0843187
--- /dev/null
+++ b/e2e/test-data/PerformanceNotebook.json
@@ -0,0 +1 @@
+{"openmct":{"6d2fa9fd-f2aa-461a-a1e1-164ac44bec9d":{"identifier":{"key":"6d2fa9fd-f2aa-461a-a1e1-164ac44bec9d","namespace":""},"name":"Performance Notebook","type":"notebook","configuration":{"defaultSort":"oldest","entries":{"3e31c412-33ba-4757-8ade-e9821f6ba321":{"8c8f6035-631c-45af-8c24-786c60295335":[{"id":"entry-1652815305457","createdOn":1652815305457,"createdBy":"","text":"Existing Entry 1","embeds":[]},{"id":"entry-1652815313465","createdOn":1652815313465,"createdBy":"","text":"Existing Entry 2","embeds":[]},{"id":"entry-1652815399955","createdOn":1652815399955,"createdBy":"","text":"Existing Entry 3","embeds":[]}]}},"imageMigrationVer":"v1","pageTitle":"Page","sections":[{"id":"3e31c412-33ba-4757-8ade-e9821f6ba321","isDefault":false,"isSelected":false,"name":"Section1","pages":[{"id":"8c8f6035-631c-45af-8c24-786c60295335","isDefault":false,"isSelected":false,"name":"Page1","pageTitle":"Page"},{"id":"36555942-c9aa-439c-bbdb-0aaf50db50f5","isDefault":false,"isSelected":false,"name":"Page2","pageTitle":"Page"}],"sectionTitle":"Section"},{"id":"dab0bd1d-2c5a-405c-987f-107123d6189a","isDefault":false,"isSelected":true,"name":"Section2","pages":[{"id":"f625a86a-cb99-4898-8082-80543c8de534","isDefault":false,"isSelected":false,"name":"Page1","pageTitle":"Page"},{"id":"e77ef810-f785-42a7-942e-07e999b79c59","isDefault":false,"isSelected":true,"name":"Page2","pageTitle":"Page"}],"sectionTitle":"Section"}],"sectionTitle":"Section","type":"General","showTime":"0"},"modified":1652815915219,"location":"mine","persisted":1652815915222}},"rootId":"6d2fa9fd-f2aa-461a-a1e1-164ac44bec9d"} \ No newline at end of file
diff --git a/e2e/test-data/VisualTestData_storage.json b/e2e/test-data/VisualTestData_storage.json
new file mode 100644
index 000000000..d05930326
--- /dev/null
+++ b/e2e/test-data/VisualTestData_storage.json
@@ -0,0 +1,22 @@
+{
+ "cookies": [],
+ "origins": [
+ {
+ "origin": "http://localhost:8080",
+ "localStorage": [
+ {
+ "name": "tcHistory",
+ "value": "{\"utc\":[{\"start\":1654548551471,\"end\":1654550351471}]}"
+ },
+ {
+ "name": "mct",
+ "value": "{\"mine\":{\"identifier\":{\"key\":\"mine\",\"namespace\":\"\"},\"name\":\"My Items\",\"type\":\"folder\",\"composition\":[{\"key\":\"527856c0-cced-4b64-bb19-f943432326d0\",\"namespace\":\"\"}],\"location\":\"ROOT\",\"persisted\":1654550352296,\"modified\":1654550352296},\"527856c0-cced-4b64-bb19-f943432326d0\":{\"identifier\":{\"key\":\"527856c0-cced-4b64-bb19-f943432326d0\",\"namespace\":\"\"},\"name\":\"Unnamed Overlay Plot\",\"type\":\"telemetry.plot.overlay\",\"composition\":[{\"key\":\"ce88ce37-8bb9-45e1-a85b-bb7e3c8453b9\",\"namespace\":\"\"}],\"configuration\":{\"series\":[{\"identifier\":{\"key\":\"ce88ce37-8bb9-45e1-a85b-bb7e3c8453b9\",\"namespace\":\"\"}}],\"yAxis\":{},\"xAxis\":{}},\"modified\":1654550353356,\"location\":\"mine\",\"persisted\":1654550353357},\"ce88ce37-8bb9-45e1-a85b-bb7e3c8453b9\":{\"name\":\"Unnamed Sine Wave Generator\",\"type\":\"generator\",\"identifier\":{\"key\":\"ce88ce37-8bb9-45e1-a85b-bb7e3c8453b9\",\"namespace\":\"\"},\"telemetry\":{\"period\":10,\"amplitude\":1,\"offset\":0,\"dataRateInHz\":1,\"phase\":0,\"randomness\":0,\"loadDelay\":\"5000\"},\"modified\":1654550353350,\"location\":\"527856c0-cced-4b64-bb19-f943432326d0\",\"persisted\":1654550353350}}"
+ },
+ {
+ "name": "mct-tree-expanded",
+ "value": "[\"/browse/mine\"]"
+ }
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/e2e/test-data/recycled_local_storage.json b/e2e/test-data/recycled_local_storage.json
new file mode 100644
index 000000000..5026bca3b
--- /dev/null
+++ b/e2e/test-data/recycled_local_storage.json
@@ -0,0 +1,22 @@
+{
+ "cookies": [],
+ "origins": [
+ {
+ "origin": "http://localhost:8080",
+ "localStorage": [
+ {
+ "name": "tcHistory",
+ "value": "{\"utc\":[{\"start\":1654537164464,\"end\":1654538964464},{\"start\":1652301954635,\"end\":1652303754635}]}"
+ },
+ {
+ "name": "mct",
+ "value": "{\"mine\":{\"identifier\":{\"key\":\"mine\",\"namespace\":\"\"},\"name\":\"My Items\",\"type\":\"folder\",\"composition\":[{\"key\":\"f64bea3b-58a7-4586-8c05-8b651e5f0bfd\",\"namespace\":\"\"},{\"key\":\"2d02a680-eb7e-4645-bba2-dd298f76efb8\",\"namespace\":\"\"}],\"location\":\"ROOT\",\"persisted\":1654538965703,\"modified\":1654538965703},\"f64bea3b-58a7-4586-8c05-8b651e5f0bfd\":{\"name\":\"Unnamed Condition Set\",\"type\":\"conditionSet\",\"identifier\":{\"key\":\"f64bea3b-58a7-4586-8c05-8b651e5f0bfd\",\"namespace\":\"\"},\"configuration\":{\"conditionTestData\":[],\"conditionCollection\":[{\"isDefault\":true,\"id\":\"73f2d9ae-d1f3-4561-b7fc-ecd5df557249\",\"configuration\":{\"name\":\"Default\",\"output\":\"Default\",\"trigger\":\"all\",\"criteria\":[]},\"summary\":\"Default condition\"}]},\"composition\":[],\"telemetry\":{},\"modified\":1652303755999,\"location\":\"mine\",\"persisted\":1652303756002},\"2d02a680-eb7e-4645-bba2-dd298f76efb8\":{\"name\":\"Unnamed Condition Set\",\"type\":\"conditionSet\",\"identifier\":{\"key\":\"2d02a680-eb7e-4645-bba2-dd298f76efb8\",\"namespace\":\"\"},\"configuration\":{\"conditionTestData\":[],\"conditionCollection\":[{\"isDefault\":true,\"id\":\"4291d80c-303c-4d8d-85e1-10f012b864fb\",\"configuration\":{\"name\":\"Default\",\"output\":\"Default\",\"trigger\":\"all\",\"criteria\":[]},\"summary\":\"Default condition\"}]},\"composition\":[],\"telemetry\":{},\"modified\":1654538965702,\"location\":\"mine\",\"persisted\":1654538965702}}"
+ },
+ {
+ "name": "mct-tree-expanded",
+ "value": "[]"
+ }
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/e2e/tests/api/forms/forms.e2e.spec.js b/e2e/tests/api/forms/forms.e2e.spec.js
new file mode 100644
index 000000000..0e5be59c2
--- /dev/null
+++ b/e2e/tests/api/forms/forms.e2e.spec.js
@@ -0,0 +1,77 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+/*
+This test suite is dedicated to tests which verify form functionality.
+*/
+
+const { test, expect } = require('@playwright/test');
+
+const TEST_FOLDER = 'test folder';
+
+test.describe('forms set', () => {
+ test('New folder form has title as required field', async ({ page }) => {
+ //Go to baseURL
+ await page.goto('/', { waitUntil: 'networkidle' });
+
+ // Click button:has-text("Create")
+ await page.click('button:has-text("Create")');
+ // Click :nth-match(:text("Folder"), 2)
+ await page.click(':nth-match(:text("Folder"), 2)');
+ // Click text=Properties Title Notes >> input[type="text"]
+ await page.click('text=Properties Title Notes >> input[type="text"]');
+ // Fill text=Properties Title Notes >> input[type="text"]
+ await page.fill('text=Properties Title Notes >> input[type="text"]', '');
+ // Press Tab
+ await page.press('text=Properties Title Notes >> input[type="text"]', 'Tab');
+
+ const okButton = page.locator('text=OK');
+
+ await expect(okButton).toBeDisabled();
+ await expect(page.locator('.c-form-row__state-indicator').first()).toHaveClass(/invalid/);
+
+ // Click text=Properties Title Notes >> input[type="text"]
+ await page.click('text=Properties Title Notes >> input[type="text"]');
+ // Fill text=Properties Title Notes >> input[type="text"]
+ await page.fill('text=Properties Title Notes >> input[type="text"]', TEST_FOLDER);
+ // Press Tab
+ await page.press('text=Properties Title Notes >> input[type="text"]', 'Tab');
+
+ await expect(page.locator('.c-form-row__state-indicator').first()).not.toHaveClass(/invalid/);
+
+ // Click text=OK
+ await Promise.all([
+ page.waitForNavigation(),
+ page.click('text=OK')
+ ]);
+
+ await expect(page.locator('.l-browse-bar__object-name')).toContainText(TEST_FOLDER);
+ });
+ test.fixme('Create all object types and verify correctness', async ({ page }) => {
+ //Create the following Domain Objects with their unique Object Types
+ // Sine Wave Generator (number object)
+ // Timer Object
+ // Plan View Object
+ // Clock Object
+ // Hyperlink
+ });
+});
diff --git a/e2e/tests/branding.e2e.spec.js b/e2e/tests/branding.e2e.spec.js
new file mode 100644
index 000000000..b543abf86
--- /dev/null
+++ b/e2e/tests/branding.e2e.spec.js
@@ -0,0 +1,64 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+/*
+This test suite is dedicated to tests which verify branding related components.
+*/
+
+const { test } = require('../fixtures.js');
+const { expect } = require('@playwright/test');
+
+test.describe('Branding tests', () => {
+ test('About Modal launches with basic branding properties', async ({ page }) => {
+ // Go to baseURL
+ await page.goto('/', { waitUntil: 'networkidle' });
+
+ // Click About button
+ await page.click('.l-shell__app-logo');
+
+ // Verify that the NASA Logo Appears
+ await expect(page.locator('.c-about__image')).toBeVisible();
+
+ // Modify the Build information in 'about' Modal
+ const versionInformationLocator = page.locator('ul.t-info.l-info.s-info');
+ await expect(versionInformationLocator).toBeEnabled();
+ await expect.soft(versionInformationLocator).toContainText(/Version: \d/);
+ await expect.soft(versionInformationLocator).toContainText(/Build Date: ((?:Mon|Tue|Wed|Thu|Fri|Sat|Sun))/);
+ await expect.soft(versionInformationLocator).toContainText(/Revision: \b[0-9a-f]{5,40}\b/);
+ await expect.soft(versionInformationLocator).toContainText(/Branch: ./);
+ });
+ test('Verify Links in About Modal', async ({ page }) => {
+ // Go to baseURL
+ await page.goto('/', { waitUntil: 'networkidle' });
+
+ // Click About button
+ await page.click('.l-shell__app-logo');
+
+ // Verify that clicking on the third party licenses information opens up another tab on licenses url
+ const [page2] = await Promise.all([
+ page.waitForEvent('popup'),
+ page.locator('text=click here for third party licensing information').click()
+ ]);
+ await page2.waitForLoadState('networkidle'); //Avoids timing issues with juggler/firefox
+ expect(page2.waitForURL('**/licenses**')).toBeTruthy();
+ });
+});
diff --git a/e2e/tests/example/eventGenerator.e2e.spec.js b/e2e/tests/example/eventGenerator.e2e.spec.js
new file mode 100644
index 000000000..c94d652af
--- /dev/null
+++ b/e2e/tests/example/eventGenerator.e2e.spec.js
@@ -0,0 +1,63 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+/*
+This test suite is dedicated to tests which verify the basic operations surrounding the example event generator.
+*/
+
+const { test } = require('../../fixtures.js');
+const { expect } = require('@playwright/test');
+
+test.describe('Example Event Generator Operations', () => {
+ test('Can create example event generator with a name', async ({ page }) => {
+ //Go to baseURL
+ await page.goto('/', { waitUntil: 'networkidle' });
+ // let's make an event generator
+ await page.locator('button:has-text("Create")').click();
+ // Click li:has-text("Event Message Generator")
+ await page.locator('li:has-text("Event Message Generator")').click();
+ // Click text=Properties Title Notes >> input[type="text"]
+ await page.locator('text=Properties Title Notes >> input[type="text"]').click();
+ // Fill text=Properties Title Notes >> input[type="text"]
+ await page.locator('text=Properties Title Notes >> input[type="text"]').fill('Test Event Generator');
+ // Press Enter
+ await page.locator('text=Properties Title Notes >> input[type="text"]').press('Enter');
+ // Click text=OK
+ await Promise.all([
+ page.waitForNavigation({ url: /.*&view=table/ }),
+ page.locator('text=OK').click()
+ ]);
+
+ await expect(page.locator('.l-browse-bar__object-name')).toContainText('Test Event Generator');
+ // Click button:has-text("Fixed Timespan")
+ await page.locator('button:has-text("Fixed Timespan")').click();
+ });
+
+ test.fixme('telemetry is coming in for test event', async ({ page }) => {
+ // Go to object created in step one
+ // Verify the telemetry table is filled with > 1 row
+ });
+ test.fixme('telemetry is sorted by time ascending', async ({ page }) => {
+ // Go to object created in step one
+ // Verify the telemetry table has a class with "is-sorting asc"
+ });
+});
diff --git a/e2e/tests/example/generator/SinewaveLimitProvider.e2e.spec.js b/e2e/tests/example/generator/SinewaveLimitProvider.e2e.spec.js
new file mode 100644
index 000000000..972e410df
--- /dev/null
+++ b/e2e/tests/example/generator/SinewaveLimitProvider.e2e.spec.js
@@ -0,0 +1,119 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+/*
+This test suite is dedicated to tests which verify the basic operations surrounding conditionSets.
+*/
+
+const { test } = require('../../../fixtures.js');
+const { expect } = require('@playwright/test');
+
+test.describe('Sine Wave Generator', () => {
+ test('Create new Sine Wave Generator Object and validate create Form Logic', async ({ page, browserName }) => {
+ test.fixme(browserName === 'firefox', 'This test needs to be updated to work with firefox');
+
+ //Go to baseURL
+ await page.goto('/', { waitUntil: 'networkidle' });
+
+ //Click the Create button
+ await page.click('button:has-text("Create")');
+
+ // Click Sine Wave Generator
+ await page.click('text=Sine Wave Generator');
+
+ // Verify that the each required field has required indicator
+ // Title
+ await expect(page.locator('.c-form-row__state-indicator').first()).toHaveClass(/req/);
+
+ // Verify that the Notes row does not have a required indicator
+ await expect(page.locator('.c-form__section div:nth-child(3) .form-row .c-form-row__state-indicator')).not.toContain('.req');
+ await page.locator('textarea[type="text"]').fill('Optional Note Text');
+
+ // Period
+ await expect(page.locator('div:nth-child(4) .c-form-row__state-indicator')).toHaveClass(/req/);
+
+ // Amplitude
+ await expect(page.locator('div:nth-child(5) .c-form-row__state-indicator')).toHaveClass(/req/);
+
+ // Offset
+ await expect(page.locator('div:nth-child(6) .c-form-row__state-indicator')).toHaveClass(/req/);
+
+ // Data Rate
+ await expect(page.locator('div:nth-child(7) .c-form-row__state-indicator')).toHaveClass(/req/);
+
+ // Phase
+ await expect(page.locator('div:nth-child(8) .c-form-row__state-indicator')).toHaveClass(/req/);
+
+ // Randomness
+ await expect(page.locator('div:nth-child(9) .c-form-row__state-indicator')).toHaveClass(/req/);
+
+ // Verify that by removing value from required text field shows invalid indicator
+ await page.locator('text=Properties Title Notes Period Amplitude Offset Data Rate (hz) Phase (radians) Ra >> input[type="text"]').fill('');
+ await expect(page.locator('.c-form-row__state-indicator').first()).toHaveClass(/invalid/);
+
+ // Verify that by adding value to empty required text field changes invalid to valid indicator
+ await page.locator('text=Properties Title Notes Period Amplitude Offset Data Rate (hz) Phase (radians) Ra >> input[type="text"]').fill('New Sine Wave Generator');
+ await expect(page.locator('.c-form-row__state-indicator').first()).toHaveClass(/valid/);
+
+ // Verify that by removing value from required number field shows invalid indicator
+ await page.locator('.field.control.l-input-sm input').first().fill('');
+ await expect(page.locator('div:nth-child(4) .c-form-row__state-indicator')).toHaveClass(/invalid/);
+
+ // Verify that by adding value to empty required number field changes invalid to valid indicator
+ await page.locator('.field.control.l-input-sm input').first().fill('3');
+ await expect(page.locator('div:nth-child(4) .c-form-row__state-indicator')).toHaveClass(/valid/);
+
+ // Verify that can change value of number field by up/down arrows keys
+ // Click .field.control.l-input-sm input >> nth=0
+ await page.locator('.field.control.l-input-sm input').first().click();
+ // Press ArrowUp 3 times to change value from 3 to 6
+ await page.locator('.field.control.l-input-sm input').first().press('ArrowUp');
+ await page.locator('.field.control.l-input-sm input').first().press('ArrowUp');
+ await page.locator('.field.control.l-input-sm input').first().press('ArrowUp');
+
+ const value = await page.locator('.field.control.l-input-sm input').first().inputValue();
+ await expect(value).toBe('6');
+
+ //Click text=OK
+ await Promise.all([
+ page.waitForNavigation(),
+ page.click('text=OK')
+ ]);
+
+ // Verify that the Sine Wave Generator is displayed and correct
+ // Verify object properties
+ await expect(page.locator('.l-browse-bar__object-name')).toContainText('New Sine Wave Generator');
+
+ // Verify canvas rendered and can be interacted with
+ await page.locator('canvas').nth(1).click({
+ position: {
+ x: 341,
+ y: 28
+ }
+ });
+
+ // Verify that where we click on canvas shows the number we clicked on
+ // Note that any number will do, we just care that a number exists
+ await expect(page.locator('.value-to-display-nearestValue')).toContainText(/[+-]?([0-9]*[.])?[0-9]+/);
+
+ });
+});
diff --git a/e2e/tests/framework.e2e.spec.js b/e2e/tests/framework.e2e.spec.js
new file mode 100644
index 000000000..b262ddc31
--- /dev/null
+++ b/e2e/tests/framework.e2e.spec.js
@@ -0,0 +1,55 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+/*
+This test suite is dedicated to testing our use of the playwright framework as it
+relates to how we've extended it (i.e. ./e2e/fixtures.js) and assumptions made in our dev environment
+(app.js and ./e2e/webpack-dev-middleware.js)
+*/
+
+const { test } = require('../fixtures.js');
+
+test.describe('fixtures.js tests', () => {
+ test('Verify that tests fail if console.error is thrown', async ({ page }) => {
+ test.fail();
+ //Go to baseURL
+ await page.goto('/', { waitUntil: 'networkidle' });
+
+ //Verify that ../fixtures.js detects console log errors
+ await Promise.all([
+ page.evaluate(() => console.error('This should result in a failure')),
+ page.waitForEvent('console') // always wait for the event to happen while triggering it!
+ ]);
+
+ });
+ test('Verify that tests pass if console.warn is thrown', async ({ page }) => {
+ //Go to baseURL
+ await page.goto('/', { waitUntil: 'networkidle' });
+
+ //Verify that ../fixtures.js detects console log errors
+ await Promise.all([
+ page.evaluate(() => console.warn('This should result in a pass')),
+ page.waitForEvent('console') // always wait for the event to happen while triggering it!
+ ]);
+
+ });
+});
diff --git a/e2e/tests/moveObjects.e2e.spec.js b/e2e/tests/moveObjects.e2e.spec.js
new file mode 100644
index 000000000..e7afc2f68
--- /dev/null
+++ b/e2e/tests/moveObjects.e2e.spec.js
@@ -0,0 +1,137 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+/*
+This test suite is dedicated to tests which verify the basic operations surrounding moving objects.
+*/
+
+const { test } = require('../fixtures.js');
+const { expect } = require('@playwright/test');
+
+test.describe('Move item tests', () => {
+ test('Create a basic object and verify that it can be moved to another folder', async ({ page }) => {
+ // Go to Open MCT
+ await page.goto('/');
+
+ // Create a new folder in the root my items folder
+ let folder1 = "Folder1";
+ await page.locator('button:has-text("Create")').click();
+ await page.locator('li.icon-folder').click();
+
+ await page.locator('text=Properties Title Notes >> input[type="text"]').click();
+ await page.locator('text=Properties Title Notes >> input[type="text"]').fill(folder1);
+
+ await Promise.all([
+ page.waitForNavigation(),
+ page.locator('text=OK').click(),
+ page.waitForSelector('.c-message-banner__message')
+ ]);
+ //Wait until Save Banner is gone
+ await page.locator('.c-message-banner__close-button').click();
+ await page.waitForSelector('.c-message-banner__message', { state: 'detached'});
+
+ // Create another folder with a new name at default location, which is currently inside Folder 1
+ let folder2 = "Folder2";
+ await page.locator('button:has-text("Create")').click();
+ await page.locator('li.icon-folder').click();
+ await page.locator('text=Properties Title Notes >> input[type="text"]').click();
+ await page.locator('text=Properties Title Notes >> input[type="text"]').fill(folder2);
+
+ await Promise.all([
+ page.waitForNavigation(),
+ page.locator('text=OK').click(),
+ page.waitForSelector('.c-message-banner__message')
+ ]);
+ //Wait until Save Banner is gone
+ await page.locator('.c-message-banner__close-button').click();
+ await page.waitForSelector('.c-message-banner__message', { state: 'detached'});
+
+ // Move Folder 2 from Folder 1 to My Items
+ await page.locator('text=Open MCT My Items >> span').nth(3).click();
+ await page.locator('.c-tree__scrollable div div:nth-child(2) .c-tree__item .c-tree__item__view-control').click();
+
+ await page.locator(`a:has-text("${folder2}")`).click({
+ button: 'right'
+ });
+ await page.locator('li.icon-move').click();
+ await page.locator('form[name="mctForm"] >> text=My Items').click();
+
+ await page.locator('text=OK').click();
+
+ // Expect that Folder 2 is in My Items, the root folder
+ expect(page.locator(`text=My Items >> nth=0:has(text=${folder2})`)).toBeTruthy();
+ });
+ test('Create a basic object and verify that it cannot be moved to telemetry object without Composition Provider', async ({ page }) => {
+ // Go to Open MCT
+ await page.goto('/');
+
+ // Create Telemetry Table
+ let telemetryTable = 'Test Telemetry Table';
+ await page.locator('button:has-text("Create")').click();
+ await page.locator('li:has-text("Telemetry Table")').click();
+ await page.locator('text=Properties Title Notes >> input[type="text"]').click();
+ await page.locator('text=Properties Title Notes >> input[type="text"]').fill(telemetryTable);
+
+ await page.locator('text=OK').click();
+
+ // Finish editing and save Telemetry Table
+ await page.locator('.c-button--menu.c-button--major.icon-save').click();
+ await page.locator('text=Save and Finish Editing').click();
+
+ // Create New Folder Basic Domain Object
+ let folder = 'Test Folder';
+ await page.locator('button:has-text("Create")').click();
+ await page.locator('li:has-text("Folder")').click();
+ await page.locator('text=Properties Title Notes >> input[type="text"]').click();
+ await page.locator('text=Properties Title Notes >> input[type="text"]').fill(folder);
+
+ // See if it's possible to put the folder in the Telemetry object during creation (Soft Assert)
+ await page.locator(`form[name="mctForm"] >> text=${telemetryTable}`).click();
+ let okButton = await page.locator('button.c-button.c-button--major:has-text("OK")');
+ let okButtonStateDisabled = await okButton.isDisabled();
+ expect.soft(okButtonStateDisabled).toBeTruthy();
+
+ // Continue test regardless of assertion and create it in My Items
+ await page.locator('form[name="mctForm"] >> text=My Items').click();
+ await page.locator('text=OK').click();
+
+ // Open My Items
+ await page.locator('text=Open MCT My Items >> span').nth(3).click();
+
+ // Select Folder Object and select Move from context menu
+ await Promise.all([
+ page.waitForNavigation(),
+ page.locator(`a:has-text("${folder}")`).click()
+ ]);
+ await page.locator('.c-tree__item.is-navigated-object .c-tree__item__label .c-tree__item__type-icon').click({
+ button: 'right'
+ });
+ await page.locator('li.icon-move').click();
+
+ // See if it's possible to put the folder in the Telemetry object after creation
+ await page.locator('text=Location Open MCT My Items >> span').nth(3).click();
+ await page.locator(`form[name="mctForm"] >> text=${telemetryTable}`).click();
+ let okButton2 = await page.locator('button.c-button.c-button--major:has-text("OK")');
+ let okButtonStateDisabled2 = await okButton2.isDisabled();
+ expect(okButtonStateDisabled2).toBeTruthy();
+ });
+});
diff --git a/e2e/tests/performance/imagery.perf.spec.js b/e2e/tests/performance/imagery.perf.spec.js
new file mode 100644
index 000000000..433bc1699
--- /dev/null
+++ b/e2e/tests/performance/imagery.perf.spec.js
@@ -0,0 +1,177 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+/*
+This test suite is dedicated to performance tests to ensure that testability of performance
+is not broken upstream on Open MCT. Any assumptions made downstream will be tested here
+
+TODO:
+ - Update resolution of performance config
+ - Add Performance Observer on init to push all performance marks
+ - Move client CDP connection to before or to a fixture
+ -
+
+*/
+
+const { test, expect } = require('@playwright/test');
+
+const filePath = 'e2e/test-data/PerformanceDisplayLayout.json';
+
+test.describe('Performance tests', () => {
+ test.beforeEach(async ({ page, browser }, testInfo) => {
+ // Go to baseURL
+ await page.goto('/', { waitUntil: 'networkidle' });
+
+ // Click a:has-text("My Items")
+ await page.locator('a:has-text("My Items")').click({
+ button: 'right'
+ });
+
+ // Click text=Import from JSON
+ await page.locator('text=Import from JSON').click();
+
+ // Upload Performance Display Layout.json
+ await page.setInputFiles('#fileElem', filePath);
+
+ // Click text=OK
+ await page.locator('text=OK').click();
+
+ await expect(page.locator('a:has-text("Performance Display Layout Display Layout")')).toBeVisible();
+
+ //Create a Chrome Performance Timeline trace to store as a test artifact
+ console.log("\n==== Devtools: startTracing ====\n");
+ await browser.startTracing(page, {
+ path: `${testInfo.outputPath()}-trace.json`,
+ screenshots: true
+ });
+ });
+ test.afterEach(async ({ page, browser}) => {
+ console.log("\n==== Devtools: stopTracing ====\n");
+ await browser.stopTracing();
+
+ /* Measurement Section
+ / The following section includes a block of performance measurements.
+ */
+ //Get time difference between viewlarge actionability and evaluate time
+ await page.evaluate(() => (window.performance.measure("machine-time-difference", "viewlarge.start", "viewLarge.start.test")));
+
+ //Get StartTime
+ const startTime = await page.evaluate(() => window.performance.timing.navigationStart);
+ console.log('window.performance.timing.navigationStart', startTime);
+
+ //Get All Performance Marks
+ const getAllMarksJson = await page.evaluate(() => JSON.stringify(window.performance.getEntriesByType("mark")));
+ const getAllMarks = JSON.parse(getAllMarksJson);
+ console.log('window.performance.getEntriesByType("mark")', getAllMarks);
+
+ //Get All Performance Measures
+ const getAllMeasuresJson = await page.evaluate(() => JSON.stringify(window.performance.getEntriesByType("measure")));
+ const getAllMeasures = JSON.parse(getAllMeasuresJson);
+ console.log('window.performance.getEntriesByType("measure")', getAllMeasures);
+
+ });
+ /* The following test will navigate to a previously created Performance Display Layout and measure the
+ / following metrics:
+ / - ElementResourceTiming
+ / - Interaction Timing
+ */
+ test('Embedded View Large for Imagery is performant in Fixed Time', async ({ page, browser }) => {
+ const client = await page.context().newCDPSession(page);
+ // Tell the DevTools session to record performance metrics
+ // https://chromedevtools.github.io/devtools-protocol/tot/Performance/#method-getMetrics
+ await client.send('Performance.enable');
+ // Go to baseURL
+ await page.goto('/');
+
+ // Search Available after Launch
+ await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
+ await page.evaluate(() => window.performance.mark("search-available"));
+ // Fill Search input
+ await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Performance Display Layout');
+ await page.evaluate(() => window.performance.mark("search-entered"));
+ //Search Result Appears and is clicked
+ await Promise.all([
+ page.waitForNavigation(),
+ page.locator('a:has-text("Performance Display Layout")').first().click(),
+ page.evaluate(() => window.performance.mark("click-search-result"))
+ ]);
+
+ //Time to Example Imagery Frame loads within Display Layout
+ await page.waitForSelector('.c-imagery__main-image__bg', { state: 'visible'});
+ //Time to Example Imagery object loads
+ await page.waitForSelector('.c-imagery__main-image__background-image', { state: 'visible'});
+
+ //Get background-image url from background-image css prop
+ const backgroundImage = await page.locator('.c-imagery__main-image__background-image');
+ let backgroundImageUrl = await backgroundImage.evaluate((el) => {
+ return window.getComputedStyle(el).getPropertyValue('background-image').match(/url\(([^)]+)\)/)[1];
+ });
+ backgroundImageUrl = backgroundImageUrl.slice(1, -1); //forgive me, padre
+ console.log('backgroundImageurl ' + backgroundImageUrl);
+
+ //Get ResourceTiming of background-image jpg
+ const resourceTimingJson = await page.evaluate((bgImageUrl) =>
+ JSON.stringify(window.performance.getEntriesByName(bgImageUrl).pop()),
+ backgroundImageUrl
+ );
+ console.log('resourceTimingJson ' + resourceTimingJson);
+
+ //Open Large view
+ await page.locator('button:has-text("Large View")').click(); //This action includes the performance.mark named 'viewLarge.start'
+ await page.evaluate(() => window.performance.mark("viewLarge.start.test")); //This is a mark only to compare evaluate timing
+
+ //Time to Imagery Rendered in Large Frame
+ await page.waitForSelector('.c-imagery__main-image__bg', { state: 'visible'});
+ await page.evaluate(() => window.performance.mark("background-image-frame"));
+
+ //Time to Example Imagery object loads
+ await page.waitForSelector('.c-imagery__main-image__background-image', { state: 'visible'});
+ await page.evaluate(() => window.performance.mark("background-image-visible"));
+
+ // Get Current number of images in thumbstrip
+ await page.waitForSelector('.c-imagery__thumb');
+ const thumbCount = await page.locator('.c-imagery__thumb').count();
+ console.log('number of thumbs rendered ' + thumbCount);
+ await page.locator('.c-imagery__thumb').last().click();
+
+ //Get ResourceTiming of all jpg resources
+ const resourceTimingJson2 = await page.evaluate(() =>
+ JSON.stringify(window.performance.getEntriesByType('resource'))
+ );
+ const resourceTiming = JSON.parse(resourceTimingJson2);
+ const jpgResourceTiming = resourceTiming.find((element) =>
+ element.name.includes('.jpg')
+ );
+ console.log('jpgResourceTiming ' + JSON.stringify(jpgResourceTiming));
+
+ // Click Close Icon
+ await page.locator('[aria-label="Close"]').click();
+ await page.evaluate(() => window.performance.mark("view-large-close-button"));
+
+ //await client.send('HeapProfiler.enable');
+ await client.send('HeapProfiler.collectGarbage');
+
+ let performanceMetrics = await client.send('Performance.getMetrics');
+ console.log(performanceMetrics.metrics);
+
+ });
+});
diff --git a/e2e/tests/performance/memleak-imagery.perf.spec.js b/e2e/tests/performance/memleak-imagery.perf.spec.js
new file mode 100644
index 000000000..6ce14f553
--- /dev/null
+++ b/e2e/tests/performance/memleak-imagery.perf.spec.js
@@ -0,0 +1,119 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+/*
+This test suite is an initial example for memory leak testing using performance. This configuration and execution must
+be kept separate from the traditional performance measurements to avoid any "observer" effects associated with tracing
+or profiling playwright and/or the browser.
+
+Based on a pattern identified in https://github.com/trentmwillis/devtools-protocol-demos/blob/master/testing-demos/memory-leak-by-heap.js
+and https://github.com/paulirish/automated-chrome-profiling/issues/3
+
+Best path forward: https://github.com/cowchimp/headless-devtools/blob/master/src/Memory/example.js
+
+*/
+
+const { test, expect } = require('@playwright/test');
+
+const filePath = 'e2e/test-data/PerformanceDisplayLayout.json';
+
+// eslint-disable-next-line playwright/no-skipped-test
+test.describe.skip('Memory Performance tests', () => {
+ test.beforeEach(async ({ page, browser }, testInfo) => {
+ // Go to baseURL
+ await page.goto('/', { waitUntil: 'networkidle' });
+
+ // Click a:has-text("My Items")
+ await page.locator('a:has-text("My Items")').click({
+ button: 'right'
+ });
+
+ // Click text=Import from JSON
+ await page.locator('text=Import from JSON').click();
+
+ // Upload Performance Display Layout.json
+ await page.setInputFiles('#fileElem', filePath);
+
+ // Click text=OK
+ await page.locator('text=OK').click();
+
+ await expect(page.locator('a:has-text("Performance Display Layout Display Layout")')).toBeVisible();
+ });
+
+ test('Embedded View Large for Imagery is performant in Fixed Time', async ({ page, browser }) => {
+
+ await page.goto('/', {waitUntil: 'networkidle'});
+
+ // To to Search Available after Launch
+ await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
+ // Fill Search input
+ await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Performance Display Layout');
+ //Search Result Appears and is clicked
+ await Promise.all([
+ page.waitForNavigation(),
+ page.locator('a:has-text("Performance Display Layout")').first().click()
+ ]);
+
+ //Time to Example Imagery Frame loads within Display Layout
+ await page.waitForSelector('.c-imagery__main-image__bg', { state: 'visible'});
+ //Time to Example Imagery object loads
+ await page.waitForSelector('.c-imagery__main-image__background-image', { state: 'visible'});
+
+ const client = await page.context().newCDPSession(page);
+ await client.send('HeapProfiler.enable');
+ await client.send('HeapProfiler.startSampling');
+ // await client.send('HeapProfiler.collectGarbage');
+ await client.send('Performance.enable');
+
+ let performanceMetricsBefore = await client.send('Performance.getMetrics');
+ console.log(performanceMetricsBefore.metrics);
+
+ //await client.send('Performance.disable');
+
+ //Open Large view
+ await page.locator('button:has-text("Large View")').click();
+ await client.send('HeapProfiler.takeHeapSnapshot');
+
+ //Time to Imagery Rendered in Large Frame
+ await page.waitForSelector('.c-imagery__main-image__bg', { state: 'visible'});
+
+ //Time to Example Imagery object loads
+ await page.waitForSelector('.c-imagery__main-image__background-image', { state: 'visible'});
+
+ // Click Close Icon
+ await page.locator('.c-click-icon').click();
+
+ //Time to Example Imagery Frame loads within Display Layout
+ await page.waitForSelector('.c-imagery__main-image__bg', { state: 'visible'});
+ //Time to Example Imagery object loads
+ await page.waitForSelector('.c-imagery__main-image__background-image', { state: 'visible'});
+
+ await client.send('HeapProfiler.collectGarbage');
+ //await client.send('Performance.enable');
+
+ let performanceMetricsAfter = await client.send('Performance.getMetrics');
+ console.log(performanceMetricsAfter.metrics);
+
+ //await client.send('Performance.disable');
+
+ });
+});
diff --git a/e2e/tests/performance/notebook.perf.spec.js b/e2e/tests/performance/notebook.perf.spec.js
new file mode 100644
index 000000000..1c10ad6ba
--- /dev/null
+++ b/e2e/tests/performance/notebook.perf.spec.js
@@ -0,0 +1,158 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+/*
+This test suite is dedicated to performance tests to ensure that testability of performance
+is not broken upstream on Open MCT. Any assumptions made downstream will be tested here.
+
+TODO:
+ - Update resolution of performance config
+ - Add Performance Observer on init to push all performance marks
+ - Move client CDP connection to before or to a fixture
+
+*/
+
+const { test, expect } = require('@playwright/test');
+
+const notebookFilePath = 'e2e/test-data/PerformanceNotebook.json';
+
+test.describe('Performance tests', () => {
+ test.beforeEach(async ({ page, browser }, testInfo) => {
+ // Go to baseURL
+ await page.goto('/', { waitUntil: 'networkidle' });
+
+ // Click a:has-text("My Items")
+ await page.locator('a:has-text("My Items")').click({
+ button: 'right'
+ });
+
+ // Click text=Import from JSON
+ await page.locator('text=Import from JSON').click();
+
+ // Upload Performance Display Layout.json
+ await page.setInputFiles('#fileElem', notebookFilePath);
+
+ // TODO Fix this
+ await page.locator('text=OK >> nth=1').click();
+
+ await expect(page.locator('a:has-text("Performance Notebook")')).toBeVisible();
+
+ //Create a Chrome Performance Timeline trace to store as a test artifact
+ console.log("\n==== Devtools: startTracing ====\n");
+ await browser.startTracing(page, {
+ path: `${testInfo.outputPath()}-trace.json`,
+ screenshots: true
+ });
+ });
+ test.afterEach(async ({ page, browser}) => {
+ console.log("\n==== Devtools: stopTracing ====\n");
+ await browser.stopTracing();
+
+ /* Measurement Section
+ / The following section includes a block of performance measurements.
+ */
+ const startTime = await page.evaluate(() => window.performance.timing.navigationStart);
+ console.log('window.performance.timing.navigationStart', startTime);
+
+ //Get All Performance Marks
+ const getAllMarksJson = await page.evaluate(() => JSON.stringify(window.performance.getEntriesByType("mark")));
+ const getAllMarks = JSON.parse(getAllMarksJson);
+ console.log('window.performance.getEntriesByType("mark")', getAllMarks);
+
+ //Get All Performance Measures
+ const getAllMeasuresJson = await page.evaluate(() => JSON.stringify(window.performance.getEntriesByType("measure")));
+ const getAllMeasures = JSON.parse(getAllMeasuresJson);
+ console.log('window.performance.getEntriesByType("measure")', getAllMeasures);
+
+ });
+ /* The following test will navigate to a previously created Performance Display Layout and measure the
+ / following metrics:
+ / - ElementResourceTiming
+ / - Interaction Timing
+ */
+ test('Notebook Search, Add Entry, Update Entry are performant', async ({ page, browser }) => {
+ const client = await page.context().newCDPSession(page);
+ // Tell the DevTools session to record performance metrics
+ // https://chromedevtools.github.io/devtools-protocol/tot/Performance/#method-getMetrics
+ await client.send('Performance.enable');
+ // Go to baseURL
+ await page.goto('/');
+
+ // To to Search Available after Launch
+ await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
+ await page.evaluate(() => window.performance.mark("search-available"));
+ // Fill Search input
+ await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Performance Notebook');
+ await page.evaluate(() => window.performance.mark("search-entered"));
+ //Search Result Appears and is clicked
+ await Promise.all([
+ page.waitForNavigation(),
+ page.locator('a:has-text("Performance Notebook")').first().click(),
+ page.evaluate(() => window.performance.mark("click-search-result"))
+ ]);
+
+ await page.waitForSelector('.c-tree__item c-tree-and-search__loading loading', {state: 'hidden'});
+ await page.evaluate(() => window.performance.mark("search-spinner-gone"));
+
+ await page.waitForSelector('.l-browse-bar__object-name', { state: 'visible'});
+ await page.evaluate(() => window.performance.mark("object-title-appears"));
+
+ await page.waitForSelector('.c-notebook__entry >> nth=0', { state: 'visible'});
+ await page.evaluate(() => window.performance.mark("notebook-entry-appears"));
+
+ // Click Add new Notebook Entry
+ await page.locator('.c-notebook__drag-area').click();
+ await page.evaluate(() => window.performance.mark("new-notebook-entry-created"));
+
+ // Enter Notebook Entry text
+ await page.locator('div.c-ne__text').last().fill('New Entry');
+ await page.keyboard.press('Enter');
+ await page.evaluate(() => window.performance.mark("new-notebook-entry-filled"));
+
+ //Individual Notebook Entry Search
+ await page.evaluate(() => window.performance.mark("notebook-search-start"));
+ await page.locator('.c-notebook__search >> input').fill('Existing Entry');
+ await page.evaluate(() => window.performance.mark("notebook-search-filled"));
+ await page.waitForSelector('text=Search Results (3)', { state: 'visible'});
+ await page.evaluate(() => window.performance.mark("notebook-search-processed"));
+ await page.waitForSelector('.c-notebook__entry >> nth=2', { state: 'visible'});
+ await page.evaluate(() => window.performance.mark("notebook-search-processed"));
+
+ //Clear Search
+ await page.locator('.c-search.c-notebook__search .c-search__clear-input').click();
+ await page.evaluate(() => window.performance.mark("notebook-search-processed"));
+
+ // Hover on Last
+ await page.evaluate(() => window.performance.mark("new-notebook-entry-delete"));
+ await page.locator('div.c-ne__time-and-content').last().hover();
+ await page.locator('button[title="Delete this entry"]').last().click();
+ await page.locator('button:has-text("Ok")').click();
+ await page.waitForSelector('.c-notebook__entry >> nth=3', { state: 'detached'});
+ await page.evaluate(() => window.performance.mark("new-notebook-entry-deleted"));
+
+ //await client.send('HeapProfiler.enable');
+ await client.send('HeapProfiler.collectGarbage');
+
+ let performanceMetrics = await client.send('Performance.getMetrics');
+ console.log(performanceMetrics.metrics);
+ });
+});
diff --git a/e2e/tests/persistence/addNoneditableObject.js b/e2e/tests/persistence/addNoneditableObject.js
new file mode 100644
index 000000000..55da25358
--- /dev/null
+++ b/e2e/tests/persistence/addNoneditableObject.js
@@ -0,0 +1,27 @@
+(function () {
+ document.addEventListener('DOMContentLoaded', () => {
+ const PERSISTENCE_KEY = 'persistence-tests';
+ const openmct = window.openmct;
+
+ openmct.objects.addRoot({
+ namespace: PERSISTENCE_KEY,
+ key: PERSISTENCE_KEY
+ });
+
+ openmct.objects.addProvider(PERSISTENCE_KEY, {
+ get(identifier) {
+ if (identifier.key !== PERSISTENCE_KEY) {
+ return undefined;
+ } else {
+ return Promise.resolve({
+ identifier,
+ type: 'folder',
+ name: 'Persistence Testing',
+ location: 'ROOT',
+ composition: []
+ });
+ }
+ }
+ });
+ });
+}());
diff --git a/e2e/tests/persistence/persistability.e2e.spec.js b/e2e/tests/persistence/persistability.e2e.spec.js
new file mode 100644
index 000000000..aa7c0b9d7
--- /dev/null
+++ b/e2e/tests/persistence/persistability.e2e.spec.js
@@ -0,0 +1,80 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+/*
+This test suite is dedicated to tests which verify the basic operations surrounding conditionSets.
+*/
+
+const { test } = require('../../fixtures.js');
+const { expect } = require('@playwright/test');
+const path = require('path');
+
+test.describe('Persistence operations @addInit', () => {
+ // add non persistable root item
+ test.beforeEach(async ({ page }) => {
+ // eslint-disable-next-line no-undef
+ await page.addInitScript({ path: path.join(__dirname, 'addNoneditableObject.js') });
+ });
+
+ test('Persistability should be respected in the create form location field', async ({ page }) => {
+ test.info().annotations.push({
+ type: 'issue',
+ description: 'https://github.com/nasa/openmct/issues/4323'
+ });
+ // Go to baseURL
+ await page.goto('/', { waitUntil: 'networkidle' });
+
+ // Click the Create button
+ await page.click('button:has-text("Create")');
+
+ // Click text=Condition Set
+ await page.click('text=Condition Set');
+
+ // Click form[name="mctForm"] >> text=Persistence Testing
+ await page.locator('form[name="mctForm"] >> text=Persistence Testing').click();
+
+ // Check that "OK" button is disabled
+ const okButton = page.locator('button:has-text("OK")');
+ await expect(okButton).toBeDisabled();
+ });
+ test('Non-persistable objects should not show persistence related actions', async ({ page }) => {
+ // Go to baseURL
+ await page.goto('/', { waitUntil: 'networkidle' });
+
+ // Click text=Persistence Testing >> nth=0
+ await page.locator('text=Persistence Testing').first().click({
+ button: 'right'
+ });
+
+ const menuOptions = page.locator('.c-menu ul');
+
+ await expect.soft(menuOptions).toContainText(['Open In New Tab', 'View', 'Create Link']);
+ await expect(menuOptions).not.toContainText(['Move', 'Duplicate', 'Remove', 'Add New Folder', 'Edit Properties...', 'Export as JSON', 'Import from JSON']);
+ });
+ test.fixme('Cannot move a previously created domain object to non-peristable boject in Move Modal', async ({ page }) => {
+ //Create a domain object
+ //Save Domain object
+ //Move Object and verify that cannot select non-persistable object
+ //Move Object to My Items
+ //Verify successful move
+ });
+});
diff --git a/e2e/tests/plugins/ExportAsJSON/exportAsJson.e2e.spec.js b/e2e/tests/plugins/ExportAsJSON/exportAsJson.e2e.spec.js
new file mode 100644
index 000000000..6da6c0287
--- /dev/null
+++ b/e2e/tests/plugins/ExportAsJSON/exportAsJson.e2e.spec.js
@@ -0,0 +1,51 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+/*
+This test suite is dedicated to tests which verify the basic operations surrounding exportAsJSON.
+*/
+
+const { test } = require('../../../fixtures.js');
+// FIXME: Remove this eslint exception once tests are implemented
+// eslint-disable-next-line no-unused-vars
+const { expect } = require('@playwright/test');
+
+test.describe('ExportAsJSON', () => {
+ test.fixme('Create a basic object and verify that it can be exported as JSON from Tree', async ({ page }) => {
+ //Create domain object
+ //Save Domain Object
+ //Verify that the newly created domain object can be exported as JSON from the Tree
+ });
+ test.fixme('Create a basic object and verify that it can be exported as JSON from 3 dot menu', async ({ page }) => {
+ //Create domain object
+ //Save Domain Object
+ //Verify that the newly created domain object can be exported as JSON from the 3 dot menu
+ });
+ test.fixme('Verify that a nested Object can be exported as JSON', async ({ page }) => {
+ // Create 2 objects with hierarchy
+ // Export as JSON
+ // Verify Hiearchy
+ });
+ test.fixme('Verify that the ExportAsJSON dropdown does not appear for the item X', async ({ page }) => {
+ // Other than non-persistible objects
+ });
+});
diff --git a/e2e/tests/plugins/ImportAsJSON/importAsJson.e2e.spec.js b/e2e/tests/plugins/ImportAsJSON/importAsJson.e2e.spec.js
new file mode 100644
index 000000000..6469e8f15
--- /dev/null
+++ b/e2e/tests/plugins/ImportAsJSON/importAsJson.e2e.spec.js
@@ -0,0 +1,49 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+/*
+This test suite is dedicated to tests which verify the basic operations surrounding importAsJSON.
+*/
+
+const { test } = require('../../../fixtures.js');
+// FIXME: Remove this eslint exception once tests are implemented
+// eslint-disable-next-line no-unused-vars
+const { expect } = require('@playwright/test');
+
+test.describe('ExportAsJSON', () => {
+ test.fixme('Verify that domain object can be importAsJSON from Tree', async ({ page }) => {
+ //Verify that an testdata JSON file can be imported from Tree
+ //Verify correctness of imported domain object
+ });
+ test.fixme('Verify that domain object can be importAsJSON from 3 dot menu on folder', async ({ page }) => {
+ //Verify that an testdata JSON file can be imported from 3 dot menu on folder domain object
+ //Verify correctness of imported domain object
+ });
+ test.fixme('Verify that a nested Objects can be importAsJSON', async ({ page }) => {
+ // Testdata with hierarchy
+ // ImportAsJSON on Tree
+ // Verify Hierarchy
+ });
+ test.fixme('Verify that the ImportAsJSON dropdown does not appear for the item X', async ({ page }) => {
+ // Other than non-persistible objects
+ });
+});
diff --git a/e2e/tests/plugins/clock/Clock.e2e.spec.js b/e2e/tests/plugins/clock/Clock.e2e.spec.js
new file mode 100644
index 000000000..645770a55
--- /dev/null
+++ b/e2e/tests/plugins/clock/Clock.e2e.spec.js
@@ -0,0 +1,67 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+/*
+This test suite is dedicated to tests which verify the basic operations surrounding Clock.
+*/
+
+const { test } = require('../../../fixtures.js');
+const { expect } = require('@playwright/test');
+
+test.describe('Clock Generator', () => {
+
+ test('Timezone dropdown will collapse when clicked outside or on dropdown icon again', async ({ page }) => {
+ test.info().annotations.push({
+ type: 'issue',
+ description: 'https://github.com/nasa/openmct/issues/4878'
+ });
+ //Go to baseURL
+ await page.goto('/', { waitUntil: 'networkidle' });
+
+ //Click the Create button
+ await page.click('button:has-text("Create")');
+
+ // Click Clock
+ await page.click('text=Clock');
+
+ // Click .icon-arrow-down
+ await page.locator('.icon-arrow-down').click();
+ //verify if the autocomplete dropdown is visible
+ await expect(page.locator(".c-input--autocomplete__options")).toBeVisible();
+ // Click .icon-arrow-down
+ await page.locator('.icon-arrow-down').click();
+
+ // Verify clicking on the autocomplete arrow collapses the dropdown
+ await expect(page.locator(".c-input--autocomplete__options")).not.toBeVisible();
+
+ // Click timezone input to open dropdown
+ await page.locator('.c-input--autocomplete__input').click();
+ //verify if the autocomplete dropdown is visible
+ await expect(page.locator(".c-input--autocomplete__options")).toBeVisible();
+
+ // Verify clicking outside the autocomplete dropdown collapses it
+ await page.locator('text=Timezone').click();
+ // Verify clicking on the autocomplete arrow collapses the dropdown
+ await expect(page.locator(".c-input--autocomplete__options")).not.toBeVisible();
+
+ });
+});
diff --git a/e2e/tests/plugins/condition/condition.e2e.spec.js b/e2e/tests/plugins/condition/condition.e2e.spec.js
index d3ecb4be2..9662b3692 100644
--- a/e2e/tests/plugins/condition/condition.e2e.spec.js
+++ b/e2e/tests/plugins/condition/condition.e2e.spec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -21,13 +21,21 @@
*****************************************************************************/
/*
-This test suite is dedicated to tests which verify the basic operations surrounding conditionSets.
+This test suite is dedicated to tests which verify the basic operations surrounding conditionSets. Note: this
+suite is sharing state between tests which is considered an anti-pattern. Implimenting in this way to
+demonstrate some playwright for test developers. This pattern should not be re-used in other CRUD suites.
*/
-const { test, expect } = require('@playwright/test');
+const { test } = require('../../../fixtures.js');
+const { expect } = require('@playwright/test');
-test.describe('condition set', () => {
- test('create new button `condition set` creates new condition object', async ({ page }) => {
+let conditionSetUrl;
+let getConditionSetIdentifierFromUrl;
+
+test.describe.serial('Condition Set CRUD Operations on @localStorage', () => {
+ test.beforeAll(async ({ browser}) => {
+ const context = await browser.newContext();
+ const page = await context.newPage();
//Go to baseURL
await page.goto('/', { waitUntil: 'networkidle' });
@@ -35,14 +43,139 @@ test.describe('condition set', () => {
await page.click('button:has-text("Create")');
// Click text=Condition Set
- await page.click('text=Condition Set');
+ await page.locator('li:has-text("Condition Set")').click();
// Click text=OK
await Promise.all([
- page.waitForNavigation(/*{ url: 'http://localhost:8080/#/browse/mine/dab945d4-5a84-480e-8180-222b4aa730fa?tc.mode=fixed&tc.startBound=1639696164435&tc.endBound=1639697964435&tc.timeSystem=utc&view=conditionSet.view' }*/),
+ page.waitForNavigation(),
page.click('text=OK')
]);
+ //Save localStorage for future test execution
+ await context.storageState({ path: './e2e/test-data/recycled_local_storage.json' });
+
+ //Set object identifier from url
+ conditionSetUrl = page.url();
+ console.log('conditionSetUrl ' + conditionSetUrl);
+
+ getConditionSetIdentifierFromUrl = conditionSetUrl.split('/').pop().split('?')[0];
+ console.debug('getConditionSetIdentifierFromUrl ' + getConditionSetIdentifierFromUrl);
+ });
+
+ //Load localStorage for subsequent tests
+ test.use({ storageState: './e2e/test-data/recycled_local_storage.json' });
+ //Begin suite of tests again localStorage
+ test('Condition set object properties persist in main view and inspector @localStorage', async ({ page }) => {
+ //Navigate to baseURL with injected localStorage
+ await page.goto(conditionSetUrl, { waitUntil: 'networkidle' });
+
+ //Assertions on loaded Condition Set in main view. This is a stateful transition step after page.goto()
+ await expect.soft(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Condition Set');
+
+ //Assertions on loaded Condition Set in Inspector
+ expect.soft(page.locator('_vue=item.name=Unnamed Condition Set')).toBeTruthy();
+
+ //Reload Page
+ await Promise.all([
+ page.reload(),
+ page.waitForLoadState('networkidle')
+ ]);
+
+ //Re-verify after reload
+ await expect.soft(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Condition Set');
+ //Assertions on loaded Condition Set in Inspector
+ expect.soft(page.locator('_vue=item.name=Unnamed Condition Set')).toBeTruthy();
+
+ });
+ test('condition set object can be modified on @localStorage', async ({ page }) => {
+ await page.goto(conditionSetUrl, { waitUntil: 'networkidle' });
+
+ //Assertions on loaded Condition Set in main view. This is a stateful transition step after page.goto()
+ await expect.soft(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Condition Set');
+
+ //Update the Condition Set properties
+ // Click Edit Button
+ await page.locator('text=Conditions View Snapshot >> button').nth(3).click();
+
+ //Edit Condition Set Name from main view
+ await page.locator('text=Unnamed Condition Set').first().fill('Renamed Condition Set');
+ await page.locator('text=Renamed Condition Set').first().press('Enter');
+ // Click Save Button
+ await page.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button').nth(1).click();
+ // Click Save and Finish Editing Option
+ await page.locator('text=Save and Finish Editing').click();
+
+ //Verify Main section reflects updated Name Property
+ await expect.soft(page.locator('.l-browse-bar__object-name')).toContainText('Renamed Condition Set');
+
+ // Verify Inspector properties
+ // Verify Inspector has updated Name property
+ expect.soft(page.locator('text=Renamed Condition Set').nth(1)).toBeTruthy();
+ // Verify Inspector Details has updated Name property
+ expect.soft(page.locator('text=Renamed Condition Set').nth(2)).toBeTruthy();
+
+ // Verify Tree reflects updated Name proprety
+ // Expand Tree
+ await page.locator('text=Open MCT My Items >> span >> nth=3').click();
+ // Verify Condition Set Object is renamed in Tree
+ expect(page.locator('a:has-text("Renamed Condition Set")')).toBeTruthy();
+ // Verify Search Tree reflects renamed Name property
+ await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Renamed');
+ expect(page.locator('a:has-text("Renamed Condition Set")')).toBeTruthy();
+
+ //Reload Page
+ await Promise.all([
+ page.reload(),
+ page.waitForLoadState('networkidle')
+ ]);
+
+ //Verify Main section reflects updated Name Property
+ await expect.soft(page.locator('.l-browse-bar__object-name')).toContainText('Renamed Condition Set');
+
+ // Verify Inspector properties
+ // Verify Inspector has updated Name property
+ expect.soft(page.locator('text=Renamed Condition Set').nth(1)).toBeTruthy();
+ // Verify Inspector Details has updated Name property
+ expect.soft(page.locator('text=Renamed Condition Set').nth(2)).toBeTruthy();
+
+ // Verify Tree reflects updated Name proprety
+ // Expand Tree
+ await page.locator('text=Open MCT My Items >> span >> nth=3').click();
+ // Verify Condition Set Object is renamed in Tree
+ expect(page.locator('a:has-text("Renamed Condition Set")')).toBeTruthy();
+ // Verify Search Tree reflects renamed Name property
+ await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Renamed');
+ expect(page.locator('a:has-text("Renamed Condition Set")')).toBeTruthy();
+ });
+ test('condition set object can be deleted by Search Tree Actions menu on @localStorage', async ({ page }) => {
+ //Navigate to baseURL
+ await page.goto('/', { waitUntil: 'networkidle' });
+
+ //Assertions on loaded Condition Set in main view. This is a stateful transition step after page.goto()
+ await expect(page.locator('a:has-text("Unnamed Condition Set Condition Set") >> nth=0')).toBeVisible();
+
+ const numberOfConditionSetsToStart = await page.locator('a:has-text("Unnamed Condition Set Condition Set")').count();
+
+ // Search for Unnamed Condition Set
+ await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Unnamed Condition Set');
+ // Click Search Result
+ await page.locator('[aria-label="OpenMCT Search"] >> text=Unnamed Condition Set').first().click();
+ // Click hamburger button
+ await page.locator('[title="More options"]').click();
+
+ // Click text=Remove
+ await page.locator('text=Remove').click();
+ await page.locator('text=OK').click();
+
+ //Expect Unnamed Condition Set to be removed in Main View
+ const numberOfConditionSetsAtEnd = await page.locator('a:has-text("Unnamed Condition Set Condition Set")').count();
+
+ expect(numberOfConditionSetsAtEnd).toEqual(numberOfConditionSetsToStart - 1);
+
+ //Feature?
+ //Domain Object is still available by direct URL after delete
+ await page.goto(conditionSetUrl, { waitUntil: 'networkidle' });
await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Condition Set');
+
});
});
diff --git a/e2e/tests/plugins/imagery/exampleImagery.e2e.spec.js b/e2e/tests/plugins/imagery/exampleImagery.e2e.spec.js
new file mode 100644
index 000000000..d4f767be8
--- /dev/null
+++ b/e2e/tests/plugins/imagery/exampleImagery.e2e.spec.js
@@ -0,0 +1,753 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+/*
+This test suite is dedicated to tests which verify the basic operations surrounding imagery,
+but only assume that example imagery is present.
+*/
+/* globals process */
+
+const { test } = require('../../../fixtures.js');
+const { expect } = require('@playwright/test');
+
+const backgroundImageSelector = '.c-imagery__main-image__background-image';
+
+//The following block of tests verifies the basic functionality of example imagery and serves as a template for Imagery objects embedded in other objects.
+test.describe('Example Imagery Object', () => {
+
+ test.beforeEach(async ({ page }) => {
+ //Go to baseURL
+ await page.goto('/', { waitUntil: 'networkidle' });
+
+ //Click the Create button
+ await page.click('button:has-text("Create")');
+
+ // Click text=Example Imagery
+ await page.click('text=Example Imagery');
+
+ // Click text=OK
+ await Promise.all([
+ page.waitForNavigation({waitUntil: 'networkidle'}),
+ page.click('text=OK'),
+ //Wait for Save Banner to appear
+ page.waitForSelector('.c-message-banner__message')
+ ]);
+ // Close Banner
+ await page.locator('.c-message-banner__close-button').click();
+
+ //Wait until Save Banner is gone
+ await page.waitForSelector('.c-message-banner__message', { state: 'detached'});
+ await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Example Imagery');
+ await page.locator(backgroundImageSelector).hover({trial: true});
+ });
+
+ test('Can use Mouse Wheel to zoom in and out of latest image', async ({ page }) => {
+ const deltaYStep = 100; //equivalent to 1x zoom
+ await page.locator(backgroundImageSelector).hover({trial: true});
+ const originalImageDimensions = await page.locator(backgroundImageSelector).boundingBox();
+ // zoom in
+ await page.locator(backgroundImageSelector).hover({trial: true});
+ await page.mouse.wheel(0, deltaYStep * 2);
+ // wait for zoom animation to finish
+ await page.locator(backgroundImageSelector).hover({trial: true});
+ const imageMouseZoomedIn = await page.locator(backgroundImageSelector).boundingBox();
+ // zoom out
+ await page.locator(backgroundImageSelector).hover({trial: true});
+ await page.mouse.wheel(0, -deltaYStep);
+ // wait for zoom animation to finish
+ await page.locator(backgroundImageSelector).hover({trial: true});
+ const imageMouseZoomedOut = await page.locator(backgroundImageSelector).boundingBox();
+
+ expect(imageMouseZoomedIn.height).toBeGreaterThan(originalImageDimensions.height);
+ expect(imageMouseZoomedIn.width).toBeGreaterThan(originalImageDimensions.width);
+ expect(imageMouseZoomedOut.height).toBeLessThan(imageMouseZoomedIn.height);
+ expect(imageMouseZoomedOut.width).toBeLessThan(imageMouseZoomedIn.width);
+
+ });
+
+ test('Can use alt+drag to move around image once zoomed in', async ({ page }) => {
+ const deltaYStep = 100; //equivalent to 1x zoom
+ const panHotkey = process.platform === 'linux' ? ['Control', 'Alt'] : ['Alt'];
+
+ await page.locator(backgroundImageSelector).hover({trial: true});
+
+ // zoom in
+ await page.mouse.wheel(0, deltaYStep * 2);
+ await page.locator(backgroundImageSelector).hover({trial: true});
+ const zoomedBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
+ const imageCenterX = zoomedBoundingBox.x + zoomedBoundingBox.width / 2;
+ const imageCenterY = zoomedBoundingBox.y + zoomedBoundingBox.height / 2;
+ // move to the right
+
+ // center the mouse pointer
+ await page.mouse.move(imageCenterX, imageCenterY);
+
+ //Get Diagnostic info about process environment
+ console.log('process.platform is ' + process.platform);
+ const getUA = await page.evaluate(() => navigator.userAgent);
+ console.log('navigator.userAgent ' + getUA);
+ // Pan Imagery Hints
+ const expectedAltText = process.platform === 'linux' ? 'Ctrl+Alt drag to pan' : 'Alt drag to pan';
+ const imageryHintsText = await page.locator('.c-imagery__hints').innerText();
+ expect(expectedAltText).toEqual(imageryHintsText);
+
+ // pan right
+ await Promise.all(panHotkey.map(x => page.keyboard.down(x)));
+ await page.mouse.down();
+ await page.mouse.move(imageCenterX - 200, imageCenterY, 10);
+ await page.mouse.up();
+ await Promise.all(panHotkey.map(x => page.keyboard.up(x)));
+ const afterRightPanBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
+ expect(zoomedBoundingBox.x).toBeGreaterThan(afterRightPanBoundingBox.x);
+
+ // pan left
+ await Promise.all(panHotkey.map(x => page.keyboard.down(x)));
+ await page.mouse.down();
+ await page.mouse.move(imageCenterX, imageCenterY, 10);
+ await page.mouse.up();
+ await Promise.all(panHotkey.map(x => page.keyboard.up(x)));
+ const afterLeftPanBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
+ expect(afterRightPanBoundingBox.x).toBeLessThan(afterLeftPanBoundingBox.x);
+
+ // pan up
+ await page.mouse.move(imageCenterX, imageCenterY);
+ await Promise.all(panHotkey.map(x => page.keyboard.down(x)));
+ await page.mouse.down();
+ await page.mouse.move(imageCenterX, imageCenterY + 200, 10);
+ await page.mouse.up();
+ await Promise.all(panHotkey.map(x => page.keyboard.up(x)));
+ const afterUpPanBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
+ expect(afterUpPanBoundingBox.y).toBeGreaterThan(afterLeftPanBoundingBox.y);
+
+ // pan down
+ await Promise.all(panHotkey.map(x => page.keyboard.down(x)));
+ await page.mouse.down();
+ await page.mouse.move(imageCenterX, imageCenterY - 200, 10);
+ await page.mouse.up();
+ await Promise.all(panHotkey.map(x => page.keyboard.up(x)));
+ const afterDownPanBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
+ expect(afterDownPanBoundingBox.y).toBeLessThan(afterUpPanBoundingBox.y);
+
+ });
+
+ test('Can use + - buttons to zoom on the image', async ({ page }) => {
+ await page.locator(backgroundImageSelector).hover({trial: true});
+ const zoomInBtn = page.locator('.t-btn-zoom-in').nth(0);
+ const zoomOutBtn = page.locator('.t-btn-zoom-out').nth(0);
+ const initialBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
+
+ await zoomInBtn.click();
+ await zoomInBtn.click();
+ // wait for zoom animation to finish
+ await page.locator(backgroundImageSelector).hover({trial: true});
+ const zoomedInBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
+ expect(zoomedInBoundingBox.height).toBeGreaterThan(initialBoundingBox.height);
+ expect(zoomedInBoundingBox.width).toBeGreaterThan(initialBoundingBox.width);
+
+ await zoomOutBtn.click();
+ // wait for zoom animation to finish
+ await page.locator(backgroundImageSelector).hover({trial: true});
+ const zoomedOutBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
+ expect(zoomedOutBoundingBox.height).toBeLessThan(zoomedInBoundingBox.height);
+ expect(zoomedOutBoundingBox.width).toBeLessThan(zoomedInBoundingBox.width);
+
+ });
+
+ test('Can use the reset button to reset the image', async ({ page }, testInfo) => {
+ test.slow(testInfo.project === 'chrome-beta', "This test is slow in chrome-beta");
+ // wait for zoom animation to finish
+ await page.locator(backgroundImageSelector).hover({trial: true});
+
+ const zoomInBtn = page.locator('.t-btn-zoom-in').nth(0);
+ const zoomResetBtn = page.locator('.t-btn-zoom-reset').nth(0);
+ const initialBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
+
+ await zoomInBtn.click();
+ // wait for zoom animation to finish
+ await page.locator(backgroundImageSelector).hover({trial: true});
+ await zoomInBtn.click();
+ // wait for zoom animation to finish
+ await page.locator(backgroundImageSelector).hover({trial: true});
+
+ const zoomedInBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
+ expect.soft(zoomedInBoundingBox.height).toBeGreaterThan(initialBoundingBox.height);
+ expect.soft(zoomedInBoundingBox.width).toBeGreaterThan(initialBoundingBox.width);
+
+ // wait for zoom animation to finish
+ // FIXME: The zoom is flakey, sometimes not returning to original dimensions
+ // https://github.com/nasa/openmct/issues/5491
+ await expect.poll(async () => {
+ await zoomResetBtn.click();
+ const boundingBox = await page.locator(backgroundImageSelector).boundingBox();
+
+ return boundingBox;
+ }, {
+ timeout: 10 * 1000
+ }).toEqual(initialBoundingBox);
+ });
+
+ test('Using the zoom features does not pause telemetry', async ({ page }) => {
+ const pausePlayButton = page.locator('.c-button.pause-play');
+ // wait for zoom animation to finish
+ await page.locator(backgroundImageSelector).hover({trial: true});
+
+ // open the time conductor drop down
+ await page.locator('button:has-text("Fixed Timespan")').click();
+ // Click local clock
+ await page.locator('[data-testid="conductor-modeOption-realtime"]').click();
+
+ await expect.soft(pausePlayButton).not.toHaveClass(/is-paused/);
+ const zoomInBtn = page.locator('.t-btn-zoom-in').nth(0);
+ await zoomInBtn.click();
+ // wait for zoom animation to finish
+ await page.locator(backgroundImageSelector).hover({trial: true});
+
+ return expect(pausePlayButton).not.toHaveClass(/is-paused/);
+ });
+
+});
+
+// The following test case will cover these scenarios
+// ('Can use Mouse Wheel to zoom in and out of previous image');
+// ('Can use alt+drag to move around image once zoomed in');
+// ('Clicking on the left arrow should pause the imagery and go to previous image');
+// ('If the imagery view is in pause mode, it should not be updated when new images come in');
+// ('If the imagery view is not in pause mode, it should be updated when new images come in');
+test('Example Imagery in Display layout', async ({ page, browserName }) => {
+ test.fixme(browserName === 'firefox', 'This test needs to be updated to work with firefox');
+ test.info().annotations.push({
+ type: 'issue',
+ description: 'https://github.com/nasa/openmct/issues/5265'
+ });
+
+ // Go to baseURL
+ await page.goto('/', { waitUntil: 'networkidle' });
+
+ // Click the Create button
+ await page.click('button:has-text("Create")');
+
+ // Click text=Example Imagery
+ await page.click('text=Example Imagery');
+
+ // Clear and set Image load delay to minimum value
+ // FIXME: Update the value to 5000 ms when this bug is fixed.
+ // See: https://github.com/nasa/openmct/issues/5265
+ await page.locator('input[type="number"]').fill('');
+ await page.locator('input[type="number"]').fill('0');
+
+ // Click text=OK
+ await Promise.all([
+ page.waitForNavigation({waitUntil: 'networkidle'}),
+ page.click('text=OK'),
+ //Wait for Save Banner to appear
+ page.waitForSelector('.c-message-banner__message')
+ ]);
+
+ // Wait until Save Banner is gone
+ await page.waitForSelector('.c-message-banner__message', { state: 'detached'});
+ await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Example Imagery');
+ await page.locator(backgroundImageSelector).hover({trial: true});
+
+ // Click previous image button
+ const previousImageButton = page.locator('.c-nav--prev');
+ await previousImageButton.click();
+
+ // Verify previous image
+ const selectedImage = page.locator('.selected');
+ await expect(selectedImage).toBeVisible();
+
+ // Zoom in
+ const originalImageDimensions = await page.locator(backgroundImageSelector).boundingBox();
+ await page.locator(backgroundImageSelector).hover({trial: true});
+ const deltaYStep = 100; // equivalent to 1x zoom
+ await page.mouse.wheel(0, deltaYStep * 2);
+ const zoomedBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
+ const imageCenterX = zoomedBoundingBox.x + zoomedBoundingBox.width / 2;
+ const imageCenterY = zoomedBoundingBox.y + zoomedBoundingBox.height / 2;
+
+ // Wait for zoom animation to finish
+ await page.locator(backgroundImageSelector).hover({trial: true});
+ const imageMouseZoomedIn = await page.locator(backgroundImageSelector).boundingBox();
+ expect(imageMouseZoomedIn.height).toBeGreaterThan(originalImageDimensions.height);
+ expect(imageMouseZoomedIn.width).toBeGreaterThan(originalImageDimensions.width);
+
+ // Center the mouse pointer
+ await page.mouse.move(imageCenterX, imageCenterY);
+
+ // Pan Imagery Hints
+ const expectedAltText = process.platform === 'linux' ? 'Ctrl+Alt drag to pan' : 'Alt drag to pan';
+ const imageryHintsText = await page.locator('.c-imagery__hints').innerText();
+ expect(expectedAltText).toEqual(imageryHintsText);
+
+ // Click next image button
+ const nextImageButton = page.locator('.c-nav--next');
+ await nextImageButton.click();
+
+ // Click time conductor mode button
+ await page.locator('.c-mode-button').click();
+
+ // Select local clock mode
+ await page.locator('[data-testid=conductor-modeOption-realtime]').click();
+
+ // Zoom in on next image
+ await page.locator(backgroundImageSelector).hover({trial: true});
+ await page.mouse.wheel(0, deltaYStep * 2);
+
+ // Wait for zoom animation to finish
+ await page.locator(backgroundImageSelector).hover({trial: true});
+ const imageNextMouseZoomedIn = await page.locator(backgroundImageSelector).boundingBox();
+ expect(imageNextMouseZoomedIn.height).toBeGreaterThan(originalImageDimensions.height);
+ expect(imageNextMouseZoomedIn.width).toBeGreaterThan(originalImageDimensions.width);
+
+ // Click previous image button
+ await previousImageButton.click();
+
+ // Verify previous image
+ await expect(selectedImage).toBeVisible();
+
+ const imageCount = await page.locator('.c-imagery__thumb').count();
+ await expect.poll(async () => {
+ const newImageCount = await page.locator('.c-imagery__thumb').count();
+
+ return newImageCount;
+ }, {
+ message: "verify that old images are discarded",
+ timeout: 6 * 1000
+ }).toBe(imageCount);
+
+ // Verify selected image is still displayed
+ await expect(selectedImage).toBeVisible();
+});
+
+test.describe('Example imagery thumbnails resize in display layouts', () => {
+ test('Resizing the layout changes thumbnail visibility and size', async ({ page }) => {
+ await page.goto('/', { waitUntil: 'networkidle' });
+
+ const thumbsWrapperLocator = page.locator('.c-imagery__thumbs-wrapper');
+ // Click button:has-text("Create")
+ await page.locator('button:has-text("Create")').click();
+
+ // Click li:has-text("Display Layout")
+ await page.locator('li:has-text("Display Layout")').click();
+ const displayLayoutTitleField = page.locator('text=Properties Title Notes Horizontal grid (px) Vertical grid (px) Horizontal size ( >> input[type="text"]');
+ await displayLayoutTitleField.click();
+
+ await displayLayoutTitleField.fill('Thumbnail Display Layout');
+
+ // Click text=OK
+ await Promise.all([
+ page.waitForNavigation(),
+ page.locator('text=OK').click()
+ ]);
+
+ // Click text=Snapshot Save and Finish Editing Save and Continue Editing >> button >> nth=1
+ await page.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button').nth(1).click();
+
+ // Click text=Save and Finish Editing
+ await page.locator('text=Save and Finish Editing').click();
+
+ // Click button:has-text("Create")
+ await page.locator('button:has-text("Create")').click();
+
+ // Click li:has-text("Example Imagery")
+ await page.locator('li:has-text("Example Imagery")').click();
+
+ const imageryTitleField = page.locator('text=Properties Title Notes Images url list (comma separated) Image load delay (milli >> input[type="text"]');
+ // Click text=Properties Title Notes Images url list (comma separated) Image load delay (milli >> input[type="text"]
+ await imageryTitleField.click();
+
+ // Fill text=Properties Title Notes Images url list (comma separated) Image load delay (milli >> input[type="text"]
+ await imageryTitleField.fill('Thumbnail Example Imagery');
+
+ // Click text=OK
+ await Promise.all([
+ page.waitForNavigation(),
+ page.locator('text=OK').click()
+ ]);
+
+ // Click text=Thumbnail Example Imagery Imagery Layout Snapshot >> button >> nth=0
+ await Promise.all([
+ page.waitForNavigation(),
+ page.locator('text=Thumbnail Example Imagery Imagery Layout Snapshot >> button').first().click()
+ ]);
+
+ // Edit mode
+ await page.locator('text=Thumbnail Display Layout Snapshot >> button').nth(3).click();
+
+ // Click on example imagery to expose toolbar
+ await page.locator('text=Thumbnail Example Imagery Snapshot Large View').click();
+
+ // expect thumbnails not be visible when first added
+ expect.soft(thumbsWrapperLocator.isHidden()).toBeTruthy();
+
+ // Resize the example imagery vertically to change the thumbnail visibility
+ /*
+ The following arbitrary values are added to observe the separate visual
+ conditions of the thumbnails (hidden, small thumbnails, regular thumbnails).
+ Specifically, height is set to 50px for small thumbs and 100px for regular
+ */
+ // Click #mct-input-id-103
+ await page.locator('#mct-input-id-103').click();
+
+ // Fill #mct-input-id-103
+ await page.locator('#mct-input-id-103').fill('50');
+
+ expect(thumbsWrapperLocator.isVisible()).toBeTruthy();
+ await expect(thumbsWrapperLocator).toHaveClass(/is-small-thumbs/);
+
+ // Resize the example imagery vertically to change the thumbnail visibility
+ // Click #mct-input-id-103
+ await page.locator('#mct-input-id-103').click();
+
+ // Fill #mct-input-id-103
+ await page.locator('#mct-input-id-103').fill('100');
+
+ expect(thumbsWrapperLocator.isVisible()).toBeTruthy();
+ await expect(thumbsWrapperLocator).not.toHaveClass(/is-small-thumbs/);
+ });
+});
+
+test.describe('Example Imagery in Flexible layout', () => {
+ test('Example Imagery in Flexible layout', async ({ page, browserName }) => {
+ test.fixme(browserName === 'firefox', 'This test needs to be updated to work with firefox');
+ test.info().annotations.push({
+ type: 'issue',
+ description: 'https://github.com/nasa/openmct/issues/5326'
+ });
+
+ // Go to baseURL
+ await page.goto('/', { waitUntil: 'networkidle' });
+
+ // Click the Create button
+ await page.click('button:has-text("Create")');
+
+ // Click text=Example Imagery
+ await page.click('text=Example Imagery');
+
+ // Clear and set Image load delay (milliseconds)
+ await page.click('input[type="number"]', {clickCount: 3});
+ await page.type('input[type="number"]', "20");
+
+ // Click text=OK
+ await Promise.all([
+ page.waitForNavigation({waitUntil: 'networkidle'}),
+ page.click('text=OK'),
+ //Wait for Save Banner to appear
+ page.waitForSelector('.c-message-banner__message')
+ ]);
+ // Wait until Save Banner is gone
+ await page.waitForSelector('.c-message-banner__message', { state: 'detached'});
+ await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Example Imagery');
+ await page.locator(backgroundImageSelector).hover({trial: true});
+
+ // Click the Create button
+ await page.click('button:has-text("Create")');
+
+ // Click text=Flexible Layout
+ await page.click('text=Flexible Layout');
+
+ // Assert Flexable layout
+ await expect(page.locator('.js-form-title')).toHaveText('Create a New Flexible Layout');
+
+ await page.locator('form[name="mctForm"] >> text=My Items').click();
+
+ // Click My Items
+ await Promise.all([
+ page.locator('text=OK').click(),
+ page.waitForNavigation({waitUntil: 'networkidle'})
+ ]);
+
+ // Click My Items
+ await page.locator('.c-disclosure-triangle').click();
+
+ // Right click example imagery
+ await page.click(('text=Unnamed Example Imagery'), { button: 'right' });
+
+ // Click move
+ await page.locator('.icon-move').click();
+
+ // Click triangle to open sub menu
+ await page.locator('.c-form__section .c-disclosure-triangle').click();
+
+ // Click Flexable Layout
+ await page.click('.c-overlay__outer >> text=Unnamed Flexible Layout');
+
+ // Click text=OK
+ await page.locator('text=OK').click();
+
+ // Save template
+ await saveTemplate(page);
+
+ // Zoom in
+ await mouseZoomIn(page);
+
+ // Center the mouse pointer
+ const zoomedBoundingBox = await await page.locator(backgroundImageSelector).boundingBox();
+ const imageCenterX = zoomedBoundingBox.x + zoomedBoundingBox.width / 2;
+ const imageCenterY = zoomedBoundingBox.y + zoomedBoundingBox.height / 2;
+ await page.mouse.move(imageCenterX, imageCenterY);
+
+ // Pan zoom
+ await panZoomAndAssertImageProperties(page);
+
+ // Click previous image button
+ const previousImageButton = page.locator('.c-nav--prev');
+ await previousImageButton.click();
+
+ // Verify previous image
+ const selectedImage = page.locator('.selected');
+ await expect(selectedImage).toBeVisible();
+
+ // Click time conductor mode button
+ await page.locator('.c-mode-button').click();
+
+ // Select local clock mode
+ await page.locator('[data-testid=conductor-modeOption-realtime]').click();
+
+ // Zoom in on next image
+ await mouseZoomIn(page);
+
+ // Click previous image button
+ await previousImageButton.click();
+
+ // Verify previous image
+ await expect(selectedImage).toBeVisible();
+
+ const imageCount = await page.locator('.c-imagery__thumb').count();
+ await expect.poll(async () => {
+ const newImageCount = await page.locator('.c-imagery__thumb').count();
+
+ return newImageCount;
+ }, {
+ message: "verify that old images are discarded",
+ timeout: 6 * 1000
+ }).toBe(imageCount);
+
+ // Verify selected image is still displayed
+ await expect(selectedImage).toBeVisible();
+
+ // Unpause imagery
+ await page.locator('.pause-play').click();
+
+ //Get background-image url from background-image css prop
+ await assertBackgroundImageUrlFromBackgroundCss(page);
+
+ // Open the image filter menu
+ await page.locator('[role=toolbar] button[title="Brightness and contrast"]').click();
+
+ // Drag the brightness and contrast sliders around and assert filter values
+ await dragBrightnessSliderAndAssertFilterValues(page);
+ await dragContrastSliderAndAssertFilterValues(page);
+ });
+});
+
+test.describe('Example Imagery in Tabs view', () => {
+ test.fixme('Can use Mouse Wheel to zoom in and out of previous image');
+ test.fixme('Can use alt+drag to move around image once zoomed in');
+ test.fixme('Can zoom into the latest image and the real-time/fixed-time imagery will pause');
+ test.fixme('Can zoom into a previous image from thumbstrip in real-time or fixed-time');
+ test.fixme('Clicking on the left arrow should pause the imagery and go to previous image');
+ test.fixme('If the imagery view is in pause mode, it should not be updated when new images come in');
+ test.fixme('If the imagery view is not in pause mode, it should be updated when new images come in');
+});
+
+/**
+ * @param {import('@playwright/test').Page} page
+ */
+async function saveTemplate(page) {
+ await page.locator('.c-button--menu.c-button--major.icon-save').click();
+ await page.locator('text=Save and Finish Editing').click();
+}
+
+/**
+ * Drag the brightness slider to max, min, and midpoint and assert the filter values
+ * @param {import('@playwright/test').Page} page
+ */
+async function dragBrightnessSliderAndAssertFilterValues(page) {
+ const brightnessSlider = 'div.c-image-controls__slider-wrapper.icon-brightness > input';
+ const brightnessBoundingBox = await page.locator(brightnessSlider).boundingBox();
+ const brightnessMidX = brightnessBoundingBox.x + brightnessBoundingBox.width / 2;
+ const brightnessMidY = brightnessBoundingBox.y + brightnessBoundingBox.height / 2;
+
+ await page.locator(brightnessSlider).hover({trial: true});
+ await page.mouse.down();
+ await page.mouse.move(brightnessBoundingBox.x + brightnessBoundingBox.width, brightnessMidY);
+ await assertBackgroundImageBrightness(page, '500');
+ await page.mouse.move(brightnessBoundingBox.x, brightnessMidY);
+ await assertBackgroundImageBrightness(page, '0');
+ await page.mouse.move(brightnessMidX, brightnessMidY);
+ await assertBackgroundImageBrightness(page, '250');
+ await page.mouse.up();
+}
+
+/**
+ * Drag the contrast slider to max, min, and midpoint and assert the filter values
+ * @param {import('@playwright/test').Page} page
+ */
+async function dragContrastSliderAndAssertFilterValues(page) {
+ const contrastSlider = 'div.c-image-controls__slider-wrapper.icon-contrast > input';
+ const contrastBoundingBox = await page.locator(contrastSlider).boundingBox();
+ const contrastMidX = contrastBoundingBox.x + contrastBoundingBox.width / 2;
+ const contrastMidY = contrastBoundingBox.y + contrastBoundingBox.height / 2;
+
+ await page.locator(contrastSlider).hover({trial: true});
+ await page.mouse.down();
+ await page.mouse.move(contrastBoundingBox.x + contrastBoundingBox.width, contrastMidY);
+ await assertBackgroundImageContrast(page, '500');
+ await page.mouse.move(contrastBoundingBox.x, contrastMidY);
+ await assertBackgroundImageContrast(page, '0');
+ await page.mouse.move(contrastMidX, contrastMidY);
+ await assertBackgroundImageContrast(page, '250');
+ await page.mouse.up();
+}
+
+/**
+ * Gets the filter:brightness value of the current background-image and
+ * asserts against an expected value
+ * @param {import('@playwright/test').Page} page
+ * @param {String} expected The expected brightness value
+ */
+async function assertBackgroundImageBrightness(page, expected) {
+ const backgroundImage = page.locator('.c-imagery__main-image__background-image');
+
+ // Get the brightness filter value (i.e: filter: brightness(500%) => "500")
+ const actual = await backgroundImage.evaluate((el) => {
+ return el.style.filter.match(/brightness\((\d{1,3})%\)/)[1];
+ });
+ expect(actual).toBe(expected);
+}
+
+/**
+ * Gets the filter:contrast value of the current background-image and
+ * asserts against an expected value
+ * @param {import('@playwright/test').Page} page
+ * @param {String} expected The expected contrast value
+ */
+async function assertBackgroundImageContrast(page, expected) {
+ const backgroundImage = page.locator('.c-imagery__main-image__background-image');
+
+ // Get the contrast filter value (i.e: filter: contrast(500%) => "500")
+ const actual = await backgroundImage.evaluate((el) => {
+ return el.style.filter.match(/contrast\((\d{1,3})%\)/)[1];
+ });
+ expect(actual).toBe(expected);
+}
+
+/**
+ * @param {import('@playwright/test').Page} page
+ */
+async function assertBackgroundImageUrlFromBackgroundCss(page) {
+ const backgroundImage = page.locator('.c-imagery__main-image__background-image');
+ let backgroundImageUrl = await backgroundImage.evaluate((el) => {
+ return window.getComputedStyle(el).getPropertyValue('background-image').match(/url\(([^)]+)\)/)[1];
+ });
+ let backgroundImageUrl1 = backgroundImageUrl.slice(1, -1); //forgive me, padre
+ console.log('backgroundImageUrl1 ' + backgroundImageUrl1);
+
+ let backgroundImageUrl2;
+ await expect.poll(async () => {
+ // Verify next image has updated
+ let backgroundImageUrlNext = await backgroundImage.evaluate((el) => {
+ return window.getComputedStyle(el).getPropertyValue('background-image').match(/url\(([^)]+)\)/)[1];
+ });
+ backgroundImageUrl2 = backgroundImageUrlNext.slice(1, -1); //forgive me, padre
+
+ return backgroundImageUrl2;
+ }, {
+ message: "verify next image has updated",
+ timeout: 6 * 1000
+ }).not.toBe(backgroundImageUrl1);
+ console.log('backgroundImageUrl2 ' + backgroundImageUrl2);
+}
+
+/**
+ * @param {import('@playwright/test').Page} page
+ */
+async function panZoomAndAssertImageProperties(page) {
+ const panHotkey = process.platform === 'linux' ? ['Control', 'Alt'] : ['Alt'];
+ const expectedAltText = process.platform === 'linux' ? 'Ctrl+Alt drag to pan' : 'Alt drag to pan';
+ const imageryHintsText = await page.locator('.c-imagery__hints').innerText();
+ expect(expectedAltText).toEqual(imageryHintsText);
+ const zoomedBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
+ const imageCenterX = zoomedBoundingBox.x + zoomedBoundingBox.width / 2;
+ const imageCenterY = zoomedBoundingBox.y + zoomedBoundingBox.height / 2;
+
+ // Pan right
+ await Promise.all(panHotkey.map(x => page.keyboard.down(x)));
+ await page.mouse.down();
+ await page.mouse.move(imageCenterX - 200, imageCenterY, 10);
+ await page.mouse.up();
+ await Promise.all(panHotkey.map(x => page.keyboard.up(x)));
+ const afterRightPanBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
+ expect(zoomedBoundingBox.x).toBeGreaterThan(afterRightPanBoundingBox.x);
+
+ // Pan left
+ await Promise.all(panHotkey.map(x => page.keyboard.down(x)));
+ await page.mouse.down();
+ await page.mouse.move(imageCenterX, imageCenterY, 10);
+ await page.mouse.up();
+ await Promise.all(panHotkey.map(x => page.keyboard.up(x)));
+ const afterLeftPanBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
+ expect(afterRightPanBoundingBox.x).toBeLessThan(afterLeftPanBoundingBox.x);
+
+ // Pan up
+ await page.mouse.move(imageCenterX, imageCenterY);
+ await Promise.all(panHotkey.map(x => page.keyboard.down(x)));
+ await page.mouse.down();
+ await page.mouse.move(imageCenterX, imageCenterY + 200, 10);
+ await page.mouse.up();
+ await Promise.all(panHotkey.map(x => page.keyboard.up(x)));
+ const afterUpPanBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
+ expect(afterUpPanBoundingBox.y).toBeGreaterThanOrEqual(afterLeftPanBoundingBox.y);
+
+ // Pan down
+ await Promise.all(panHotkey.map(x => page.keyboard.down(x)));
+ await page.mouse.down();
+ await page.mouse.move(imageCenterX, imageCenterY - 200, 10);
+ await page.mouse.up();
+ await Promise.all(panHotkey.map(x => page.keyboard.up(x)));
+ const afterDownPanBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
+ expect(afterDownPanBoundingBox.y).toBeLessThanOrEqual(afterUpPanBoundingBox.y);
+}
+
+/**
+ * @param {import('@playwright/test').Page} page
+*/
+async function mouseZoomIn(page) {
+ // Zoom in
+ const originalImageDimensions = await page.locator(backgroundImageSelector).boundingBox();
+ await page.locator(backgroundImageSelector).hover({trial: true});
+ const deltaYStep = 100; // equivalent to 1x zoom
+ await page.mouse.wheel(0, deltaYStep * 2);
+ const zoomedBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
+ const imageCenterX = zoomedBoundingBox.x + zoomedBoundingBox.width / 2;
+ const imageCenterY = zoomedBoundingBox.y + zoomedBoundingBox.height / 2;
+
+ // center the mouse pointer
+ await page.mouse.move(imageCenterX, imageCenterY);
+
+ // Wait for zoom animation to finish
+ await page.locator(backgroundImageSelector).hover({trial: true});
+ const imageMouseZoomedIn = await page.locator(backgroundImageSelector).boundingBox();
+ expect(imageMouseZoomedIn.height).toBeGreaterThan(originalImageDimensions.height);
+ expect(imageMouseZoomedIn.width).toBeGreaterThan(originalImageDimensions.width);
+}
diff --git a/src/adapter/capabilities/AdapterCapability.js b/e2e/tests/plugins/notebook/addInitRestrictedNotebook.js
index e98ac518f..dd303fb52 100644
--- a/src/adapter/capabilities/AdapterCapability.js
+++ b/e2e/tests/plugins/notebook/addInitRestrictedNotebook.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -20,17 +20,11 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
-define(['objectUtils'], function (objectUtils) {
- function AdapterCapability(domainObject) {
- this.domainObject = domainObject;
- }
+// this will be called from the test suite with
+// await page.addInitScript({ path: path.join(__dirname, 'addInitRestrictedNotebook.js') });
+// it will install the RestrictedNotebook since it is not installed by default
- AdapterCapability.prototype.invoke = function () {
- return objectUtils.toNewFormat(
- this.domainObject.getModel(),
- this.domainObject.getId()
- );
- };
-
- return AdapterCapability;
+document.addEventListener('DOMContentLoaded', () => {
+ const openmct = window.openmct;
+ openmct.install(openmct.plugins.RestrictedNotebook('CUSTOM_NAME'));
});
diff --git a/e2e/tests/plugins/notebook/notebook.e2e.spec.js b/e2e/tests/plugins/notebook/notebook.e2e.spec.js
new file mode 100644
index 000000000..021946c87
--- /dev/null
+++ b/e2e/tests/plugins/notebook/notebook.e2e.spec.js
@@ -0,0 +1,198 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+/*
+This test suite is dedicated to tests which verify the basic operations surrounding Notebooks.
+*/
+
+const { test } = require('../../../fixtures');
+
+test.describe('Notebook CRUD Operations', () => {
+ test.fixme('Can create a Notebook Object', async ({ page }) => {
+ //Create domain object
+ //Newly created notebook should have one Section and one page, 'Unnamed Section'/'Unnamed Page'
+ });
+ test.fixme('Can update a Notebook Object', async ({ page }) => {});
+ test.fixme('Can view a perviously created Notebook Object', async ({ page }) => {});
+ test.fixme('Can Delete a Notebook Object', async ({ page }) => {
+ // Other than non-persistible objects
+ });
+});
+
+test.describe('Default Notebook', () => {
+ // General Default Notebook statements
+ // ## Useful commands:
+ // 1. - To check default notebook:
+ // `JSON.parse(localStorage.getItem('notebook-storage'));`
+ // 1. - Clear default notebook:
+ // `localStorage.setItem('notebook-storage', null);`
+ test.fixme('A newly created Notebook is automatically set as the default notebook if no other notebooks exist', async ({ page }) => {
+ //Create new notebook
+ //Verify Default Notebook Characteristics
+ });
+ test.fixme('A newly created Notebook is automatically set as the default notebook if at least one other notebook exists', async ({ page }) => {
+ //Create new notebook A
+ //Create second notebook B
+ //Verify Non-Default Notebook A Characteristics
+ //Verify Default Notebook B Characteristics
+ });
+ test.fixme('If a default notebook is deleted, the second most recent notebook becomes the default', async ({ page }) => {
+ //Create new notebook A
+ //Create second notebook B
+ //Delete Notebook B
+ //Verify Default Notebook A Characteristics
+ });
+});
+
+test.describe('Notebook section tests', () => {
+ //The following test cases are associated with Notebook Sections
+ test.fixme('New sections are automatically named Unnamed Section with Unnamed Page', async ({ page }) => {
+ //Create new notebook A
+ //Add section
+ //Verify new section and new page details
+ });
+ test.fixme('Section selection operations and associated behavior', async ({ page }) => {
+ //Create new notebook A
+ //Add Sections until 6 total with no default section/page
+ //Select 3rd section
+ //Delete 4th section
+ //3rd section is still selected
+ //Delete 3rd section
+ //1st section is selected
+ //Set 3rd section as default
+ //Delete 2nd section
+ //3rd section is still default
+ //Delete 3rd section
+ //1st is selected and there is no default notebook
+ });
+});
+
+test.describe('Notebook page tests', () => {
+ //The following test cases are associated with Notebook Pages
+ test.fixme('Page selection operations and associated behavior', async ({ page }) => {
+ //Create new notebook A
+ //Delete existing Page
+ //New 'Unnamed Page' automatically created
+ //Create 6 total Pages without a default page
+ //Select 3rd
+ //Delete 3rd
+ //First is now selected
+ //Set 3rd as default
+ //Select 2nd page
+ //Delete 2nd page
+ //3rd (default) is now selected
+ //Set 3rd as default page
+ //Select 3rd (default) page
+ //Delete 3rd page
+ //First is now selected and there is no default notebook
+ });
+});
+
+test.describe('Notebook search tests', () => {
+ test.fixme('Can search for a single result', async ({ page }) => {});
+ test.fixme('Can search for many results', async ({ page }) => {});
+ test.fixme('Can search for new and recently modified entries', async ({ page }) => {});
+ test.fixme('Can search for section text', async ({ page }) => {});
+ test.fixme('Can search for page text', async ({ page }) => {});
+ test.fixme('Can search for entry text', async ({ page }) => {});
+});
+
+test.describe('Notebook entry tests', () => {
+ test.fixme('When a new entry is created, it should be focused', async ({ page }) => {});
+ test.fixme('When a telemetry object is dropped into a notebook, a new entry is created and it should be focused', async ({ page }) => {
+ // Drag and drop any telmetry object on 'drop object'
+ // new entry gets created with telemtry object
+ });
+ test.fixme('When a telemetry object is dropped into a notebooks existing entry, it should be focused', async ({ page }) => {
+ // Drag and drop any telemetry object onto existing entry
+ // Entry updated with object and snapshot
+ });
+ test.fixme('new entries persist through navigation events without save', async ({ page }) => {});
+ test.fixme('previous and new entries can be deleted', async ({ page }) => {});
+});
+
+test.describe('Snapshot Menu tests', () => {
+ test.fixme('When no default notebook is selected, Snapshot Menu dropdown should only have a single option', async ({ page }) => {
+ // There should be no default notebook
+ // Clear default notebook if exists using `localStorage.setItem('notebook-storage', null);`
+ // refresh page
+ // Click on 'Notebook Snaphot Menu'
+ // 'save to Notebook Snapshots' should be only option there
+ });
+ test.fixme('When default notebook is updated selected, Snapshot Menu dropdown should list it as the newest option', async ({ page }) => {
+ // Create 2a notebooks
+ // Set Notebook A as Default
+ // Open Snapshot Menu and note that Notebook A is listed
+ // Close Snapshot Menu
+ // Set Default Notebook to Notebook B
+ // Open Snapshot Notebook and note that Notebook B is listed
+ // Select Default Notebook Option and verify that Snapshot is added to Notebook B
+ });
+ test.fixme('Can add Snapshots via Snapshot Menu and details are correct', async ({ page }) => {
+ //Note this should be a visual test, too
+ // Create Telemetry object
+ // Create A notebook with many pages and sections.
+ // Set page and section defaults to be between first and last of many. i.e. 3 of 5
+ // Navigate to Telemetry object
+ // Select Default Notebook Option and verify that Snapshot is added to Notebook A
+ // Verify Snapshot Details appear correctly
+ });
+ test.fixme('Snapshots adjust time conductor', async ({ page }) => {
+ // Create Telemetry object
+ // Set Telemetry object's timeconductor to Fixed time with Start and Endtimes are recorded
+ // Embed Telemetry object into notebook
+ // Set Time Conductor to Local clock
+ // Click into embedded telemetry object and verify object appears with same fixed time from record
+ });
+});
+
+test.describe('Snapshot Container tests', () => {
+ test.fixme('5 Snapshots can be added to a container', async ({ page }) => {});
+ test.fixme('5 Snapshots can be added to a container and Deleted with Delete All action', async ({ page }) => {});
+ test.fixme('A snapshot can be Deleted from Container', async ({ page }) => {});
+ test.fixme('A snapshot can be Previewed from Container', async ({ page }) => {});
+ test.fixme('A snapshot Container can be open and closed', async ({ page }) => {});
+ test.fixme('Can add object to Snapshot container and pull into notebook and create a new entry', async ({ page }) => {
+ //Create Notebook
+ //Create Telemetry Object
+ //From Telemetry Object, use 'save to Notebook Snapshots'
+ //Snapshots indicator should blink, click on it to view snapshots
+ //Navigate to Notebook
+ //Drag and Drop onto droppable area for new entry
+ //New Entry created with given snapshot added
+ //Snapshot removed from container?
+ });
+ test.fixme('Can add object to Snapshot container and pull into notebook and existing entry', async ({ page }) => {
+ //Create Notebook
+ //Create Telemetry Object
+ //From Telemetry Object, use 'save to Notebook Snapshots'
+ //Snapshots indicator should blink, click on it to view snapshots
+ //Navigate to Notebook
+ //Drag and Drop into exiting entry
+ //Existing Entry updated with given snapshot
+ //Snapshot removed from container?
+ });
+ test.fixme('Verify Embedded options for PNG, JPG, and Annotate work correctly', async ({ page }) => {
+ //Add snapshot to container
+ //Verify PNG, JPG, and Annotate buttons work correctly
+ });
+});
diff --git a/e2e/tests/plugins/notebook/restrictedNotebook.e2e.spec.js b/e2e/tests/plugins/notebook/restrictedNotebook.e2e.spec.js
new file mode 100644
index 000000000..18c4653d8
--- /dev/null
+++ b/e2e/tests/plugins/notebook/restrictedNotebook.e2e.spec.js
@@ -0,0 +1,255 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+const { test } = require('../../../fixtures');
+const { expect } = require('@playwright/test');
+const path = require('path');
+
+const TEST_TEXT = 'Testing text for entries.';
+const TEST_TEXT_NAME = 'Test Page';
+const CUSTOM_NAME = 'CUSTOM_NAME';
+const NOTEBOOK_DROP_AREA = '.c-notebook__drag-area';
+
+test.describe('Restricted Notebook', () => {
+ test.beforeEach(async ({ page }) => {
+ await startAndAddRestrictedNotebookObject(page);
+ });
+
+ test('Can be renamed @addInit', async ({ page }) => {
+ await expect(page.locator('.l-browse-bar__object-name')).toContainText(`Unnamed ${CUSTOM_NAME}`);
+ });
+
+ test('Can be deleted if there are no locked pages @addInit', async ({ page }) => {
+ await openContextMenuRestrictedNotebook(page);
+
+ const menuOptions = page.locator('.c-menu ul');
+ await expect.soft(menuOptions).toContainText('Remove');
+
+ const restrictedNotebookTreeObject = page.locator(`a:has-text("Unnamed ${CUSTOM_NAME}")`);
+
+ // notbook tree object exists
+ expect.soft(await restrictedNotebookTreeObject.count()).toEqual(1);
+
+ // Click Remove Text
+ await page.locator('text=Remove').click();
+
+ // Click 'OK' on confirmation window and wait for save banner to appear
+ await Promise.all([
+ page.waitForNavigation(),
+ page.locator('text=OK').click(),
+ page.waitForSelector('.c-message-banner__message')
+ ]);
+
+ // has been deleted
+ expect(await restrictedNotebookTreeObject.count()).toEqual(0);
+ });
+
+ test('Can be locked if at least one page has one entry @addInit', async ({ page }) => {
+
+ await enterTextEntry(page);
+
+ const commitButton = page.locator('button:has-text("Commit Entries")');
+ expect(await commitButton.count()).toEqual(1);
+ });
+
+});
+
+test.describe('Restricted Notebook with at least one entry and with the page locked @addInit', () => {
+
+ test.beforeEach(async ({ page }) => {
+ await startAndAddRestrictedNotebookObject(page);
+ await enterTextEntry(page);
+ await lockPage(page);
+
+ // FIXME: Give ample time for the mutation to happen
+ // https://github.com/nasa/openmct/issues/5409
+ // eslint-disable-next-line playwright/no-wait-for-timeout
+ await page.waitForTimeout(1 * 1000);
+
+ // open sidebar
+ await page.locator('button.c-notebook__toggle-nav-button').click();
+ });
+
+ test('Locked page should now be in a locked state @addInit', async ({ page }, testInfo) => {
+ test.fixme(testInfo.project === 'chrome-beta', "Test is unreliable on chrome-beta");
+ // main lock message on page
+ const lockMessage = page.locator('text=This page has been committed and cannot be modified or removed');
+ expect.soft(await lockMessage.count()).toEqual(1);
+
+ // lock icon on page in sidebar
+ const pageLockIcon = page.locator('ul.c-notebook__pages li div.icon-lock');
+ expect.soft(await pageLockIcon.count()).toEqual(1);
+
+ // no way to remove a restricted notebook with a locked page
+ await openContextMenuRestrictedNotebook(page);
+ const menuOptions = page.locator('.c-menu ul');
+
+ await expect(menuOptions).not.toContainText('Remove');
+ });
+
+ test('Can still: add page, rename, add entry, delete unlocked pages @addInit', async ({ page }) => {
+ // Click text=Page Add >> button
+ await Promise.all([
+ page.waitForNavigation(),
+ page.locator('text=Page Add >> button').click()
+ ]);
+ // Click text=Unnamed Page >> nth=1
+ await page.locator('text=Unnamed Page').nth(1).click();
+ // Press a with modifiers
+ await page.locator('text=Unnamed Page').nth(1).fill(TEST_TEXT_NAME);
+
+ // expect to be able to rename unlocked pages
+ const newPageElement = page.locator(`text=${TEST_TEXT_NAME}`);
+ const newPageCount = await newPageElement.count();
+ await newPageElement.press('Enter'); // exit contenteditable state
+ expect.soft(newPageCount).toEqual(1);
+
+ // enter test text
+ await enterTextEntry(page);
+
+ // expect new page to be lockable
+ const commitButton = page.locator('BUTTON:HAS-TEXT("COMMIT ENTRIES")');
+ expect.soft(await commitButton.count()).toEqual(1);
+
+ // Click text=Unnamed PageTest Page >> button
+ await page.locator('text=Unnamed PageTest Page >> button').click();
+ // Click text=Delete Page
+ await page.locator('text=Delete Page').click();
+ // Click text=Ok
+ await Promise.all([
+ page.waitForNavigation(),
+ page.locator('text=Ok').click()
+ ]);
+
+ // deleted page, should no longer exist
+ const deletedPageElement = page.locator(`text=${TEST_TEXT_NAME}`);
+ expect(await deletedPageElement.count()).toEqual(0);
+ });
+});
+
+test.describe('Restricted Notebook with a page locked and with an embed @addInit', () => {
+
+ test.beforeEach(async ({ page }) => {
+ await startAndAddRestrictedNotebookObject(page);
+ await dragAndDropEmbed(page);
+ });
+
+ test('Allows embeds to be deleted if page unlocked @addInit', async ({ page }) => {
+ // Click .c-ne__embed__name .c-popup-menu-button
+ await page.locator('.c-ne__embed__name .c-popup-menu-button').click(); // embed popup menu
+
+ const embedMenu = page.locator('body >> .c-menu');
+ await expect(embedMenu).toContainText('Remove This Embed');
+ });
+
+ test('Disallows embeds to be deleted if page locked @addInit', async ({ page }) => {
+ await lockPage(page);
+ // Click .c-ne__embed__name .c-popup-menu-button
+ await page.locator('.c-ne__embed__name .c-popup-menu-button').click(); // embed popup menu
+
+ const embedMenu = page.locator('body >> .c-menu');
+ await expect(embedMenu).not.toContainText('Remove This Embed');
+ });
+
+});
+
+/**
+ * @param {import('@playwright/test').Page} page
+ */
+async function startAndAddRestrictedNotebookObject(page) {
+ // eslint-disable-next-line no-undef
+ await page.addInitScript({ path: path.join(__dirname, 'addInitRestrictedNotebook.js') });
+ //Go to baseURL
+ await page.goto('/', { waitUntil: 'networkidle' });
+ //Click the Create button
+ await page.click('button:has-text("Create")');
+ // Click text=CUSTOME_NAME
+ await page.click(`text=${CUSTOM_NAME}`); // secondarily tests renamability also
+ // Click text=OK
+ await Promise.all([
+ page.waitForNavigation({waitUntil: 'networkidle'}),
+ page.click('text=OK')
+ ]);
+}
+
+/**
+ * @param {import('@playwright/test').Page} page
+ */
+async function enterTextEntry(page) {
+ // Click .c-notebook__drag-area
+ await page.locator(NOTEBOOK_DROP_AREA).click();
+
+ // enter text
+ await page.locator('div.c-ne__text').click();
+ await page.locator('div.c-ne__text').fill(TEST_TEXT);
+ await page.locator('div.c-ne__text').press('Enter');
+}
+
+/**
+ * @param {import('@playwright/test').Page} page
+ */
+async function dragAndDropEmbed(page) {
+ // Click button:has-text("Create")
+ await page.locator('button:has-text("Create")').click();
+ // Click li:has-text("Sine Wave Generator")
+ await page.locator('li:has-text("Sine Wave Generator")').click();
+ // Click form[name="mctForm"] >> text=My Items
+ await page.locator('form[name="mctForm"] >> text=My Items').click();
+ // Click text=OK
+ await page.locator('text=OK').click();
+ // Click text=Open MCT My Items >> span >> nth=3
+ await page.locator('text=Open MCT My Items >> span').nth(3).click();
+ // Click text=Unnamed CUSTOM_NAME
+ await Promise.all([
+ page.waitForNavigation(),
+ page.locator('text=Unnamed CUSTOM_NAME').click()
+ ]);
+
+ await page.dragAndDrop('text=UNNAMED SINE WAVE GENERATOR', NOTEBOOK_DROP_AREA);
+}
+
+/**
+ * @param {import('@playwright/test').Page} page
+ */
+async function lockPage(page) {
+ const commitButton = page.locator('button:has-text("Commit Entries")');
+ await commitButton.click();
+
+ //Wait until Lock Banner is visible
+ await page.locator('text=Lock Page').click();
+}
+
+/**
+ * @param {import('@playwright/test').Page} page
+ */
+async function openContextMenuRestrictedNotebook(page) {
+ const myItemsFolder = page.locator('text=Open MCT My Items >> span').nth(3);
+ const className = await myItemsFolder.getAttribute('class');
+ if (!className.includes('c-disclosure-triangle--expanded')) {
+ await myItemsFolder.click();
+ }
+
+ // Click a:has-text("Unnamed CUSTOM_NAME")
+ await page.locator(`a:has-text("Unnamed ${CUSTOM_NAME}")`).click({
+ button: 'right'
+ });
+}
diff --git a/e2e/tests/plugins/notebook/tags.e2e.spec.js b/e2e/tests/plugins/notebook/tags.e2e.spec.js
new file mode 100644
index 000000000..c7f145c2e
--- /dev/null
+++ b/e2e/tests/plugins/notebook/tags.e2e.spec.js
@@ -0,0 +1,205 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+/*
+This test suite is dedicated to tests which verify form functionality.
+*/
+
+const { expect } = require('@playwright/test');
+const { test } = require('../../../fixtures');
+
+/**
+ * Creates a notebook object and adds an entry.
+ * @param {import('@playwright/test').Page} - page to load
+ * @param {number} [iterations = 1] - the number of entries to create
+ */
+async function createNotebookAndEntry(page, iterations = 1) {
+ //Go to baseURL
+ await page.goto('/', { waitUntil: 'networkidle' });
+
+ // Click button:has-text("Create")
+ await page.locator('button:has-text("Create")').click();
+ await page.locator('[title="Create and save timestamped notes with embedded object snapshots."]').click();
+ // Click button:has-text("OK")
+ await Promise.all([
+ page.waitForNavigation(),
+ page.locator('[name="mctForm"] >> text=My Items').click(),
+ page.locator('button:has-text("OK")').click()
+ ]);
+
+ for (let iteration = 0; iteration < iterations; iteration++) {
+ // Click text=To start a new entry, click here or drag and drop any object
+ await page.locator('text=To start a new entry, click here or drag and drop any object').click();
+ const entryLocator = `[aria-label="Notebook Entry Input"] >> nth = ${iteration}`;
+ await page.locator(entryLocator).click();
+ await page.locator(entryLocator).fill(`Entry ${iteration}`);
+ }
+
+}
+
+/**
+ * Creates a notebook object, adds an entry, and adds a tag.
+ * @param {import('@playwright/test').Page} page
+ * @param {number} [iterations = 1] - the number of entries (and tags) to create
+ */
+async function createNotebookEntryAndTags(page, iterations = 1) {
+ await createNotebookAndEntry(page, iterations);
+
+ for (let iteration = 0; iteration < iterations; iteration++) {
+ // Click text=To start a new entry, click here or drag and drop any object
+ await page.locator(`button:has-text("Add Tag") >> nth = ${iteration}`).click();
+
+ // Click [placeholder="Type to select tag"]
+ await page.locator('[placeholder="Type to select tag"]').click();
+ // Click text=Driving
+ await page.locator('[aria-label="Autocomplete Options"] >> text=Driving').click();
+
+ // Click button:has-text("Add Tag")
+ await page.locator(`button:has-text("Add Tag") >> nth = ${iteration}`).click();
+ // Click [placeholder="Type to select tag"]
+ await page.locator('[placeholder="Type to select tag"]').click();
+ // Click text=Science
+ await page.locator('[aria-label="Autocomplete Options"] >> text=Science').click();
+ }
+}
+
+test.describe('Tagging in Notebooks', () => {
+ test('Can load tags', async ({ page }) => {
+ await createNotebookAndEntry(page);
+ // Click text=To start a new entry, click here or drag and drop any object
+ await page.locator('button:has-text("Add Tag")').click();
+
+ // Click [placeholder="Type to select tag"]
+ await page.locator('[placeholder="Type to select tag"]').click();
+
+ await expect(page.locator('[aria-label="Autocomplete Options"]')).toContainText("Science");
+ await expect(page.locator('[aria-label="Autocomplete Options"]')).toContainText("Drilling");
+ await expect(page.locator('[aria-label="Autocomplete Options"]')).toContainText("Driving");
+ });
+ test('Can add tags', async ({ page }) => {
+ await createNotebookEntryAndTags(page);
+
+ await expect(page.locator('[aria-label="Notebook Entry"]')).toContainText("Science");
+ await expect(page.locator('[aria-label="Notebook Entry"]')).toContainText("Driving");
+
+ // Click button:has-text("Add Tag")
+ await page.locator('button:has-text("Add Tag")').click();
+ // Click [placeholder="Type to select tag"]
+ await page.locator('[placeholder="Type to select tag"]').click();
+
+ await expect(page.locator('[aria-label="Autocomplete Options"]')).not.toContainText("Science");
+ await expect(page.locator('[aria-label="Autocomplete Options"]')).not.toContainText("Driving");
+ await expect(page.locator('[aria-label="Autocomplete Options"]')).toContainText("Drilling");
+ });
+ test('Can search for tags', async ({ page }) => {
+ await createNotebookEntryAndTags(page);
+ // Click [aria-label="OpenMCT Search"] input[type="search"]
+ await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
+ // Fill [aria-label="OpenMCT Search"] input[type="search"]
+ await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('sc');
+ await expect(page.locator('[aria-label="Search Result"]')).toContainText("Science");
+ await expect(page.locator('[aria-label="Search Result"]')).toContainText("Driving");
+
+ // Click [aria-label="OpenMCT Search"] input[type="search"]
+ await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
+ // Fill [aria-label="OpenMCT Search"] input[type="search"]
+ await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Sc');
+ await expect(page.locator('[aria-label="Search Result"]')).toContainText("Science");
+ await expect(page.locator('[aria-label="Search Result"]')).toContainText("Driving");
+
+ // Click [aria-label="OpenMCT Search"] input[type="search"]
+ await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
+ // Fill [aria-label="OpenMCT Search"] input[type="search"]
+ await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Xq');
+ await expect(page.locator('[aria-label="Search Result"]')).not.toBeVisible();
+ await expect(page.locator('[aria-label="Search Result"]')).not.toBeVisible();
+ });
+
+ test('Can delete tags', async ({ page }) => {
+ await createNotebookEntryAndTags(page);
+ await page.locator('[aria-label="Notebook Entries"]').click();
+ // Delete Driving
+ await page.locator('text=Science Driving Add Tag >> button').nth(1).click();
+
+ await expect(page.locator('[aria-label="Notebook Entry"]')).toContainText("Science");
+ await expect(page.locator('[aria-label="Notebook Entry"]')).not.toContainText("Driving");
+
+ // Fill [aria-label="OpenMCT Search"] input[type="search"]
+ await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('sc');
+ await expect(page.locator('[aria-label="Search Result"]')).not.toContainText("Driving");
+ });
+ test('Tags persist across reload', async ({ page }) => {
+ //Go to baseURL
+ await page.goto('/', { waitUntil: 'networkidle' });
+
+ // Create a clock object we can navigate to
+ await page.click('button:has-text("Create")');
+
+ // Click Clock
+ await page.click('text=Clock');
+ // Click button:has-text("OK")
+ await Promise.all([
+ page.waitForNavigation(),
+ page.locator('[name="mctForm"] >> text=My Items').click(),
+ page.locator('button:has-text("OK")').click()
+ ]);
+
+ await page.click('.c-disclosure-triangle');
+
+ const ITERATIONS = 4;
+ await createNotebookEntryAndTags(page, ITERATIONS);
+
+ for (let iteration = 0; iteration < ITERATIONS; iteration++) {
+ const entryLocator = `[aria-label="Notebook Entry"] >> nth = ${iteration}`;
+ await expect(page.locator(entryLocator)).toContainText("Science");
+ await expect(page.locator(entryLocator)).toContainText("Driving");
+ }
+
+ // Click Unnamed Clock
+ await page.click('text="Unnamed Clock"');
+
+ // Click Unnamed Notebook
+ await page.click('text="Unnamed Notebook"');
+
+ for (let iteration = 0; iteration < ITERATIONS; iteration++) {
+ const entryLocator = `[aria-label="Notebook Entry"] >> nth = ${iteration}`;
+ await expect(page.locator(entryLocator)).toContainText("Science");
+ await expect(page.locator(entryLocator)).toContainText("Driving");
+ }
+
+ //Reload Page
+ await Promise.all([
+ page.reload(),
+ page.waitForLoadState('networkidle')
+ ]);
+
+ // Click Unnamed Notebook
+ await page.click('text="Unnamed Notebook"');
+
+ for (let iteration = 0; iteration < ITERATIONS; iteration++) {
+ const entryLocator = `[aria-label="Notebook Entry"] >> nth = ${iteration}`;
+ await expect(page.locator(entryLocator)).toContainText("Science");
+ await expect(page.locator(entryLocator)).toContainText("Driving");
+ }
+
+ });
+});
diff --git a/e2e/tests/plugins/plot/autoscale.e2e.spec.js b/e2e/tests/plugins/plot/autoscale.e2e.spec.js
new file mode 100644
index 000000000..ee9782ec4
--- /dev/null
+++ b/e2e/tests/plugins/plot/autoscale.e2e.spec.js
@@ -0,0 +1,185 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+/*
+Testsuite for plot autoscale.
+*/
+
+const { test } = require('../../../fixtures.js');
+const { expect } = require('@playwright/test');
+
+test.use({
+ viewport: {
+ width: 1280,
+ height: 720
+ }
+});
+
+test.describe('ExportAsJSON', () => {
+ test('User can set autoscale with a valid range @snapshot', async ({ page }) => {
+ //This is necessary due to the size of the test suite.
+ test.slow();
+
+ await page.goto('/', { waitUntil: 'networkidle' });
+
+ await setTimeRange(page);
+
+ await createSinewaveOverlayPlot(page);
+
+ await testYTicks(page, ['-1.00', '-0.50', '0.00', '0.50', '1.00']);
+
+ await turnOffAutoscale(page);
+
+ // Make sure that after turning off autoscale, the user selected range values start at the same values the plot had prior.
+ await testYTicks(page, ['-1.00', '-0.50', '0.00', '0.50', '1.00']);
+
+ const canvas = page.locator('canvas').nth(1);
+
+ await canvas.hover({trial: true});
+
+ expect(await canvas.screenshot()).toMatchSnapshot('autoscale-canvas-prepan');
+
+ //Alt Drag Start
+ await page.keyboard.down('Alt');
+
+ await canvas.dragTo(canvas, {
+ sourcePosition: {
+ x: 200,
+ y: 200
+ },
+ targetPosition: {
+ x: 400,
+ y: 400
+ }
+ });
+
+ //Alt Drag End
+ await page.keyboard.up('Alt');
+
+ // Ensure the drag worked.
+ await testYTicks(page, ['0.00', '0.50', '1.00', '1.50', '2.00']);
+
+ await canvas.hover({trial: true});
+
+ expect(await canvas.screenshot()).toMatchSnapshot('autoscale-canvas-panned');
+ });
+});
+
+/**
+ * @param {import('@playwright/test').Page} page
+ * @param {string} start
+ * @param {string} end
+ */
+async function setTimeRange(page, start = '2022-03-29 22:00:00.000Z', end = '2022-03-29 22:00:30.000Z') {
+ // Set a specific time range for consistency, otherwise it will change
+ // on every test to a range based on the current time.
+
+ const timeInputs = page.locator('input.c-input--datetime');
+ await timeInputs.first().click();
+ await timeInputs.first().fill(start);
+
+ await timeInputs.nth(1).click();
+ await timeInputs.nth(1).fill(end);
+}
+
+/**
+ * @param {import('@playwright/test').Page} page
+ */
+async function createSinewaveOverlayPlot(page) {
+ // click create button
+ await page.locator('button:has-text("Create")').click();
+
+ // add overlay plot with defaults
+ await page.locator('li:has-text("Overlay Plot")').click();
+ await Promise.all([
+ page.waitForNavigation(),
+ page.locator('text=OK').click(),
+ //Wait for Save Banner to appear1
+ page.waitForSelector('.c-message-banner__message')
+ ]);
+ //Wait until Save Banner is gone
+ await page.locator('.c-message-banner__close-button').click();
+ await page.waitForSelector('.c-message-banner__message', { state: 'detached'});
+
+ // save (exit edit mode)
+ await page.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button').nth(1).click();
+ await page.locator('text=Save and Finish Editing').click();
+
+ // click create button
+ await page.locator('button:has-text("Create")').click();
+
+ // add sine wave generator with defaults
+ await page.locator('li:has-text("Sine Wave Generator")').click();
+ await Promise.all([
+ page.waitForNavigation(),
+ page.locator('text=OK').click(),
+ //Wait for Save Banner to appear1
+ page.waitForSelector('.c-message-banner__message')
+ ]);
+ //Wait until Save Banner is gone
+ await page.locator('.c-message-banner__close-button').click();
+ await page.waitForSelector('.c-message-banner__message', { state: 'detached'});
+
+ // focus the overlay plot
+ await page.locator('text=Open MCT My Items >> span').nth(3).click();
+ await Promise.all([
+ page.waitForNavigation(),
+ page.locator('text=Unnamed Overlay Plot').first().click()
+ ]);
+}
+
+/**
+ * @param {import('@playwright/test').Page} page
+ */
+async function turnOffAutoscale(page) {
+ // enter edit mode
+ await page.locator('text=Unnamed Overlay Plot Snapshot >> button').nth(3).click();
+
+ // uncheck autoscale
+ await page.locator('text=Y Axis Label Log mode Auto scale Padding >> input[type="checkbox"] >> nth=1').uncheck();
+
+ // save
+ await page.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button').nth(1).click();
+ await Promise.all([
+ page.locator('text=Save and Finish Editing').click(),
+ //Wait for Save Banner to appear
+ page.waitForSelector('.c-message-banner__message')
+ ]);
+ //Wait until Save Banner is gone
+ await page.locator('.c-message-banner__close-button').click();
+ await page.waitForSelector('.c-message-banner__message', { state: 'detached'});
+}
+
+/**
+ * @param {import('@playwright/test').Page} page
+ */
+async function testYTicks(page, values) {
+ const yTicks = page.locator('.gl-plot-y-tick-label');
+ await page.locator('canvas >> nth=1').hover();
+ let promises = [yTicks.count().then(c => expect(c).toBe(values.length))];
+
+ for (let i = 0, l = values.length; i < l; i += 1) {
+ promises.push(expect(yTicks.nth(i)).toHaveText(values[i])); // eslint-disable-line
+ }
+
+ await Promise.all(promises);
+}
diff --git a/e2e/tests/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-panned-chrome-darwin b/e2e/tests/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-panned-chrome-darwin
new file mode 100644
index 000000000..bea4d7c40
--- /dev/null
+++ b/e2e/tests/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-panned-chrome-darwin
Binary files differ
diff --git a/e2e/tests/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-panned-chrome-linux b/e2e/tests/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-panned-chrome-linux
new file mode 100644
index 000000000..345901fcc
--- /dev/null
+++ b/e2e/tests/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-panned-chrome-linux
Binary files differ
diff --git a/e2e/tests/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-prepan-chrome-darwin b/e2e/tests/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-prepan-chrome-darwin
new file mode 100644
index 000000000..fc3db7c14
--- /dev/null
+++ b/e2e/tests/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-prepan-chrome-darwin
Binary files differ
diff --git a/e2e/tests/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-prepan-chrome-linux b/e2e/tests/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-prepan-chrome-linux
new file mode 100644
index 000000000..88e71e789
--- /dev/null
+++ b/e2e/tests/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-prepan-chrome-linux
Binary files differ
diff --git a/e2e/tests/plugins/plot/logPlot.e2e.spec.js b/e2e/tests/plugins/plot/logPlot.e2e.spec.js
new file mode 100644
index 000000000..798109a5d
--- /dev/null
+++ b/e2e/tests/plugins/plot/logPlot.e2e.spec.js
@@ -0,0 +1,298 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+/*
+Tests to verify log plot functionality. Note this test suite if very much under active development and should not
+necessarily be used for reference when writing new tests in this area.
+*/
+
+const { test } = require('../../../fixtures.js');
+const { expect } = require('@playwright/test');
+
+test.describe('Log plot tests', () => {
+ test('Log Plot ticks are functionally correct in regular and log mode and after refresh', async ({ page }) => {
+ //Test.slow decorator is currently broken. Needs to be fixed in https://github.com/nasa/openmct/issues/5374
+ test.slow();
+
+ await makeOverlayPlot(page);
+ await testRegularTicks(page);
+ await enableEditMode(page);
+ await enableLogMode(page);
+ await testLogTicks(page);
+ await disableLogMode(page);
+ await testRegularTicks(page);
+ await enableLogMode(page);
+ await testLogTicks(page);
+ await saveOverlayPlot(page);
+ await testLogTicks(page);
+ });
+
+ // Leaving test as 'TODO' for now.
+ // NOTE: Not eligible for community contributions.
+ test.fixme('Verify that log mode option is reflected in import/export JSON', async ({ page }) => {
+ await makeOverlayPlot(page);
+ await enableEditMode(page);
+ await enableLogMode(page);
+ await saveOverlayPlot(page);
+
+ // TODO ...export, delete the overlay, then import it...
+
+ //await testLogTicks(page);
+
+ // TODO, the plot is slightly at different position that in the other test, so this fails.
+ // ...We can fix it by copying all steps from the first test...
+ // await testLogPlotPixels(page);
+ });
+});
+
+/**
+ * Makes an overlay plot with a sine wave generator and clicks on the overlay plot in the sidebar so it is the active thing displayed.
+ * @param {import('@playwright/test').Page} page
+ */
+async function makeOverlayPlot(page) {
+ // fresh page with time range from 2022-03-29 22:00:00.000Z to 2022-03-29 22:00:30.000Z
+ await page.goto('/', { waitUntil: 'networkidle' });
+
+ // Set a specific time range for consistency, otherwise it will change
+ // on every test to a range based on the current time.
+
+ const timeInputs = page.locator('input.c-input--datetime');
+ await timeInputs.first().click();
+ await timeInputs.first().fill('2022-03-29 22:00:00.000Z');
+
+ await timeInputs.nth(1).click();
+ await timeInputs.nth(1).fill('2022-03-29 22:00:30.000Z');
+
+ // create overlay plot
+
+ await page.locator('button.c-create-button').click();
+ await page.locator('li:has-text("Overlay Plot")').click();
+ await Promise.all([
+ page.waitForNavigation({ waitUntil: 'networkidle'}),
+ page.locator('text=OK').click(),
+ //Wait for Save Banner to appear
+ page.waitForSelector('.c-message-banner__message')
+ ]);
+ //Wait until Save Banner is gone
+ await page.locator('.c-message-banner__close-button').click();
+ await page.waitForSelector('.c-message-banner__message', { state: 'detached'});
+
+ // save the overlay plot
+
+ await saveOverlayPlot(page);
+
+ // create a sinewave generator
+
+ await page.locator('button.c-create-button').click();
+ await page.locator('li:has-text("Sine Wave Generator")').click();
+
+ // set amplitude to 6, offset 4, period 2
+
+ await page.locator('div:nth-child(5) .c-form-row__controls .form-control .field input').click();
+ await page.locator('div:nth-child(5) .c-form-row__controls .form-control .field input').fill('6');
+
+ await page.locator('div:nth-child(6) .c-form-row__controls .form-control .field input').click();
+ await page.locator('div:nth-child(6) .c-form-row__controls .form-control .field input').fill('4');
+
+ await page.locator('div:nth-child(7) .c-form-row__controls .form-control .field input').click();
+ await page.locator('div:nth-child(7) .c-form-row__controls .form-control .field input').fill('2');
+
+ // Click OK to make generator
+
+ await Promise.all([
+ page.waitForNavigation({ waitUntil: 'networkidle'}),
+ page.locator('text=OK').click(),
+ //Wait for Save Banner to appear
+ page.waitForSelector('.c-message-banner__message')
+ ]);
+ //Wait until Save Banner is gone
+ await page.locator('.c-message-banner__close-button').click();
+ await page.waitForSelector('.c-message-banner__message', { state: 'detached'});
+
+ // click on overlay plot
+
+ await page.locator('text=Open MCT My Items >> span').nth(3).click();
+ await Promise.all([
+ page.waitForNavigation(),
+ page.locator('text=Unnamed Overlay Plot').first().click()
+ ]);
+}
+
+/**
+ * @param {import('@playwright/test').Page} page
+ */
+async function testRegularTicks(page) {
+ const yTicks = await page.locator('.gl-plot-y-tick-label');
+ expect(await yTicks.count()).toBe(7);
+ await expect(yTicks.nth(0)).toHaveText('-2');
+ await expect(yTicks.nth(1)).toHaveText('0');
+ await expect(yTicks.nth(2)).toHaveText('2');
+ await expect(yTicks.nth(3)).toHaveText('4');
+ await expect(yTicks.nth(4)).toHaveText('6');
+ await expect(yTicks.nth(5)).toHaveText('8');
+ await expect(yTicks.nth(6)).toHaveText('10');
+}
+
+/**
+ * @param {import('@playwright/test').Page} page
+ */
+async function testLogTicks(page) {
+ const yTicks = await page.locator('.gl-plot-y-tick-label');
+ expect(await yTicks.count()).toBe(28);
+ await expect(yTicks.nth(0)).toHaveText('-2.98');
+ await expect(yTicks.nth(1)).toHaveText('-2.50');
+ await expect(yTicks.nth(2)).toHaveText('-2.00');
+ await expect(yTicks.nth(3)).toHaveText('-1.51');
+ await expect(yTicks.nth(4)).toHaveText('-1.20');
+ await expect(yTicks.nth(5)).toHaveText('-1.00');
+ await expect(yTicks.nth(6)).toHaveText('-0.80');
+ await expect(yTicks.nth(7)).toHaveText('-0.58');
+ await expect(yTicks.nth(8)).toHaveText('-0.40');
+ await expect(yTicks.nth(9)).toHaveText('-0.20');
+ await expect(yTicks.nth(10)).toHaveText('-0.00');
+ await expect(yTicks.nth(11)).toHaveText('0.20');
+ await expect(yTicks.nth(12)).toHaveText('0.40');
+ await expect(yTicks.nth(13)).toHaveText('0.58');
+ await expect(yTicks.nth(14)).toHaveText('0.80');
+ await expect(yTicks.nth(15)).toHaveText('1.00');
+ await expect(yTicks.nth(16)).toHaveText('1.20');
+ await expect(yTicks.nth(17)).toHaveText('1.51');
+ await expect(yTicks.nth(18)).toHaveText('2.00');
+ await expect(yTicks.nth(19)).toHaveText('2.50');
+ await expect(yTicks.nth(20)).toHaveText('2.98');
+ await expect(yTicks.nth(21)).toHaveText('3.50');
+ await expect(yTicks.nth(22)).toHaveText('4.00');
+ await expect(yTicks.nth(23)).toHaveText('4.50');
+ await expect(yTicks.nth(24)).toHaveText('5.31');
+ await expect(yTicks.nth(25)).toHaveText('7.00');
+ await expect(yTicks.nth(26)).toHaveText('8.00');
+ await expect(yTicks.nth(27)).toHaveText('9.00');
+}
+
+/**
+ * @param {import('@playwright/test').Page} page
+ */
+async function enableEditMode(page) {
+ // turn on edit mode
+ await page.locator('text=Unnamed Overlay Plot Snapshot >> button').nth(3).click();
+ await expect(await page.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button').nth(1)).toBeVisible();
+}
+
+/**
+ * @param {import('@playwright/test').Page} page
+ */
+async function enableLogMode(page) {
+ // turn on log mode
+ await page.locator('text=Y Axis Label Log mode Auto scale Padding >> input[type="checkbox"]').first().check();
+}
+
+/**
+ * @param {import('@playwright/test').Page} page
+ */
+async function disableLogMode(page) {
+ // turn off log mode
+ await page.locator('text=Y Axis Label Log mode Auto scale Padding >> input[type="checkbox"]').first().uncheck();
+}
+
+/**
+ * @param {import('@playwright/test').Page} page
+ */
+async function saveOverlayPlot(page) {
+ // save overlay plot
+ await page.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button').nth(1).click();
+
+ await Promise.all([
+ page.locator('text=Save and Finish Editing').click(),
+ //Wait for Save Banner to appear
+ page.waitForSelector('.c-message-banner__message')
+ ]);
+ //Wait until Save Banner is gone
+ await page.locator('.c-message-banner__close-button').click();
+ await page.waitForSelector('.c-message-banner__message', { state: 'detached' });
+}
+
+/**
+ * @param {import('@playwright/test').Page} page
+ */
+// FIXME: Remove this eslint exception once implemented
+// eslint-disable-next-line no-unused-vars
+async function testLogPlotPixels(page) {
+ const pixelsMatch = await page.evaluate(async () => {
+ // TODO get canvas pixels at a few locations to make sure they're the correct color, to test that the plot comes out as expected.
+
+ await new Promise((r) => setTimeout(r, 5 * 1000));
+
+ // These are some pixels that should be blue points in the log plot.
+ // If the plot changes shape to an unexpected shape, this will
+ // likely fail, which is what we want.
+ //
+ // I found these pixels by pausing playwright in debug mode at this
+ // point, and using similar code as below to output the pixel data, then
+ // I logged those pixels here.
+ const expectedBluePixels = [
+ // TODO these pixel sets only work with the first test, but not the second test.
+
+ // [60, 35],
+ // [121, 125],
+ // [156, 377],
+ // [264, 73],
+ // [372, 186],
+ // [576, 73],
+ // [659, 439],
+ // [675, 423]
+
+ [60, 35],
+ [120, 125],
+ [156, 375],
+ [264, 73],
+ [372, 185],
+ [575, 72],
+ [659, 437],
+ [675, 421]
+ ];
+
+ // The first canvas in the DOM is the one that has the plot point
+ // icons (canvas 2d), which is the one we are testing. The second
+ // one in the DOM is the WebGL canvas with the line. (Why aren't
+ // they both WebGL?)
+ const canvas = document.querySelector('canvas');
+
+ const ctx = canvas.getContext('2d');
+
+ for (const pixel of expectedBluePixels) {
+ // XXX Possible optimization: call getImageData only once with
+ // area including all pixels to be tested.
+ const data = ctx.getImageData(pixel[0], pixel[1], 1, 1).data;
+
+ // #43b0ffff <-- openmct cyanish-blue with 100% opacity
+ // if (data[0] !== 0x43 || data[1] !== 0xb0 || data[2] !== 0xff || data[3] !== 0xff) {
+ if (data[0] === 0 && data[1] === 0 && data[2] === 0 && data[3] === 0) {
+ // If any pixel is empty, it means we didn't hit a plot point.
+ return false;
+ }
+ }
+
+ return true;
+ });
+
+ expect(pixelsMatch).toBe(true);
+}
diff --git a/e2e/tests/plugins/plot/missingPlotObj.e2e.spec.js b/e2e/tests/plugins/plot/missingPlotObj.e2e.spec.js
new file mode 100644
index 000000000..a10f990c5
--- /dev/null
+++ b/e2e/tests/plugins/plot/missingPlotObj.e2e.spec.js
@@ -0,0 +1,155 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+/*
+Tests to verify log plot functionality when objects are missing
+*/
+
+const { test } = require('../../../fixtures.js');
+const { expect } = require('@playwright/test');
+
+test.describe('Handle missing object for plots', () => {
+ test('Displays empty div for missing stacked plot item', async ({ page, browserName }) => {
+ test.fixme(browserName === 'firefox', 'Firefox failing due to console events being missed');
+ const errorLogs = [];
+
+ page.on("console", (message) => {
+ if (message.type() === 'warning' && message.text().includes('Missing domain object')) {
+ errorLogs.push(message.text());
+ }
+ });
+
+ //Make stacked plot
+ await makeStackedPlot(page);
+
+ //Gets local storage and deletes the last sine wave generator in the stacked plot
+ const localStorage = await page.evaluate(() => window.localStorage);
+ const parsedData = JSON.parse(localStorage.mct);
+ const keys = Object.keys(parsedData);
+ const lastKey = keys[keys.length - 1];
+
+ delete parsedData[lastKey];
+
+ //Sets local storage with missing object
+ await page.evaluate(
+ `window.localStorage.setItem('mct', '${JSON.stringify(parsedData)}')`
+ );
+
+ //Reloads page and clicks on stacked plot
+ await Promise.all([
+ page.reload(),
+ page.waitForLoadState('networkidle')
+ ]);
+
+ //Verify Main section is there on load
+ await expect.soft(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Stacked Plot');
+
+ await page.locator('text=Open MCT My Items >> span').nth(3).click();
+ await Promise.all([
+ page.waitForNavigation(),
+ page.locator('text=Unnamed Stacked Plot').first().click()
+ ]);
+
+ //Check that there is only one stacked item plot with a plot, the missing one will be empty
+ await expect(page.locator(".c-plot--stacked-container:has(.gl-plot)")).toHaveCount(1);
+ //Verify that console.warn is thrown
+ expect(errorLogs).toHaveLength(1);
+ });
+});
+
+/**
+ * This is used the create a stacked plot object
+ * @private
+ */
+async function makeStackedPlot(page) {
+ // fresh page with time range from 2022-03-29 22:00:00.000Z to 2022-03-29 22:00:30.000Z
+ await page.goto('/', { waitUntil: 'networkidle' });
+
+ // create stacked plot
+ await page.locator('button.c-create-button').click();
+ await page.locator('li:has-text("Stacked Plot")').click();
+
+ await Promise.all([
+ page.waitForNavigation({ waitUntil: 'networkidle'}),
+ page.locator('text=OK').click(),
+ //Wait for Save Banner to appear
+ page.waitForSelector('.c-message-banner__message')
+ ]);
+
+ // save the stacked plot
+ await saveStackedPlot(page);
+
+ // create a sinewave generator
+ await createSineWaveGenerator(page);
+
+ // click on stacked plot
+ await page.locator('text=Open MCT My Items >> span').nth(3).click();
+ await Promise.all([
+ page.waitForNavigation(),
+ page.locator('text=Unnamed Stacked Plot').first().click()
+ ]);
+
+ // create a second sinewave generator
+ await createSineWaveGenerator(page);
+
+ // click on stacked plot
+ await page.locator('text=Open MCT My Items >> span').nth(3).click();
+ await Promise.all([
+ page.waitForNavigation(),
+ page.locator('text=Unnamed Stacked Plot').first().click()
+ ]);
+}
+
+/**
+ * This is used to save a stacked plot object
+ * @private
+ */
+async function saveStackedPlot(page) {
+ // save stacked plot
+ await page.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button').nth(1).click();
+
+ await Promise.all([
+ page.locator('text=Save and Finish Editing').click(),
+ //Wait for Save Banner to appear
+ page.waitForSelector('.c-message-banner__message')
+ ]);
+ //Wait until Save Banner is gone
+ await page.locator('.c-message-banner__close-button').click();
+ await page.waitForSelector('.c-message-banner__message', { state: 'detached' });
+}
+
+/**
+ * This is used to create a sine wave generator object
+ * @private
+ */
+async function createSineWaveGenerator(page) {
+ //Create sine wave generator
+ await page.locator('button.c-create-button').click();
+ await page.locator('li:has-text("Sine Wave Generator")').click();
+
+ await Promise.all([
+ page.waitForNavigation({ waitUntil: 'networkidle'}),
+ page.locator('text=OK').click(),
+ //Wait for Save Banner to appear
+ page.waitForSelector('.c-message-banner__message')
+ ]);
+}
diff --git a/platform/commonUI/general/test/filters/ReverseFilterSpec.js b/e2e/tests/plugins/remoteClock/remoteClock.e2e.spec.js
index 8a2f51560..f9167d9e4 100644
--- a/platform/commonUI/general/test/filters/ReverseFilterSpec.js
+++ b/e2e/tests/plugins/remoteClock/remoteClock.e2e.spec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -20,24 +20,22 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
-define(
- ['../../src/filters/ReverseFilter'],
- function (ReverseFilter) {
+const { test } = require('../../../fixtures.js');
+// eslint-disable-next-line no-unused-vars
+const { expect } = require('@playwright/test');
- describe("The reverse filter", function () {
- var reverse;
-
- beforeEach(function () {
- reverse = new ReverseFilter();
- });
-
- it("reverses text", function () {
- expect(reverse('foo')).toEqual('oof');
- });
-
- it("returns undefined for undefined inputs", function () {
- expect(reverse(undefined)).toBeUndefined();
- });
+test.describe('Remote Clock', () => {
+ // eslint-disable-next-line require-await
+ test.fixme('blocks historical requests until first tick is received', async ({ page }) => {
+ test.info().annotations.push({
+ type: 'issue',
+ description: 'https://github.com/nasa/openmct/issues/5221'
});
- }
-);
+ // addInitScript to with remote clock
+ // Switch time conductor mode to 'remote clock'
+ // Navigate to telemetry
+ // Verify that the plot renders historical data within the correct bounds
+ // Refresh the page
+ // Verify again that the plot renders historical data within the correct bounds
+ });
+});
diff --git a/e2e/tests/plugins/telemetryTable/telemetryTable.e2e.spec.js b/e2e/tests/plugins/telemetryTable/telemetryTable.e2e.spec.js
new file mode 100644
index 000000000..192c11628
--- /dev/null
+++ b/e2e/tests/plugins/telemetryTable/telemetryTable.e2e.spec.js
@@ -0,0 +1,104 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+const { test } = require('../../../fixtures');
+const { expect } = require('@playwright/test');
+
+test.describe('Telemetry Table', () => {
+ test('unpauses and filters data when paused by button and user changes bounds', async ({ page }) => {
+ test.info().annotations.push({
+ type: 'issue',
+ description: 'https://github.com/nasa/openmct/issues/5113'
+ });
+
+ const bannerMessage = '.c-message-banner__message';
+ const createButton = 'button:has-text("Create")';
+
+ await page.goto('/', { waitUntil: 'networkidle' });
+
+ // Click create button
+ await page.locator(createButton).click();
+ await page.locator('li:has-text("Telemetry Table")').click();
+
+ await Promise.all([
+ page.waitForNavigation(),
+ page.locator('text=OK').click(),
+ // Wait for Save Banner to appear
+ page.waitForSelector(bannerMessage)
+ ]);
+
+ // Save (exit edit mode)
+ await page.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button').nth(3).click();
+ await page.locator('text=Save and Finish Editing').click();
+
+ // Click create button
+ await page.locator(createButton).click();
+
+ // add Sine Wave Generator with defaults
+ await page.locator('li:has-text("Sine Wave Generator")').click();
+
+ await Promise.all([
+ page.waitForNavigation(),
+ page.locator('text=OK').click(),
+ // Wait for Save Banner to appear
+ page.waitForSelector(bannerMessage)
+ ]);
+
+ // focus the Telemetry Table
+ await page.locator('text=Open MCT My Items >> span').nth(3).click();
+ await Promise.all([
+ page.waitForNavigation(),
+ page.locator('text=Unnamed Telemetry Table').first().click()
+ ]);
+
+ // Click pause button
+ const pauseButton = page.locator('button.c-button.icon-pause');
+ await pauseButton.click();
+
+ const tableWrapper = page.locator('div.c-table-wrapper');
+ await expect(tableWrapper).toHaveClass(/is-paused/);
+
+ // Subtract 5 minutes from the current end bound datetime and set it
+ const endTimeInput = page.locator('input[type="text"].c-input--datetime').nth(1);
+ await endTimeInput.click();
+
+ let endDate = await endTimeInput.inputValue();
+ endDate = new Date(endDate);
+
+ endDate.setUTCMinutes(endDate.getUTCMinutes() - 5);
+ endDate = endDate.toISOString().replace(/T/, ' ');
+
+ await endTimeInput.fill('');
+ await endTimeInput.fill(endDate);
+ await page.keyboard.press('Enter');
+
+ await expect(tableWrapper).not.toHaveClass(/is-paused/);
+
+ // Get the most recent telemetry date
+ const latestTelemetryDate = await page.locator('table.c-telemetry-table__body > tbody > tr').last().locator('td').nth(1).getAttribute('title');
+
+ // Verify that it is <= our new end bound
+ const latestMilliseconds = Date.parse(latestTelemetryDate);
+ const endBoundMilliseconds = Date.parse(endDate);
+ expect(latestMilliseconds).toBeLessThanOrEqual(endBoundMilliseconds);
+ });
+});
diff --git a/e2e/tests/plugins/timeConductor/timeConductor.e2e.spec.js b/e2e/tests/plugins/timeConductor/timeConductor.e2e.spec.js
new file mode 100644
index 000000000..ed759a53d
--- /dev/null
+++ b/e2e/tests/plugins/timeConductor/timeConductor.e2e.spec.js
@@ -0,0 +1,235 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+const { test } = require('../../../fixtures.js');
+const { expect } = require('@playwright/test');
+
+test.describe('Time conductor operations', () => {
+ test('validate start time does not exceeds end time', async ({ page }) => {
+ // Go to baseURL
+ await page.goto('/', { waitUntil: 'networkidle' });
+ const year = new Date().getFullYear();
+
+ let startDate = 'xxxx-01-01 01:00:00.000Z';
+ startDate = year + startDate.substring(4);
+
+ let endDate = 'xxxx-01-01 02:00:00.000Z';
+ endDate = year + endDate.substring(4);
+
+ const startTimeLocator = page.locator('input[type="text"]').first();
+ const endTimeLocator = page.locator('input[type="text"]').nth(1);
+
+ // Click start time
+ await startTimeLocator.click();
+
+ // Click end time
+ await endTimeLocator.click();
+
+ await endTimeLocator.fill(endDate.toString());
+ await startTimeLocator.fill(startDate.toString());
+
+ // invalid start date
+ startDate = (year + 1) + startDate.substring(4);
+ await startTimeLocator.fill(startDate.toString());
+ await endTimeLocator.click();
+
+ const startDateValidityStatus = await startTimeLocator.evaluate((element) => element.checkValidity());
+ expect(startDateValidityStatus).not.toBeTruthy();
+
+ // fix to valid start date
+ startDate = (year - 1) + startDate.substring(4);
+ await startTimeLocator.fill(startDate.toString());
+
+ // invalid end date
+ endDate = (year - 2) + endDate.substring(4);
+ await endTimeLocator.fill(endDate.toString());
+ await startTimeLocator.click();
+
+ const endDateValidityStatus = await endTimeLocator.evaluate((element) => element.checkValidity());
+ expect(endDateValidityStatus).not.toBeTruthy();
+ });
+});
+
+// Testing instructions:
+// Try to change the realtime offsets when in realtime (local clock) mode.
+test.describe('Time conductor input fields real-time mode', () => {
+ test('validate input fields in real-time mode', async ({ page }) => {
+ const startOffset = {
+ secs: '23'
+ };
+
+ const endOffset = {
+ secs: '31'
+ };
+
+ // Go to baseURL
+ await page.goto('/', { waitUntil: 'networkidle' });
+
+ // Switch to real-time mode
+ await setRealTimeMode(page);
+
+ // Set start time offset
+ await setStartOffset(page, startOffset);
+
+ // Verify time was updated on time offset button
+ await expect(page.locator('data-testid=conductor-start-offset-button')).toContainText('00:30:23');
+
+ // Set end time offset
+ await setEndOffset(page, endOffset);
+
+ // Verify time was updated on preceding time offset button
+ await expect(page.locator('data-testid=conductor-end-offset-button')).toContainText('00:00:31');
+ });
+
+ /**
+ * Verify that offsets and url params are preserved when switching
+ * between fixed timespan and real-time mode.
+ */
+ test('preserve offsets and url params when switching between fixed and real-time mode', async ({ page }) => {
+ const startOffset = {
+ mins: '30',
+ secs: '23'
+ };
+
+ const endOffset = {
+ secs: '01'
+ };
+
+ // Convert offsets to milliseconds
+ const startDelta = (30 * 60 * 1000) + (23 * 1000);
+ const endDelta = (1 * 1000);
+
+ // Go to baseURL
+ await page.goto('/', { waitUntil: 'networkidle' });
+
+ // Switch to real-time mode
+ await setRealTimeMode(page);
+
+ // Set start time offset
+ await setStartOffset(page, startOffset);
+
+ // Set end time offset
+ await setEndOffset(page, endOffset);
+
+ // Switch to fixed timespan mode
+ await setFixedTimeMode(page);
+
+ // Switch back to real-time mode
+ await setRealTimeMode(page);
+
+ // Verify updated start time offset persists after mode switch
+ await expect(page.locator('data-testid=conductor-start-offset-button')).toContainText('00:30:23');
+
+ // Verify updated end time offset persists after mode switch
+ await expect(page.locator('data-testid=conductor-end-offset-button')).toContainText('00:00:01');
+
+ // Verify url parameters persist after mode switch
+ await page.waitForNavigation();
+ expect(page.url()).toContain(`startDelta=${startDelta}`);
+ expect(page.url()).toContain(`endDelta=${endDelta}`);
+ });
+});
+
+/**
+ * @typedef {Object} OffsetValues
+ * @property {string | undefined} hours
+ * @property {string | undefined} mins
+ * @property {string | undefined} secs
+ */
+
+/**
+ * Set the values (hours, mins, secs) for the start time offset when in realtime mode
+ * @param {import('@playwright/test').Page} page
+ * @param {OffsetValues} offset
+ */
+async function setStartOffset(page, offset) {
+ const startOffsetButton = page.locator('data-testid=conductor-start-offset-button');
+ await setTimeConductorOffset(page, offset, startOffsetButton);
+}
+
+/**
+ * Set the values (hours, mins, secs) for the end time offset when in realtime mode
+ * @param {import('@playwright/test').Page} page
+ * @param {OffsetValues} offset
+ */
+async function setEndOffset(page, offset) {
+ const endOffsetButton = page.locator('data-testid=conductor-end-offset-button');
+ await setTimeConductorOffset(page, offset, endOffsetButton);
+}
+
+/**
+ * Set the time conductor to fixed timespan mode
+ * @param {import('@playwright/test').Page} page
+ */
+async function setFixedTimeMode(page) {
+ await setTimeConductorMode(page, true);
+}
+
+/**
+ * Set the time conductor to realtime mode
+ * @param {import('@playwright/test').Page} page
+ */
+async function setRealTimeMode(page) {
+ await setTimeConductorMode(page, false);
+}
+
+/**
+ * Set the values (hours, mins, secs) for the TimeConductor offsets when in realtime mode
+ * @param {import('@playwright/test').Page} page
+ * @param {OffsetValues} offset
+ * @param {import('@playwright/test').Locator} offsetButton
+ */
+async function setTimeConductorOffset(page, {hours, mins, secs}, offsetButton) {
+ await offsetButton.click();
+
+ if (hours) {
+ await page.fill('.pr-time-controls__hrs', hours);
+ }
+
+ if (mins) {
+ await page.fill('.pr-time-controls__mins', mins);
+ }
+
+ if (secs) {
+ await page.fill('.pr-time-controls__secs', secs);
+ }
+
+ // Click the check button
+ await page.locator('.icon-check').click();
+}
+
+/**
+ * Set the time conductor mode to either fixed timespan or realtime mode.
+ * @param {import('@playwright/test').Page} page
+ * @param {boolean} [isFixedTimespan=true] true for fixed timespan mode, false for realtime mode; default is true
+ */
+async function setTimeConductorMode(page, isFixedTimespan = true) {
+ // Click 'mode' button
+ await page.locator('.c-mode-button').click();
+
+ // Switch time conductor mode
+ if (isFixedTimespan) {
+ await page.locator('data-testid=conductor-modeOption-fixed').click();
+ } else {
+ await page.locator('data-testid=conductor-modeOption-realtime').click();
+ }
+}
diff --git a/e2e/tests/plugins/timer/timer.e2e.spec.js b/e2e/tests/plugins/timer/timer.e2e.spec.js
new file mode 100644
index 000000000..3c8a051a9
--- /dev/null
+++ b/e2e/tests/plugins/timer/timer.e2e.spec.js
@@ -0,0 +1,185 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+const { test } = require('../../../fixtures.js');
+const { expect } = require('@playwright/test');
+
+test.describe('Timer', () => {
+
+ test.beforeEach(async ({ page }) => {
+ //Go to baseURL
+ await page.goto('/', { waitUntil: 'networkidle' });
+
+ //Click the Create button
+ await page.click('button:has-text("Create")');
+
+ // Click 'Timer'
+ await page.click('text=Timer');
+
+ // Click text=OK
+ await Promise.all([
+ page.waitForNavigation({waitUntil: 'networkidle'}),
+ page.click('text=OK')
+ ]);
+
+ await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Timer');
+ });
+
+ test('Can perform actions on the Timer', async ({ page }) => {
+ test.info().annotations.push({
+ type: 'issue',
+ description: 'https://github.com/nasa/openmct/issues/4313'
+ });
+
+ await test.step("From the tree context menu", async () => {
+ await triggerTimerContextMenuAction(page, 'Start');
+ await triggerTimerContextMenuAction(page, 'Pause');
+ await triggerTimerContextMenuAction(page, 'Restart at 0');
+ await triggerTimerContextMenuAction(page, 'Stop');
+ });
+
+ await test.step("From the 3dot menu", async () => {
+ await triggerTimer3dotMenuAction(page, 'Start');
+ await triggerTimer3dotMenuAction(page, 'Pause');
+ await triggerTimer3dotMenuAction(page, 'Restart at 0');
+ await triggerTimer3dotMenuAction(page, 'Stop');
+ });
+
+ await test.step("From the object view", async () => {
+ await triggerTimerViewAction(page, 'Start');
+ await triggerTimerViewAction(page, 'Pause');
+ await triggerTimerViewAction(page, 'Restart at 0');
+ });
+ });
+});
+
+/**
+ * Actions that can be performed on a timer from context menus.
+ * @typedef {'Start' | 'Stop' | 'Pause' | 'Restart at 0'} TimerAction
+ */
+
+/**
+ * Actions that can be performed on a timer from the object view.
+ * @typedef {'Start' | 'Pause' | 'Restart at 0'} TimerViewAction
+ */
+
+/**
+ * Open the timer context menu from the object tree.
+ * Expands the 'My Items' folder if it is not already expanded.
+ * @param {import('@playwright/test').Page} page
+ */
+async function openTimerContextMenu(page) {
+ const myItemsFolder = page.locator('text=Open MCT My Items >> span').nth(3);
+ const className = await myItemsFolder.getAttribute('class');
+ if (!className.includes('c-disclosure-triangle--expanded')) {
+ await myItemsFolder.click();
+ }
+
+ await page.locator(`a:has-text("Unnamed Timer")`).click({
+ button: 'right'
+ });
+}
+
+/**
+ * Trigger a timer action from the tree context menu
+ * @param {import('@playwright/test').Page} page
+ * @param {TimerAction} action
+ */
+async function triggerTimerContextMenuAction(page, action) {
+ const menuAction = `.c-menu ul li >> text="${action}"`;
+ await openTimerContextMenu(page);
+ await page.locator(menuAction).click();
+ assertTimerStateAfterAction(page, action);
+}
+
+/**
+ * Trigger a timer action from the 3dot menu
+ * @param {import('@playwright/test').Page} page
+ * @param {TimerAction} action
+ */
+async function triggerTimer3dotMenuAction(page, action) {
+ const menuAction = `.c-menu ul li >> text="${action}"`;
+ const threeDotMenuButton = 'button[title="More options"]';
+ let isActionAvailable = false;
+ let iterations = 0;
+ // Dismiss/open the 3dot menu until the action is available
+ // or a maxiumum number of iterations is reached
+ while (!isActionAvailable && iterations <= 20) {
+ await page.click('.c-object-view');
+ await page.click(threeDotMenuButton);
+ isActionAvailable = await page.locator(menuAction).isVisible();
+ iterations++;
+ }
+
+ await page.locator(menuAction).click();
+ assertTimerStateAfterAction(page, action);
+}
+
+/**
+ * Trigger a timer action from the object view
+ * @param {import('@playwright/test').Page} page
+ * @param {TimerViewAction} action
+ */
+async function triggerTimerViewAction(page, action) {
+ await page.locator('.c-timer').hover({trial: true});
+ const buttonTitle = buttonTitleFromAction(action);
+ await page.click(`button[title="${buttonTitle}"]`);
+ assertTimerStateAfterAction(page, action);
+}
+
+/**
+ * Takes in a TimerViewAction and returns the button title
+ * @param {TimerViewAction} action
+ */
+function buttonTitleFromAction(action) {
+ switch (action) {
+ case 'Start':
+ return 'Start';
+ case 'Pause':
+ return 'Pause';
+ case 'Restart at 0':
+ return 'Reset';
+ }
+}
+
+/**
+ * Verify the timer state after a timer action has been performed.
+ * @param {import('@playwright/test').Page} page
+ * @param {TimerAction} action
+ */
+async function assertTimerStateAfterAction(page, action) {
+ let timerStateClass;
+ switch (action) {
+ case 'Start':
+ case 'Restart at 0':
+ timerStateClass = "is-started";
+ break;
+ case 'Stop':
+ timerStateClass = 'is-stopped';
+ break;
+ case 'Pause':
+ timerStateClass = 'is-paused';
+ break;
+ }
+
+ await expect.soft(page.locator('.c-timer')).toHaveClass(new RegExp(timerStateClass));
+}
diff --git a/e2e/tests/smoke.spec.js b/e2e/tests/smoke.e2e.spec.js
index 1ce574b1a..f8f87794f 100644
--- a/e2e/tests/smoke.spec.js
+++ b/e2e/tests/smoke.e2e.spec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -33,7 +33,8 @@ comfortable running this test during a live mission?" Avoid creating or deleting
Make no assumptions about the order that elements appear in the DOM.
*/
-const { test, expect } = require('@playwright/test');
+const { test } = require('../fixtures.js');
+const { expect } = require('@playwright/test');
test('Verify that the create button appears and that the Folder Domain Object is available for selection', async ({ page }) => {
@@ -44,6 +45,15 @@ test('Verify that the create button appears and that the Folder Domain Object is
await page.click('button:has-text("Create")');
// Verify that Create Folder appears in the dropdown
- const locator = page.locator(':nth-match(:text("Folder"), 2)');
- await expect(locator).toBeEnabled();
+ await expect(page.locator(':nth-match(:text("Folder"), 2)')).toBeEnabled();
+});
+
+test('Verify that My Items Tree appears @ipad', async ({ page }) => {
+ //Test.slow annotation is currently broken. Needs to be fixed in https://github.com/nasa/openmct/issues/5374
+ test.slow();
+ //Go to baseURL
+ await page.goto('/');
+
+ //My Items to be visible
+ await expect(page.locator('a:has-text("My Items")')).toBeEnabled();
});
diff --git a/e2e/tests/ui/layout/search/grandsearch.e2e.spec.js b/e2e/tests/ui/layout/search/grandsearch.e2e.spec.js
new file mode 100644
index 000000000..f67b33673
--- /dev/null
+++ b/e2e/tests/ui/layout/search/grandsearch.e2e.spec.js
@@ -0,0 +1,111 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+/*
+This test suite is dedicated to tests which verify search functionality.
+*/
+
+const { expect } = require('@playwright/test');
+const { test } = require('../../../../fixtures');
+
+/**
+ * Creates a notebook object and adds an entry.
+ * @param {import('@playwright/test').Page} page
+ */
+async function createClockAndDisplayLayout(page) {
+ //Go to baseURL
+ await page.goto('/', { waitUntil: 'networkidle' });
+
+ // Click button:has-text("Create")
+ await page.locator('button:has-text("Create")').click();
+ // Click li:has-text("Notebook")
+ await page.locator('li:has-text("Clock")').click();
+ // Click button:has-text("OK")
+ await Promise.all([
+ page.waitForNavigation(),
+ page.locator('button:has-text("OK")').click()
+ ]);
+
+ // Click a:has-text("My Items")
+ await Promise.all([
+ page.waitForNavigation(),
+ page.locator('a:has-text("My Items") >> nth=0').click()
+ ]);
+ // Click button:has-text("Create")
+ await page.locator('button:has-text("Create")').click();
+ // Click li:has-text("Notebook")
+ await page.locator('li:has-text("Display Layout")').click();
+ // Click button:has-text("OK")
+ await Promise.all([
+ page.waitForNavigation(),
+ page.locator('button:has-text("OK")').click()
+ ]);
+}
+
+test.describe('Grand Search', () => {
+ test('Can search for objects, and subsequent search dropdown behaves properly', async ({ page }) => {
+ await createClockAndDisplayLayout(page);
+
+ // Click [aria-label="OpenMCT Search"] input[type="search"]
+ await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
+ // Fill [aria-label="OpenMCT Search"] input[type="search"]
+ await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Cl');
+ await expect(page.locator('[aria-label="Search Result"]')).toContainText('Clock');
+ // Click text=Elements >> nth=0
+ await page.locator('text=Elements').first().click();
+ await expect(page.locator('[aria-label="Search Result"]')).not.toBeVisible();
+
+ // Click [aria-label="OpenMCT Search"] [aria-label="Search Input"]
+ await page.locator('[aria-label="OpenMCT Search"] [aria-label="Search Input"]').click();
+ // Click [aria-label="Unnamed Clock clock result"] >> text=Unnamed Clock
+ await page.locator('[aria-label="Unnamed Clock clock result"] >> text=Unnamed Clock').click();
+ await expect(page.locator('.js-preview-window')).toBeVisible();
+
+ // Click [aria-label="Close"]
+ await page.locator('[aria-label="Close"]').click();
+ await expect(page.locator('[aria-label="Search Result"]')).toBeVisible();
+ await expect(page.locator('[aria-label="Search Result"]')).toContainText('Cloc');
+
+ // Click [aria-label="OpenMCT Search"] a >> nth=0
+ await page.locator('[aria-label="OpenMCT Search"] a').first().click();
+ await expect(page.locator('[aria-label="Search Result"]')).not.toBeVisible();
+
+ // Fill [aria-label="OpenMCT Search"] input[type="search"]
+ await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('foo');
+ await expect(page.locator('[aria-label="Search Result"]')).not.toBeVisible();
+
+ // Click text=Snapshot Save and Finish Editing Save and Continue Editing >> button >> nth=1
+ await page.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button').nth(1).click();
+ // Click text=Save and Finish Editing
+ await page.locator('text=Save and Finish Editing').click();
+ // Click [aria-label="OpenMCT Search"] [aria-label="Search Input"]
+ await page.locator('[aria-label="OpenMCT Search"] [aria-label="Search Input"]').click();
+ // Fill [aria-label="OpenMCT Search"] [aria-label="Search Input"]
+ await page.locator('[aria-label="OpenMCT Search"] [aria-label="Search Input"]').fill('Cl');
+ // Click text=Unnamed Clock
+ await Promise.all([
+ page.waitForNavigation(),
+ page.locator('text=Unnamed Clock').click()
+ ]);
+ await expect(page.locator('.is-object-type-clock')).toBeVisible();
+ });
+});
diff --git a/e2e/tests/visual/addInit.visual.spec.js b/e2e/tests/visual/addInit.visual.spec.js
new file mode 100644
index 000000000..c7f142c24
--- /dev/null
+++ b/e2e/tests/visual/addInit.visual.spec.js
@@ -0,0 +1,76 @@
+/* eslint-disable no-undef */
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+/*
+Collection of Visual Tests set to run with modified init scripts to inject plugins not otherwise available in the default contexts.
+
+These should only use functional expect statements to verify assumptions about the state
+in a test and not for functional verification of correctness. Visual tests are not supposed
+to "fail" on assertions. Instead, they should be used to detect changes between builds or branches.
+
+Note: Larger testsuite sizes are OK due to the setup time associated with these tests.
+*/
+
+const { test } = require('@playwright/test');
+const percySnapshot = require('@percy/playwright');
+const path = require('path');
+const sinon = require('sinon');
+
+const VISUAL_GRACE_PERIOD = 5 * 1000; //Lets the application "simmer" before the snapshot is taken
+
+const CUSTOM_NAME = 'CUSTOM_NAME';
+
+// Snippet from https://github.com/microsoft/playwright/issues/6347#issuecomment-965887758
+// Will replace with cy.clock() equivalent
+test.beforeEach(async ({ context }) => {
+ await context.addInitScript({
+ path: path.join(__dirname, '../../..', './node_modules/sinon/pkg/sinon.js')
+ });
+ await context.addInitScript(() => {
+ window.__clock = sinon.useFakeTimers({
+ now: 0,
+ shouldAdvanceTime: true
+ }); //Set browser clock to UNIX Epoch
+ });
+});
+
+test('Visual - Restricted Notebook is visually correct @addInit', async ({ page }) => {
+ // eslint-disable-next-line no-undef
+ await page.addInitScript({ path: path.join(__dirname, '../plugins/notebook', './addInitRestrictedNotebook.js') });
+ //Go to baseURL
+ await page.goto('/', { waitUntil: 'networkidle' });
+ //Click the Create button
+ await page.click('button:has-text("Create")');
+ // Click text=CUSTOM_NAME
+ await page.click(`text=${CUSTOM_NAME}`);
+ // Click text=OK
+ await Promise.all([
+ page.waitForNavigation({waitUntil: 'networkidle'}),
+ page.click('text=OK')
+ ]);
+
+ // Take a snapshot of the newly created CUSTOM_NAME notebook
+ await page.waitForTimeout(VISUAL_GRACE_PERIOD);
+ await percySnapshot(page, 'Restricted Notebook with CUSTOM_NAME');
+
+});
diff --git a/e2e/tests/visual/controlledClock.visual.spec.js b/e2e/tests/visual/controlledClock.visual.spec.js
new file mode 100644
index 000000000..7eb7f64c8
--- /dev/null
+++ b/e2e/tests/visual/controlledClock.visual.spec.js
@@ -0,0 +1,70 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+/*
+Collection of Visual Tests set to run in a default context. The tests within this suite
+are only meant to run against openmct's app.js started by `npm run start` within the
+`./e2e/playwright-visual.config.js` file.
+
+These should only use functional expect statements to verify assumptions about the state
+in a test and not for functional verification of correctness. Visual tests are not supposed
+to "fail" on assertions. Instead, they should be used to detect changes between builds or branches.
+
+Note: Larger testsuite sizes are OK due to the setup time associated with these tests.
+*/
+
+const { test, expect } = require('@playwright/test');
+const percySnapshot = require('@percy/playwright');
+const path = require('path');
+const sinon = require('sinon');
+
+// Snippet from https://github.com/microsoft/playwright/issues/6347#issuecomment-965887758
+// Will replace with cy.clock() equivalent
+test.beforeEach(async ({ context }) => {
+ await context.addInitScript({
+ // eslint-disable-next-line no-undef
+ path: path.join(__dirname, '../../..', './node_modules/sinon/pkg/sinon.js')
+ });
+ await context.addInitScript(() => {
+ window.__clock = sinon.useFakeTimers({
+ now: 0, //Set browser clock to UNIX Epoch
+ shouldAdvanceTime: false, //Don't advance the clock
+ toFake: ["setTimeout", "nextTick"]
+ });
+ });
+});
+test.use({ storageState: './e2e/test-data/VisualTestData_storage.json' });
+
+test('Visual - Overlay Plot Loading Indicator @localstorage', async ({ page }) => {
+ // Go to baseURL
+ await page.goto('/', { waitUntil: 'networkidle' });
+
+ await page.locator('a:has-text("Unnamed Overlay Plot Overlay Plot")').click();
+ //Ensure that we're on the Unnamed Overlay Plot object
+ await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Overlay Plot');
+
+ //Wait for canvas to be rendered and stop animating
+ await page.locator('canvas >> nth=1').hover({trial: true});
+
+ //Take snapshot of Sine Wave Generator within Overlay Plot
+ await percySnapshot(page, 'SineWaveInOverlayPlot');
+});
diff --git a/e2e/tests/visual/default.spec.js b/e2e/tests/visual/default.spec.js
deleted file mode 100644
index fa6308f2f..000000000
--- a/e2e/tests/visual/default.spec.js
+++ /dev/null
@@ -1,113 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/*
-Collection of Visual Tests set to run in a default context. The tests within this suite
-are only meant to run against openmct's app.js started by `npm run start` within the
-`./e2e/playwright-visual.config.js` file.
-
-These should only use functional expect statements to verify assumptions about the state
-in a test and not for functional verification of correctness. Visual tests are not supposed
-to "fail" on assertions. Instead, they should be used to detect changes between builds or branches.
-
-Note: Larger testsuite sizes are OK due to the setup time associated with these tests.
-*/
-
-const { test, expect } = require('@playwright/test');
-const percySnapshot = require('@percy/playwright');
-const path = require('path');
-const sinon = require('sinon');
-
-const VISUAL_GRACE_PERIOD = 5 * 1000; //Lets the application "simmer" before the snapshot is taken
-
-// Snippet from https://github.com/microsoft/playwright/issues/6347#issuecomment-965887758
-// Will replace with cy.clock() equivalent
-test.beforeEach(async ({ context }) => {
- await context.addInitScript({
- // eslint-disable-next-line no-undef
- path: path.join(__dirname, '../../..', './node_modules/sinon/pkg/sinon.js')
- });
- await context.addInitScript(() => {
- window.__clock = sinon.useFakeTimers(); //Set browser clock to UNIX Epoch
- });
-});
-
-test('Visual - Root and About', async ({ page }) => {
- // Go to baseURL
- await page.goto('/', { waitUntil: 'networkidle' });
-
- // Verify that Create button is actionable
- const createButtonLocator = page.locator('button:has-text("Create")');
- await expect(createButtonLocator).toBeEnabled();
-
- // Take a snapshot of the Dashboard
- await page.waitForTimeout(VISUAL_GRACE_PERIOD);
- await percySnapshot(page, 'Root');
-
- // Click About button
- await page.click('.l-shell__app-logo');
-
- // Modify the Build information in 'about' to be consistent run-over-run
- const versionInformationLocator = page.locator('ul.t-info.l-info.s-info');
- await expect(versionInformationLocator).toBeEnabled();
- await versionInformationLocator.evaluate(node => node.innerHTML = '<li>Version: visual-snapshot</li> <li>Build Date: Mon Nov 15 2021 08:07:51 GMT-0800 (Pacific Standard Time)</li> <li>Revision: 93049cdbc6c047697ca204893db9603b864b8c9f</li> <li>Branch: master</li>');
-
- // Take a snapshot of the About modal
- await page.waitForTimeout(VISUAL_GRACE_PERIOD);
- await percySnapshot(page, 'About');
-});
-
-test('Visual - Default Condition Set', async ({ page }) => {
- //Go to baseURL
- await page.goto('/', { waitUntil: 'networkidle' });
-
- //Click the Create button
- await page.click('button:has-text("Create")');
-
- // Click text=Condition Set
- await page.click('text=Condition Set');
-
- // Click text=OK
- await page.click('text=OK');
-
- // Take a snapshot of the newly created Condition Set object
- await page.waitForTimeout(VISUAL_GRACE_PERIOD);
- await percySnapshot(page, 'Default Condition Set');
-});
-
-test('Visual - Default Condition Widget', async ({ page }) => {
- //Go to baseURL
- await page.goto('/', { waitUntil: 'networkidle' });
-
- //Click the Create button
- await page.click('button:has-text("Create")');
-
- // Click text=Condition Widget
- await page.click('text=Condition Widget');
-
- // Click text=OK
- await page.click('text=OK');
-
- // Take a snapshot of the newly created Condition Widget object
- await page.waitForTimeout(VISUAL_GRACE_PERIOD);
- await percySnapshot(page, 'Default Condition Widget');
-});
diff --git a/e2e/tests/visual/default.visual.spec.js b/e2e/tests/visual/default.visual.spec.js
new file mode 100644
index 000000000..d5c2210ee
--- /dev/null
+++ b/e2e/tests/visual/default.visual.spec.js
@@ -0,0 +1,232 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+/*
+Collection of Visual Tests set to run in a default context. The tests within this suite
+are only meant to run against openmct's app.js started by `npm run start` within the
+`./e2e/playwright-visual.config.js` file.
+
+These should only use functional expect statements to verify assumptions about the state
+in a test and not for functional verification of correctness. Visual tests are not supposed
+to "fail" on assertions. Instead, they should be used to detect changes between builds or branches.
+
+Note: Larger testsuite sizes are OK due to the setup time associated with these tests.
+*/
+
+const { test } = require('../../fixtures.js');
+const { expect } = require('@playwright/test');
+const percySnapshot = require('@percy/playwright');
+const path = require('path');
+const sinon = require('sinon');
+
+const VISUAL_GRACE_PERIOD = 5 * 1000; //Lets the application "simmer" before the snapshot is taken
+
+// Snippet from https://github.com/microsoft/playwright/issues/6347#issuecomment-965887758
+// Will replace with cy.clock() equivalent
+test.beforeEach(async ({ context }) => {
+ await context.addInitScript({
+ // eslint-disable-next-line no-undef
+ path: path.join(__dirname, '../../..', './node_modules/sinon/pkg/sinon.js')
+ });
+ await context.addInitScript(() => {
+ window.__clock = sinon.useFakeTimers({
+ now: 0,
+ shouldAdvanceTime: true
+ }); //Set browser clock to UNIX Epoch
+ });
+});
+
+test('Visual - Root and About', async ({ page }) => {
+ // Go to baseURL
+ await page.goto('/', { waitUntil: 'networkidle' });
+
+ // Verify that Create button is actionable
+ await expect(page.locator('button:has-text("Create")')).toBeEnabled();
+
+ // Take a snapshot of the Dashboard
+ await page.waitForTimeout(VISUAL_GRACE_PERIOD);
+ await percySnapshot(page, 'Root');
+
+ // Click About button
+ await page.click('.l-shell__app-logo');
+
+ // Modify the Build information in 'about' to be consistent run-over-run
+ const versionInformationLocator = page.locator('ul.t-info.l-info.s-info');
+ await expect(versionInformationLocator).toBeEnabled();
+ await versionInformationLocator.evaluate(node => node.innerHTML = '<li>Version: visual-snapshot</li> <li>Build Date: Mon Nov 15 2021 08:07:51 GMT-0800 (Pacific Standard Time)</li> <li>Revision: 93049cdbc6c047697ca204893db9603b864b8c9f</li> <li>Branch: master</li>');
+
+ // Take a snapshot of the About modal
+ await page.waitForTimeout(VISUAL_GRACE_PERIOD);
+ await percySnapshot(page, 'About');
+});
+
+test('Visual - Default Condition Set', async ({ page }) => {
+ //Go to baseURL
+ await page.goto('/', { waitUntil: 'networkidle' });
+
+ //Click the Create button
+ await page.click('button:has-text("Create")');
+
+ // Click text=Condition Set
+ await page.click('text=Condition Set');
+
+ // Click text=OK
+ await page.click('text=OK');
+
+ // Take a snapshot of the newly created Condition Set object
+ await page.waitForTimeout(VISUAL_GRACE_PERIOD);
+ await percySnapshot(page, 'Default Condition Set');
+});
+
+test.fixme('Visual - Default Condition Widget', async ({ page }) => {
+ test.info().annotations.push({
+ type: 'issue',
+ description: 'https://github.com/nasa/openmct/issues/5349'
+ });
+ //Go to baseURL
+ await page.goto('/', { waitUntil: 'networkidle' });
+
+ //Click the Create button
+ await page.click('button:has-text("Create")');
+
+ // Click text=Condition Widget
+ await page.click('text=Condition Widget');
+
+ // Click text=OK
+ await page.click('text=OK');
+
+ // Take a snapshot of the newly created Condition Widget object
+ await page.waitForTimeout(VISUAL_GRACE_PERIOD);
+ await percySnapshot(page, 'Default Condition Widget');
+});
+
+test('Visual - Time Conductor start time is less than end time', async ({ page }) => {
+ //Go to baseURL
+ await page.goto('/', { waitUntil: 'networkidle' });
+ const year = new Date().getFullYear();
+
+ let startDate = 'xxxx-01-01 01:00:00.000Z';
+ startDate = year + startDate.substring(4);
+
+ let endDate = 'xxxx-01-01 02:00:00.000Z';
+ endDate = year + endDate.substring(4);
+
+ await page.locator('input[type="text"]').nth(1).fill(endDate.toString());
+ await page.locator('input[type="text"]').first().fill(startDate.toString());
+
+ // verify no error msg
+ await page.waitForTimeout(VISUAL_GRACE_PERIOD);
+ await percySnapshot(page, 'Default Time conductor');
+
+ startDate = (year + 1) + startDate.substring(4);
+ await page.locator('input[type="text"]').first().fill(startDate.toString());
+ await page.locator('input[type="text"]').nth(1).click();
+
+ // verify error msg for start time (unable to capture snapshot of popup)
+ await page.waitForTimeout(VISUAL_GRACE_PERIOD);
+ await percySnapshot(page, 'Start time error');
+
+ startDate = (year - 1) + startDate.substring(4);
+ await page.locator('input[type="text"]').first().fill(startDate.toString());
+
+ endDate = (year - 2) + endDate.substring(4);
+ await page.locator('input[type="text"]').nth(1).fill(endDate.toString());
+
+ await page.locator('input[type="text"]').first().click();
+
+ // verify error msg for end time (unable to capture snapshot of popup)
+ await page.waitForTimeout(VISUAL_GRACE_PERIOD);
+ await percySnapshot(page, 'End time error');
+});
+
+test('Visual - Sine Wave Generator Form', async ({ page }) => {
+ //Go to baseURL
+ await page.goto('/', { waitUntil: 'networkidle' });
+
+ //Click the Create button
+ await page.click('button:has-text("Create")');
+
+ // Click text=Sine Wave Generator
+ await page.click('text=Sine Wave Generator');
+
+ await page.waitForTimeout(VISUAL_GRACE_PERIOD);
+ await percySnapshot(page, 'Default Sine Wave Generator Form');
+
+ await page.locator('.field.control.l-input-sm input').first().click();
+ await page.locator('.field.control.l-input-sm input').first().fill('');
+
+ // Validate red x mark
+ await page.waitForTimeout(VISUAL_GRACE_PERIOD);
+ await percySnapshot(page, 'removed amplitude property value');
+});
+
+test('Visual - Save Successful Banner', async ({ page }) => {
+ //Go to baseURL
+ await page.goto('/', { waitUntil: 'networkidle' });
+
+ //Click the Create button
+ await page.click('button:has-text("Create")');
+
+ //NOTE Something other than example imagery
+ await page.click('text=Timer');
+
+ // Click text=OK
+ await page.click('text=OK');
+ await page.locator('.c-message-banner__message').hover({ trial: true });
+ await percySnapshot(page, 'Banner message shown');
+
+ //Wait until Save Banner is gone
+ await page.waitForSelector('.c-message-banner__message', { state: 'detached'});
+ await percySnapshot(page, 'Banner message gone');
+});
+
+test('Visual - Display Layout Icon is correct', async ({ page }) => {
+ //Go to baseURL
+ await page.goto('/', { waitUntil: 'networkidle' });
+
+ //Click the Create button
+ await page.click('button:has-text("Create")');
+
+ //Hover on Display Layout option.
+ await page.locator('text=Display Layout').hover();
+ await percySnapshot(page, 'Display Layout Create Menu');
+
+});
+
+test('Visual - Default Gauge is correct', async ({ page }) => {
+
+ //Go to baseURL
+ await page.goto('/', { waitUntil: 'networkidle' });
+
+ //Click the Create button
+ await page.click('button:has-text("Create")');
+
+ await page.click('text=Gauge');
+
+ await page.click('text=OK');
+
+ // Take a snapshot of the newly created Gauge object
+ await page.waitForTimeout(VISUAL_GRACE_PERIOD);
+ await percySnapshot(page, 'Default Gauge');
+
+});
+
diff --git a/e2e/tests/visual/generateVisualTestData.e2e.spec.js b/e2e/tests/visual/generateVisualTestData.e2e.spec.js
new file mode 100644
index 000000000..fc67dab72
--- /dev/null
+++ b/e2e/tests/visual/generateVisualTestData.e2e.spec.js
@@ -0,0 +1,86 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+/*
+This test suite is dedicated to generating LocalStorage via Session Storage to be used
+in some visual test suites like controlledClock.visual.spec.js. This suite should run to completion
+and generate an artifact named ./e2e/test-data/VisualTestData_storage.json . This will run
+on every Commit to ensure that this object still loads into tests correctly and will retain the
+.e2e.spec.js suffix.
+
+TODO: Provide additional validation of object properties as it grows.
+
+*/
+
+const { test } = require('../../fixtures.js');
+const { expect } = require('@playwright/test');
+
+test('Generate Visual Test Data @localStorage', async ({ page, context }) => {
+ //Go to baseURL
+ await page.goto('/', { waitUntil: 'networkidle' });
+
+ await page.locator('button:has-text("Create")').click();
+
+ // add overlay plot with defaults
+ await page.locator('li:has-text("Overlay Plot")').click();
+
+ // Click on My Items in Tree. Workaround for https://github.com/nasa/openmct/issues/5184
+ await page.click('form[name="mctForm"] a:has-text("My Items")');
+
+ await Promise.all([
+ page.waitForNavigation(),
+ page.locator('text=OK').click(),
+ //Wait for Save Banner to appear1
+ page.waitForSelector('.c-message-banner__message')
+ ]);
+
+ // save (exit edit mode)
+ await page.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button').nth(1).click();
+ await page.locator('text=Save and Finish Editing').click();
+
+ // click create button
+ await page.locator('button:has-text("Create")').click();
+
+ // add sine wave generator with defaults
+ await page.locator('li:has-text("Sine Wave Generator")').click();
+
+ //Add a 5000 ms Delay
+ await page.locator('[aria-label="Loading Delay \\(ms\\)"]').fill('5000');
+
+ await Promise.all([
+ page.waitForNavigation(),
+ page.locator('text=OK').click(),
+ //Wait for Save Banner to appear1
+ page.waitForSelector('.c-message-banner__message')
+ ]);
+
+ // focus the overlay plot
+ await page.locator('text=Open MCT My Items >> span').nth(3).click();
+ await Promise.all([
+ page.waitForNavigation(),
+ page.locator('text=Unnamed Overlay Plot').first().click()
+ ]);
+
+ await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Overlay Plot');
+ //Save localStorage for future test execution
+ await context.storageState({ path: './e2e/test-data/VisualTestData_storage.json' });
+});
diff --git a/e2e/tests/visual/search.visual.spec.js b/e2e/tests/visual/search.visual.spec.js
new file mode 100644
index 000000000..654a336dd
--- /dev/null
+++ b/e2e/tests/visual/search.visual.spec.js
@@ -0,0 +1,104 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+/*
+This test suite is dedicated to tests which verify search functionality.
+*/
+
+const { test, expect } = require('@playwright/test');
+const percySnapshot = require('@percy/playwright');
+
+/**
+ * Creates a notebook object and adds an entry.
+ * @param {import('@playwright/test').Page} page
+ */
+async function createClockAndDisplayLayout(page) {
+ //Go to baseURL
+ await page.goto('/', { waitUntil: 'networkidle' });
+
+ // Click button:has-text("Create")
+ await page.locator('button:has-text("Create")').click();
+ // Click li:has-text("Notebook")
+ await page.locator('li:has-text("Clock")').click();
+ // Click button:has-text("OK")
+ await Promise.all([
+ page.waitForNavigation(),
+ page.locator('button:has-text("OK")').click()
+ ]);
+
+ // Click a:has-text("My Items")
+ await Promise.all([
+ page.waitForNavigation(),
+ page.locator('a:has-text("My Items") >> nth=0').click()
+ ]);
+ // Click button:has-text("Create")
+ await page.locator('button:has-text("Create")').click();
+ // Click li:has-text("Notebook")
+ await page.locator('li:has-text("Display Layout")').click();
+ // Click button:has-text("OK")
+ await Promise.all([
+ page.waitForNavigation(),
+ page.locator('button:has-text("OK")').click()
+ ]);
+}
+
+test.describe('Grand Search', () => {
+ test('Can search for objects, and subsequent search dropdown behaves properly', async ({ page }) => {
+ await createClockAndDisplayLayout(page);
+
+ // Click [aria-label="OpenMCT Search"] input[type="search"]
+ await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
+ // Fill [aria-label="OpenMCT Search"] input[type="search"]
+ await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Cl');
+ await expect(page.locator('[aria-label="Search Result"]')).toContainText('Clock');
+ await percySnapshot(page, 'Searching for Clocks');
+ // Click text=Elements >> nth=0
+ await page.locator('text=Elements').first().click();
+ await expect(page.locator('[aria-label="Search Result"]')).not.toBeVisible();
+
+ // Click [aria-label="OpenMCT Search"] [aria-label="Search Input"]
+ await page.locator('[aria-label="OpenMCT Search"] [aria-label="Search Input"]').click();
+ // Click [aria-label="Unnamed Clock clock result"] >> text=Unnamed Clock
+ await page.locator('[aria-label="Unnamed Clock clock result"] >> text=Unnamed Clock').click();
+ await percySnapshot(page, 'Preview for clock should display when editing enabled and search item clicked');
+
+ // Click [aria-label="Close"]
+ await page.locator('[aria-label="Close"]').click();
+ await percySnapshot(page, 'Search should still be showing after preview closed');
+
+ // Click text=Snapshot Save and Finish Editing Save and Continue Editing >> button >> nth=1
+ await page.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button').nth(1).click();
+ // Click text=Save and Finish Editing
+ await page.locator('text=Save and Finish Editing').click();
+ // Click [aria-label="OpenMCT Search"] [aria-label="Search Input"]
+ await page.locator('[aria-label="OpenMCT Search"] [aria-label="Search Input"]').click();
+ // Fill [aria-label="OpenMCT Search"] [aria-label="Search Input"]
+ await page.locator('[aria-label="OpenMCT Search"] [aria-label="Search Input"]').fill('Cl');
+ // Click text=Unnamed Clock
+ await Promise.all([
+ page.waitForNavigation(),
+ page.locator('text=Unnamed Clock').click()
+ ]);
+ await percySnapshot(page, 'Clicking on search results should navigate to them if not editing');
+
+ });
+});
diff --git a/example/profiling/bundle.js b/example/eventGenerator/EventMetadataProvider.js
index df48fcc0a..bf566f61b 100644
--- a/example/profiling/bundle.js
+++ b/example/eventGenerator/EventMetadataProvider.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -20,36 +20,45 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
-define([
- "./src/WatchIndicator",
- "./src/DigestIndicator"
-], function (
- WatchIndicator,
- DigestIndicator
-) {
- "use strict";
-
- return {
- name: "example/profiling",
- definition: {
- "extensions": {
- "indicators": [
+class EventMetadataProvider {
+ constructor() {
+ this.METADATA_BY_TYPE = {
+ 'eventGenerator': {
+ values: [
+ {
+ key: "name",
+ name: "Name",
+ format: "string"
+ },
{
- "implementation": WatchIndicator,
- "depends": [
- "$interval",
- "$rootScope"
- ]
+ key: "utc",
+ name: "Time",
+ format: "utc",
+ hints: {
+ domain: 1
+ }
},
{
- "implementation": DigestIndicator,
- "depends": [
- "$interval",
- "$rootScope"
- ]
+ key: "message",
+ name: "Message",
+ format: "string"
}
]
}
- }
- };
-});
+ };
+ }
+
+ supportsMetadata(domainObject) {
+ return Object.prototype.hasOwnProperty.call(this.METADATA_BY_TYPE, domainObject.type);
+ }
+
+ getMetadata(domainObject) {
+ return Object.assign(
+ {},
+ domainObject.telemetry,
+ this.METADATA_BY_TYPE[domainObject.type]
+ );
+ }
+}
+
+export default EventMetadataProvider;
diff --git a/example/eventGenerator/EventTelemetryProvider.js b/example/eventGenerator/EventTelemetryProvider.js
new file mode 100644
index 000000000..b5e2dbec0
--- /dev/null
+++ b/example/eventGenerator/EventTelemetryProvider.js
@@ -0,0 +1,96 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+/**
+ * Module defining EventTelemetryProvider. Created by chacskaylo on 06/18/2015.
+ */
+
+import messages from './transcript.json';
+
+class EventTelemetryProvider {
+ constructor() {
+ this.defaultSize = 25;
+ }
+
+ generateData(firstObservedTime, count, startTime, duration, name) {
+ const millisecondsSinceStart = startTime - firstObservedTime;
+ const utc = startTime + (count * duration);
+ const ind = count % messages.length;
+ const message = messages[ind] + " - [" + millisecondsSinceStart + "]";
+
+ return {
+ name,
+ utc,
+ message
+ };
+ }
+
+ supportsRequest(domainObject) {
+ return domainObject.type === 'eventGenerator';
+ }
+
+ supportsSubscribe(domainObject) {
+ return domainObject.type === 'eventGenerator';
+ }
+
+ subscribe(domainObject, callback) {
+ const duration = domainObject.telemetry.duration * 1000;
+ const firstObservedTime = Date.now();
+ let count = 0;
+
+ const interval = setInterval(() => {
+ const startTime = Date.now();
+ const datum = this.generateData(firstObservedTime, count, startTime, duration, domainObject.name);
+ count += 1;
+ callback(datum);
+ }, duration);
+
+ return function () {
+ clearInterval(interval);
+ };
+ }
+
+ request(domainObject, options) {
+ let start = options.start;
+ const end = Math.min(Date.now(), options.end); // no future values
+ const duration = domainObject.telemetry.duration * 1000;
+ const size = options.size ? options.size : this.defaultSize;
+ const data = [];
+ const firstObservedTime = options.start;
+ let count = 0;
+
+ if (options.strategy === 'latest' || options.size === 1) {
+ start = end;
+ }
+
+ while (start <= end && data.length < size) {
+ const startTime = options.start + count;
+ data.push(this.generateData(firstObservedTime, count, startTime, duration, domainObject.name));
+ start += duration;
+ count += 1;
+ }
+
+ return Promise.resolve(data);
+ }
+}
+
+export default EventTelemetryProvider;
diff --git a/example/eventGenerator/bundle.js b/example/eventGenerator/bundle.js
deleted file mode 100644
index 8c37d7562..000000000
--- a/example/eventGenerator/bundle.js
+++ /dev/null
@@ -1,80 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
- "./src/EventTelemetryProvider"
-], function (
- EventTelemetryProvider
-) {
- "use strict";
-
- return {
- name: "example/eventGenerator",
- definition: {
- "name": "Event Message Generator",
- "description": "For development use. Creates sample event message data that mimics a live data stream.",
- "extensions": {
- "components": [
- {
- "implementation": EventTelemetryProvider,
- "type": "provider",
- "provides": "telemetryService",
- "depends": [
- "$q",
- "$timeout"
- ]
- }
- ],
- "types": [
- {
- "key": "eventGenerator",
- "name": "Event Message Generator",
- "cssClass": "icon-generator-events",
- "description": "For development use. Creates sample event message data that mimics a live data stream.",
- "priority": 10,
- "features": "creation",
- "model": {
- "telemetry": {}
- },
- "telemetry": {
- "source": "eventGenerator",
- "domains": [
- {
- "key": "utc",
- "name": "Timestamp",
- "format": "utc"
- }
- ],
- "ranges": [
- {
- "key": "message",
- "name": "Message",
- "format": "string"
- }
- ]
- }
- }
- ]
- }
- }
- };
-});
diff --git a/example/identity/bundle.js b/example/eventGenerator/plugin.js
index 45c7b4338..513406dc1 100644
--- a/example/identity/bundle.js
+++ b/example/eventGenerator/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -19,30 +19,24 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
+import EventTelmetryProvider from './EventTelemetryProvider';
+import EventMetadataProvider from './EventMetadataProvider';
-define([
- "./src/ExampleIdentityService"
-], function (
- ExampleIdentityService
-) {
- "use strict";
-
- return {
- name: "example/identity",
- definition: {
- "extensions": {
- "components": [
- {
- "implementation": ExampleIdentityService,
- "provides": "identityService",
- "type": "provider",
- "depends": [
- "dialogService",
- "$q"
- ]
- }
- ]
+export default function EventGeneratorPlugin(options) {
+ return function install(openmct) {
+ openmct.types.addType("eventGenerator", {
+ name: "Event Message Generator",
+ description: "For development use. Creates sample event message data that mimics a live data stream.",
+ cssClass: "icon-generator-events",
+ creatable: true,
+ initialize: function (object) {
+ object.telemetry = {
+ duration: 5
+ };
}
- }
+ });
+ openmct.telemetry.addProvider(new EventTelmetryProvider());
+ openmct.telemetry.addProvider(new EventMetadataProvider());
+
};
-});
+}
diff --git a/example/eventGenerator/pluginSpec.js b/example/eventGenerator/pluginSpec.js
new file mode 100644
index 000000000..54627b7b2
--- /dev/null
+++ b/example/eventGenerator/pluginSpec.js
@@ -0,0 +1,76 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+import EventMessageGeneratorPlugin from './plugin.js';
+import {
+ createOpenMct,
+ resetApplicationState
+} from '../../src/utils/testing';
+
+describe('the plugin', () => {
+ let openmct;
+ const mockDomainObject = {
+ identifier: {
+ namespace: '',
+ key: 'some-value'
+ },
+ telemetry: {
+ duration: 0
+ },
+ options: {},
+ type: 'eventGenerator'
+ };
+
+ beforeEach((done) => {
+ const options = {};
+ openmct = createOpenMct();
+ openmct.install(new EventMessageGeneratorPlugin(options));
+ openmct.on('start', done);
+ openmct.startHeadless();
+ });
+
+ afterEach(async () => {
+ await resetApplicationState(openmct);
+ });
+
+ describe('the plugin', () => {
+ it("supports subscription", (done) => {
+ const unsubscribe = openmct.telemetry.subscribe(mockDomainObject, (telemetry) => {
+ expect(telemetry).not.toEqual(null);
+ expect(telemetry.message).toContain('CC: Eagle, Houston');
+ expect(unsubscribe).not.toEqual(null);
+ unsubscribe();
+ done();
+ });
+ });
+
+ it("supports requests without start/end defined", async () => {
+ const telemetry = await openmct.telemetry.request(mockDomainObject);
+ expect(telemetry[0].message).toContain('CC: Eagle, Houston');
+ });
+
+ it("supports requests with arbitrary start time in the past", async () => {
+ mockDomainObject.options.start = 100000000000; // Mar 03 1973
+ const telemetry = await openmct.telemetry.request(mockDomainObject);
+ expect(telemetry[0].message).toContain('CC: Eagle, Houston');
+ });
+ });
+});
diff --git a/example/eventGenerator/src/EventTelemetry.js b/example/eventGenerator/src/EventTelemetry.js
deleted file mode 100644
index 331176175..000000000
--- a/example/eventGenerator/src/EventTelemetry.js
+++ /dev/null
@@ -1,62 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Module defining EventTelemetry.
- * Created by chacskaylo on 06/18/2015.
- * Modified by shale on 06/23/2015.
- */
-define(
- ['../data/transcript.json'],
- function (messages) {
- "use strict";
-
- var firstObservedTime = Date.now();
-
- function EventTelemetry(request, interval) {
-
- var latestObservedTime = Date.now(),
- count = Math.floor((latestObservedTime - firstObservedTime) / interval),
- generatorData = {};
-
- generatorData.getPointCount = function () {
- return count;
- };
-
- generatorData.getDomainValue = function (i, domain) {
- return i * interval
- + (domain !== 'delta' ? firstObservedTime : 0);
- };
-
- generatorData.getRangeValue = function (i, range) {
- var domainDelta = this.getDomainValue(i) - firstObservedTime,
- ind = i % messages.length;
-
- return messages[ind] + " - [" + domainDelta.toString() + "]";
- };
-
- return generatorData;
- }
-
- return EventTelemetry;
- }
-);
diff --git a/example/eventGenerator/src/EventTelemetryProvider.js b/example/eventGenerator/src/EventTelemetryProvider.js
deleted file mode 100644
index c6ed7ac90..000000000
--- a/example/eventGenerator/src/EventTelemetryProvider.js
+++ /dev/null
@@ -1,118 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Module defining EventTelemetryProvider. Created by chacskaylo on 06/18/2015.
- */
-define(
- ["./EventTelemetry"],
- function (EventTelemetry) {
- "use strict";
-
- /**
- *
- * @constructor
- */
- function EventTelemetryProvider($q, $timeout) {
- var subscriptions = [],
- genInterval = 1000,
- generating = false;
-
- //
- function matchesSource(request) {
- return request.source === "eventGenerator";
- }
-
- // Used internally; this will be repacked by doPackage
- function generateData(request) {
- return {
- key: request.key,
- telemetry: new EventTelemetry(request, genInterval)
- };
- }
-
- //
- function doPackage(results) {
- var packaged = {};
- results.forEach(function (result) {
- packaged[result.key] = result.telemetry;
- });
-
- // Format as expected (sources -> keys -> telemetry)
- return { eventGenerator: packaged };
- }
-
- function requestTelemetry(requests) {
- return $timeout(function () {
- return doPackage(requests.filter(matchesSource).map(generateData));
- }, 0);
- }
-
- function handleSubscriptions(timeout) {
- subscriptions.forEach(function (subscription) {
- var requests = subscription.requests;
- subscription.callback(doPackage(
- requests.filter(matchesSource).map(generateData)
- ));
- });
- }
-
- function startGenerating() {
- generating = true;
- $timeout(function () {
- handleSubscriptions();
- if (generating && subscriptions.length > 0) {
- startGenerating();
- } else {
- generating = false;
- }
- }, genInterval);
- }
-
- function subscribe(callback, requests) {
- var subscription = {
- callback: callback,
- requests: requests
- };
- function unsubscribe() {
- subscriptions = subscriptions.filter(function (s) {
- return s !== subscription;
- });
- }
-
- subscriptions.push(subscription);
- if (!generating) {
- startGenerating();
- }
-
- return unsubscribe;
- }
-
- return {
- requestTelemetry: requestTelemetry,
- subscribe: subscribe
- };
- }
-
- return EventTelemetryProvider;
- }
-);
diff --git a/example/eventGenerator/data/transcript.json b/example/eventGenerator/transcript.json
index ce7873533..ce7873533 100644
--- a/example/eventGenerator/data/transcript.json
+++ b/example/eventGenerator/transcript.json
diff --git a/platform/persistence/queue/test/PersistenceFailureConstantsSpec.js b/example/exampleTags/plugin.js
index 7a68bf152..b78ad89eb 100644
--- a/platform/persistence/queue/test/PersistenceFailureConstantsSpec.js
+++ b/example/exampleTags/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -19,16 +19,15 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
-
-define(
- ["../src/PersistenceFailureConstants"],
- function (PersistenceFailureConstants) {
-
- describe("Persistence failure constants", function () {
- it("defines an overwrite key", function () {
- expect(PersistenceFailureConstants.OVERWRITE_KEY)
- .toEqual(jasmine.any(String));
- });
+import availableTags from './tags.json';
+/**
+ * @returns {function} The plugin install function
+ */
+export default function exampleTagsPlugin() {
+ return function install(openmct) {
+ Object.keys(availableTags.tags).forEach(tagKey => {
+ const tagDefinition = availableTags.tags[tagKey];
+ openmct.annotation.defineTag(tagKey, tagDefinition);
});
- }
-);
+ };
+}
diff --git a/example/exampleTags/tags.json b/example/exampleTags/tags.json
new file mode 100644
index 000000000..31a1b823a
--- /dev/null
+++ b/example/exampleTags/tags.json
@@ -0,0 +1,19 @@
+{
+ "tags": {
+ "46a62ad1-bb86-4f88-9a17-2a029e12669d": {
+ "label": "Science",
+ "backgroundColor": "#cc0000",
+ "foregroundColor": "#ffffff"
+ },
+ "65f150ef-73b7-409a-b2e8-258cbd8b7323": {
+ "label": "Driving",
+ "backgroundColor": "#ffad32",
+ "foregroundColor": "#333333"
+ },
+ "f156b038-c605-46db-88a6-67cf2489a371": {
+ "label": "Drilling",
+ "backgroundColor": "#b0ac4e",
+ "foregroundColor": "#FFFFFF"
+ }
+ }
+}
diff --git a/example/exampleUser/ExampleUserProvider.js b/example/exampleUser/ExampleUserProvider.js
new file mode 100644
index 000000000..8fdd02923
--- /dev/null
+++ b/example/exampleUser/ExampleUserProvider.js
@@ -0,0 +1,207 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+import EventEmitter from 'EventEmitter';
+import { v4 as uuid } from 'uuid';
+import createExampleUser from './exampleUserCreator';
+
+const STATUSES = [{
+ key: "NO_STATUS",
+ label: "Not set",
+ iconClass: "icon-question-mark",
+ iconClassPoll: "icon-status-poll-question-mark"
+}, {
+ key: "GO",
+ label: "Go",
+ iconClass: "icon-check",
+ iconClassPoll: "icon-status-poll-question-mark",
+ statusClass: "s-status-ok",
+ statusBgColor: "#33cc33",
+ statusFgColor: "#000"
+}, {
+ key: "MAYBE",
+ label: "Maybe",
+ iconClass: "icon-alert-triangle",
+ iconClassPoll: "icon-status-poll-question-mark",
+ statusClass: "s-status-warning",
+ statusBgColor: "#ffb66c",
+ statusFgColor: "#000"
+}, {
+ key: "NO_GO",
+ label: "No go",
+ iconClass: "icon-circle-slash",
+ iconClassPoll: "icon-status-poll-question-mark",
+ statusClass: "s-status-error",
+ statusBgColor: "#9900cc",
+ statusFgColor: "#fff"
+}];
+/**
+ * @implements {StatusUserProvider}
+ */
+export default class ExampleUserProvider extends EventEmitter {
+ constructor(openmct, {defaultStatusRole} = {defaultStatusRole: undefined}) {
+ super();
+
+ this.openmct = openmct;
+ this.user = undefined;
+ this.loggedIn = false;
+ this.autoLoginUser = undefined;
+ this.status = STATUSES[1];
+ this.pollQuestion = undefined;
+ this.defaultStatusRole = defaultStatusRole;
+
+ this.ExampleUser = createExampleUser(this.openmct.user.User);
+ this.loginPromise = undefined;
+ }
+
+ isLoggedIn() {
+ return this.loggedIn;
+ }
+
+ autoLogin(username) {
+ this.autoLoginUser = username;
+ }
+
+ getCurrentUser() {
+ if (!this.loginPromise) {
+ this.loginPromise = this._login().then(() => this.user);
+ }
+
+ return this.loginPromise;
+ }
+
+ canProvideStatusForRole() {
+ return Promise.resolve(true);
+ }
+
+ canSetPollQuestion() {
+ return Promise.resolve(true);
+ }
+
+ hasRole(roleId) {
+ if (!this.loggedIn) {
+ Promise.resolve(undefined);
+ }
+
+ return Promise.resolve(this.user.getRoles().includes(roleId));
+ }
+
+ getStatusRoleForCurrentUser() {
+ return Promise.resolve(this.defaultStatusRole);
+ }
+
+ getAllStatusRoles() {
+ return Promise.resolve([this.defaultStatusRole]);
+ }
+
+ getStatusForRole(role) {
+ return Promise.resolve(this.status);
+ }
+
+ async getDefaultStatusForRole(role) {
+ const allRoles = await this.getPossibleStatuses();
+
+ return allRoles?.[0];
+ }
+
+ setStatusForRole(role, status) {
+ this.status = status;
+ this.emit('statusChange', {
+ role,
+ status
+ });
+
+ return true;
+ }
+
+ getPollQuestion() {
+ return Promise.resolve({
+ question: 'Set "GO" if your position is ready for a boarding action on the Klingon cruiser',
+ timestamp: Date.now()
+ });
+ }
+
+ setPollQuestion(pollQuestion) {
+ this.pollQuestion = {
+ question: pollQuestion,
+ timestamp: Date.now()
+ };
+ this.emit("pollQuestionChange", this.pollQuestion);
+
+ return true;
+ }
+
+ getPossibleStatuses() {
+ return Promise.resolve(STATUSES);
+ }
+
+ _login() {
+ const id = uuid();
+
+ // for testing purposes, this will skip the form, this wouldn't be used in
+ // a normal authentication process
+ if (this.autoLoginUser) {
+ this.user = new this.ExampleUser(id, this.autoLoginUser, ['example-role']);
+ this.loggedIn = true;
+
+ return Promise.resolve();
+ }
+
+ const formStructure = {
+ title: "Login",
+ sections: [
+ {
+ rows: [
+ {
+ key: "username",
+ control: "textfield",
+ name: "Username",
+ pattern: "\\S+",
+ required: true,
+ cssClass: "l-input-lg",
+ value: ''
+ }
+ ]
+ }
+ ],
+ buttons: {
+ submit: {
+ label: 'Login'
+ }
+ }
+ };
+
+ return this.openmct.forms.showForm(formStructure).then(
+ (info) => {
+ this.user = new this.ExampleUser(id, info.username, ['example-role']);
+ this.loggedIn = true;
+ },
+ () => { // user canceled, setting a default username
+ this.user = new this.ExampleUser(id, 'Pat', ['example-role']);
+ this.loggedIn = true;
+ }
+ );
+ }
+}
+/**
+ * @typedef {import('@/api/user/StatusUserProvider').default} StatusUserProvider
+ */
diff --git a/example/mobile/res/sass/mobile-example.scss b/example/exampleUser/exampleUserCreator.js
index 5c10aca81..8ffc88e3f 100644
--- a/example/mobile/res/sass/mobile-example.scss
+++ b/example/exampleUser/exampleUserCreator.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -19,13 +19,18 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
-@import "../../../../platform/commonUI/general/res/sass/constants";
-@import "../../../../platform/commonUI/general/res/sass/mobile/constants";
-@import "../../../../platform/commonUI/general/res/sass/mobile/mixins";
-@include phoneandtablet {
- // Show the Create button
- .create-button-holder {
- display: block !important;
- }
+export default function createExampleUser(UserClass) {
+ return class ExampleUser extends UserClass {
+ constructor(id, name, roles) {
+ super(id, name);
+
+ this.roles = roles;
+ this.getRoles = this.getRoles.bind(this);
+ }
+
+ getRoles() {
+ return this.roles;
+ }
+ };
}
diff --git a/platform/commonUI/general/src/SplashScreenManager.js b/example/exampleUser/plugin.js
index cd24aec06..af533f098 100644
--- a/platform/commonUI/general/src/SplashScreenManager.js
+++ b/example/exampleUser/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -20,25 +20,21 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
-define([
+import ExampleUserProvider from './ExampleUserProvider';
-], function (
-
-) {
+export default function ExampleUserPlugin({autoLoginUser, defaultStatusRole} = {
+ autoLoginUser: 'guest',
+ defaultStatusRole: 'test-role'
+}) {
+ return function install(openmct) {
+ const userProvider = new ExampleUserProvider(openmct, {
+ defaultStatusRole
+ });
- function SplashScreenManager($document) {
- var splash;
- $document = $document[0];
- splash = $document.querySelectorAll('.l-splash-holder')[0];
- if (!splash) {
- return;
+ if (autoLoginUser !== undefined) {
+ userProvider.autoLogin(autoLoginUser);
}
- splash.className += ' fadeout';
- splash.addEventListener('transitionend', function () {
- splash.parentNode.removeChild(splash);
- });
- }
-
- return SplashScreenManager;
-});
+ openmct.user.setProvider(userProvider);
+ };
+}
diff --git a/platform/identity/test/IdentityProviderSpec.js b/example/exampleUser/pluginSpec.js
index 03da7c766..02719d99d 100644
--- a/platform/identity/test/IdentityProviderSpec.js
+++ b/example/exampleUser/pluginSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -20,30 +20,31 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
-define(
- [
- '../src/IdentityProvider'
- ],
- function (IdentityProvider) {
+import {
+ createOpenMct,
+ resetApplicationState
+} from '../../src/utils/testing';
+import ExampleUserProvider from './ExampleUserProvider';
- describe("IdentityProvider", function () {
- var mockQ, provider;
+describe("The Example User Plugin", () => {
+ let openmct;
- beforeEach(function () {
- mockQ = jasmine.createSpyObj('$q', ['when']);
- mockQ.when.and.callFake(function (v) {
- return Promise.resolve(v);
- });
+ beforeEach(() => {
+ openmct = createOpenMct();
+ });
- provider = new IdentityProvider(mockQ);
- });
+ afterEach(() => {
+ return resetApplicationState(openmct);
+ });
- it("provides an undefined user", function () {
- return provider.getUser().then(function (user) {
- expect(user).toBe(undefined);
- });
- });
+ it('is not installed by default', () => {
+ expect(openmct.user.hasProvider()).toBeFalse();
+ });
+ it('can be installed', () => {
+ openmct.user.on('providerAdded', (provider) => {
+ expect(provider).toBeInstanceOf(ExampleUserProvider);
});
- }
-);
+ openmct.install(openmct.plugins.example.ExampleUser());
+ });
+});
diff --git a/example/export/ExportTelemetryAsCSVAction.js b/example/export/ExportTelemetryAsCSVAction.js
deleted file mode 100644
index fdc162dd9..000000000
--- a/example/export/ExportTelemetryAsCSVAction.js
+++ /dev/null
@@ -1,89 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([], function () {
- 'use strict';
-
- /**
- * An example of using the `exportService`; queries for telemetry
- * and provides the results as a CSV file.
- * @param {platform/exporters.ExportService} exportService the
- * service which will handle the CSV export
- * @param {ActionContext} context the action's context
- * @constructor
- * @memberof example/export
- * @implements {Action}
- */
- function ExportTelemetryAsCSVAction(exportService, context) {
- this.exportService = exportService;
- this.context = context;
- }
-
- ExportTelemetryAsCSVAction.prototype.perform = function () {
- var context = this.context,
- domainObject = context.domainObject,
- telemetry = domainObject.getCapability("telemetry"),
- metadata = telemetry.getMetadata(),
- domains = metadata.domains,
- ranges = metadata.ranges,
- exportService = this.exportService;
-
- function getName(domainOrRange) {
- return domainOrRange.name;
- }
-
- telemetry.requestData({}).then(function (series) {
- var headers = domains.map(getName).concat(ranges.map(getName)),
- rows = [],
- row,
- i;
-
- function copyDomainsToRow(telemetryRow, index) {
- domains.forEach(function (domain) {
- telemetryRow[domain.name] = series.getDomainValue(index, domain.key);
- });
- }
-
- function copyRangesToRow(telemetryRow, index) {
- ranges.forEach(function (range) {
- telemetryRow[range.name] = series.getRangeValue(index, range.key);
- });
- }
-
- for (i = 0; i < series.getPointCount(); i += 1) {
- row = {};
- copyDomainsToRow(row, i);
- copyRangesToRow(row, i);
- rows.push(row);
- }
-
- exportService.exportCSV(rows, { headers: headers });
- });
- };
-
- ExportTelemetryAsCSVAction.appliesTo = function (context) {
- return context.domainObject
- && context.domainObject.hasCapability("telemetry");
- };
-
- return ExportTelemetryAsCSVAction;
-});
diff --git a/example/faultManagment/exampleFaultSource.js b/example/faultManagment/exampleFaultSource.js
new file mode 100644
index 000000000..338f0903b
--- /dev/null
+++ b/example/faultManagment/exampleFaultSource.js
@@ -0,0 +1,83 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+export default function () {
+ return function install(openmct) {
+ openmct.install(openmct.plugins.FaultManagement());
+
+ openmct.faults.addProvider({
+ request(domainObject, options) {
+ const faults = JSON.parse(localStorage.getItem('faults'));
+
+ return Promise.resolve(faults.alarms);
+ },
+ subscribe(domainObject, callback) {
+ const faultsData = JSON.parse(localStorage.getItem('faults')).alarms;
+
+ function getRandomIndex(start, end) {
+ return Math.floor(start + (Math.random() * (end - start + 1)));
+ }
+
+ let id = setInterval(() => {
+ const index = getRandomIndex(0, faultsData.length - 1);
+ const randomFaultData = faultsData[index];
+ const randomFault = randomFaultData.fault;
+ randomFault.currentValueInfo.value = Math.random();
+ callback({
+ fault: randomFault,
+ type: 'alarms'
+ });
+ }, 300);
+
+ return () => {
+ clearInterval(id);
+ };
+ },
+ supportsRequest(domainObject) {
+ const faults = localStorage.getItem('faults');
+
+ return faults && domainObject.type === 'faultManagement';
+ },
+ supportsSubscribe(domainObject) {
+ const faults = localStorage.getItem('faults');
+
+ return faults && domainObject.type === 'faultManagement';
+ },
+ acknowledgeFault(fault, { comment = '' }) {
+ console.log('acknowledgeFault', fault);
+ console.log('comment', comment);
+
+ return Promise.resolve({
+ success: true
+ });
+ },
+ shelveFault(fault, shelveData) {
+ console.log('shelveFault', fault);
+ console.log('shelveData', shelveData);
+
+ return Promise.resolve({
+ success: true
+ });
+ }
+ });
+ };
+}
diff --git a/src/adapter/policies/AdaptedViewPolicy.js b/example/faultManagment/pluginSpec.js
index 4701d2a8d..b7a0fa680 100644
--- a/src/adapter/policies/AdaptedViewPolicy.js
+++ b/example/faultManagment/pluginSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -20,23 +20,28 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
-define([], function () {
- function AdaptedViewPolicy(openmct) {
- this.openmct = openmct;
- }
+import {
+ createOpenMct,
+ resetApplicationState
+} from '../../src/utils/testing';
- AdaptedViewPolicy.prototype.allow = function (
- view,
- legacyObject
- ) {
- if (Object.prototype.hasOwnProperty.call(view, 'provider')) {
- const domainObject = legacyObject.useCapability('adapter');
+describe("The Example Fault Source Plugin", () => {
+ let openmct;
- return view.provider.canView(domainObject, this.openmct.router.path);
- }
+ beforeEach(() => {
+ openmct = createOpenMct();
+ });
- return true;
- };
+ afterEach(() => {
+ return resetApplicationState(openmct);
+ });
- return AdaptedViewPolicy;
+ it('is not installed by default', () => {
+ expect(openmct.faults.provider).toBeUndefined();
+ });
+
+ it('can be installed', () => {
+ openmct.install(openmct.plugins.example.ExampleFaultSource());
+ expect(openmct.faults.provider).not.toBeUndefined();
+ });
});
diff --git a/example/forms/bundle.js b/example/forms/bundle.js
deleted file mode 100644
index 638b4d45c..000000000
--- a/example/forms/bundle.js
+++ /dev/null
@@ -1,53 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
- "./src/ExampleFormController"
-], function (
- ExampleFormController
-) {
- "use strict";
-
- return {
- name: "example/forms",
- definition: {
- "name": "Declarative Forms example",
- "sources": "src",
- "extensions": {
- "controllers": [
- {
- "key": "ExampleFormController",
- "implementation": ExampleFormController,
- "depends": [
- "$scope"
- ]
- }
- ],
- "routes": [
- {
- "templateUrl": "templates/exampleForm.html"
- }
- ]
- }
- }
- };
-});
diff --git a/example/forms/res/templates/exampleForm.html b/example/forms/res/templates/exampleForm.html
deleted file mode 100644
index ec582b9e5..000000000
--- a/example/forms/res/templates/exampleForm.html
+++ /dev/null
@@ -1,42 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
- as represented by the Administrator of the National Aeronautics and Space
- Administration. All rights reserved.
-
- Open MCT is licensed under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0.
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Open MCT includes source code licensed under additional open source
- licenses. See the Open Source Licenses file (LICENSES.md) included with
- this source code distribution or the Licensing information page available
- at runtime from the About dialog for additional information.
--->
-<div ng-controller="ExampleFormController">
- <mct-toolbar structure="toolbar"
- ng-model="state"
- name="aToolbar"></mct-toolbar>
-
- <mct-form structure="form"
- ng-model="state"
- class="validates"
- name="aForm"></mct-form>
-
- <ul>
- <li>Dirty: {{aForm.$dirty}}</li>
- <li>Valid: {{aForm.$valid}}</li>
- </ul>
-
- <pre>
- <textarea>
- {{state | json}}
- </textarea>
- </pre>
-</div>
diff --git a/example/forms/src/ExampleFormController.js b/example/forms/src/ExampleFormController.js
deleted file mode 100644
index cea41b861..000000000
--- a/example/forms/src/ExampleFormController.js
+++ /dev/null
@@ -1,205 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
- "use strict";
-
- function ExampleFormController($scope) {
- $scope.state = {
-
- };
-
- $scope.toolbar = {
- name: "An example toolbar.",
- sections: [
- {
- description: "First section",
- items: [
- {
- name: "X",
- description: "X coordinate",
- control: "textfield",
- pattern: "^\\d+$",
- disabled: true,
- size: 2,
- key: "x"
- },
- {
- name: "Y",
- description: "Y coordinate",
- control: "textfield",
- pattern: "^\\d+$",
- size: 2,
- key: "y"
- },
- {
- name: "W",
- description: "Cell width",
- control: "textfield",
- pattern: "^\\d+$",
- size: 2,
- key: "w"
- },
- {
- name: "H",
- description: "Cell height",
- control: "textfield",
- pattern: "^\\d+$",
- size: 2,
- key: "h"
- }
-
- ]
- },
- {
- description: "Second section",
- items: [
- {
- control: "button",
- csslass: "icon-save",
- click: function () {
- console.log("Save");
- }
- },
- {
- control: "button",
- csslass: "icon-x",
- description: "Button B",
- click: function () {
- console.log("Cancel");
- }
- },
- {
- control: "button",
- csslass: "icon-trash",
- description: "Button C",
- disabled: true,
- click: function () {
- console.log("Delete");
- }
- }
- ]
- },
- {
- items: [
- {
- control: "color",
- key: "color"
- }
- ]
- }
- ]
- };
-
- $scope.form = {
- name: "An example form.",
- sections: [
- {
- name: "First section",
- rows: [
- {
- name: "Check me",
- control: "checkbox",
- key: "checkMe"
- },
- {
- name: "Enter your name",
- required: true,
- control: "textfield",
- key: "yourName"
- },
- {
- name: "Enter a number",
- control: "textfield",
- pattern: "^\\d+$",
- key: "aNumber"
- }
- ]
- },
- {
- name: "Second section",
- rows: [
- {
- name: "Pick a date",
- required: true,
- description: "Enter date in form YYYY-DDD",
- control: "datetime",
- key: "aDate"
- },
- {
- name: "Choose something",
- control: "select",
- options: [
- {
- name: "Hats",
- value: "hats"
- },
- {
- name: "Bats",
- value: "bats"
- },
- {
- name: "Cats",
- value: "cats"
- },
- {
- name: "Mats",
- value: "mats"
- }
- ],
- key: "aChoice"
- },
- {
- name: "Choose something",
- control: "select",
- required: true,
- options: [
- {
- name: "Hats",
- value: "hats"
- },
- {
- name: "Bats",
- value: "bats"
- },
- {
- name: "Cats",
- value: "cats"
- },
- {
- name: "Mats",
- value: "mats"
- }
- ],
- key: "aRequiredChoice"
- }
- ]
- }
- ]
- };
- }
-
- return ExampleFormController;
- }
-);
diff --git a/example/generator/GeneratorMetadataProvider.js b/example/generator/GeneratorMetadataProvider.js
index 7a8cd9832..f274d2d53 100644
--- a/example/generator/GeneratorMetadataProvider.js
+++ b/example/generator/GeneratorMetadataProvider.js
@@ -29,12 +29,12 @@ define([
}
},
{
- key: "cos",
- name: "Cosine",
- unit: "deg",
- formatString: '%0.2f',
+ key: "wavelengths",
+ name: "Wavelength",
+ unit: "nm",
+ format: 'string[]',
hints: {
- domain: 3
+ range: 4
}
},
// Need to enable "LocalTimeSystem" plugin to make use of this
@@ -64,6 +64,14 @@ define([
hints: {
range: 2
}
+ },
+ {
+ key: "intensities",
+ name: "Intensities",
+ format: 'number[]',
+ hints: {
+ range: 3
+ }
}
]
},
diff --git a/example/generator/GeneratorProvider.js b/example/generator/GeneratorProvider.js
index 14bd9031a..ee0bf98f9 100644
--- a/example/generator/GeneratorProvider.js
+++ b/example/generator/GeneratorProvider.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -32,11 +32,12 @@ define([
offset: 0,
dataRateInHz: 1,
randomness: 0,
- phase: 0
+ phase: 0,
+ loadDelay: 0
};
- function GeneratorProvider() {
- this.workerInterface = new WorkerInterface();
+ function GeneratorProvider(openmct) {
+ this.workerInterface = new WorkerInterface(openmct);
}
GeneratorProvider.prototype.canProvideTelemetry = function (domainObject) {
@@ -53,8 +54,9 @@ define([
'period',
'offset',
'dataRateInHz',
+ 'randomness',
'phase',
- 'randomness'
+ 'loadDelay'
];
request = request || {};
diff --git a/example/generator/SinewaveLimitProvider.js b/example/generator/SinewaveLimitProvider.js
index e714f3f0d..53259f70a 100644
--- a/example/generator/SinewaveLimitProvider.js
+++ b/example/generator/SinewaveLimitProvider.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/example/generator/StateGeneratorProvider.js b/example/generator/StateGeneratorProvider.js
index 3733d03a3..98aa5940d 100644
--- a/example/generator/StateGeneratorProvider.js
+++ b/example/generator/StateGeneratorProvider.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/example/generator/WorkerInterface.js b/example/generator/WorkerInterface.js
index 372bd3bbc..1573800ff 100644
--- a/example/generator/WorkerInterface.js
+++ b/example/generator/WorkerInterface.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -21,20 +21,13 @@
*****************************************************************************/
define([
- 'raw-loader!./generatorWorker.js',
'uuid'
], function (
- workerText,
- uuid
+ { v4: uuid }
) {
-
- var workerBlob = new Blob(
- [workerText],
- {type: 'application/javascript'}
- );
- var workerUrl = URL.createObjectURL(workerBlob);
-
- function WorkerInterface() {
+ function WorkerInterface(openmct) {
+ // eslint-disable-next-line no-undef
+ const workerUrl = `${openmct.getAssetPath()}${__OPENMCT_ROOT_RELATIVE__}generatorWorker.js`;
this.worker = new Worker(workerUrl);
this.worker.onmessage = this.onMessage.bind(this);
this.callbacks = {};
diff --git a/example/generator/generatorWorker.js b/example/generator/generatorWorker.js
index 563dbda69..bc9083da3 100644
--- a/example/generator/generatorWorker.js
+++ b/example/generator/generatorWorker.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -77,7 +77,8 @@
utc: nextStep,
yesterday: nextStep - 60 * 60 * 24 * 1000,
sin: sin(nextStep, data.period, data.amplitude, data.offset, data.phase, data.randomness),
- wavelength: wavelength(start, nextStep),
+ wavelengths: wavelengths(),
+ intensities: intensities(),
cos: cos(nextStep, data.period, data.amplitude, data.offset, data.phase, data.randomness)
}
});
@@ -115,6 +116,7 @@
var dataRateInHz = request.dataRateInHz;
var phase = request.phase;
var randomness = request.randomness;
+ var loadDelay = Math.max(request.loadDelay, 0);
var step = 1000 / dataRateInHz;
var nextStep = start - (start % step) + step;
@@ -126,11 +128,20 @@
utc: nextStep,
yesterday: nextStep - 60 * 60 * 24 * 1000,
sin: sin(nextStep, period, amplitude, offset, phase, randomness),
- wavelength: wavelength(start, nextStep),
+ wavelengths: wavelengths(),
+ intensities: intensities(),
cos: cos(nextStep, period, amplitude, offset, phase, randomness)
});
}
+ if (loadDelay === 0) {
+ postOnRequest(message, request, data);
+ } else {
+ setTimeout(() => postOnRequest(message, request, data), loadDelay);
+ }
+ }
+
+ function postOnRequest(message, request, data) {
self.postMessage({
id: message.id,
data: request.spectra ? {
@@ -154,8 +165,28 @@
* Math.sin(phase + (timestamp / period / 1000 * Math.PI * 2)) + (amplitude * Math.random() * randomness) + offset;
}
- function wavelength(start, nextStep) {
- return (nextStep - start) / 10;
+ function wavelengths() {
+ let values = [];
+ while (values.length < 5) {
+ const randomValue = Math.random() * 100;
+ if (!values.includes(randomValue)) {
+ values.push(String(randomValue));
+ }
+ }
+
+ return values;
+ }
+
+ function intensities() {
+ let values = [];
+ while (values.length < 5) {
+ const randomValue = Math.random() * 10;
+ if (!values.includes(randomValue)) {
+ values.push(String(randomValue));
+ }
+ }
+
+ return values;
}
function sendError(error, message) {
diff --git a/example/generator/plugin.js b/example/generator/plugin.js
index a7a027e32..8f820e16f 100644
--- a/example/generator/plugin.js
+++ b/example/generator/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -81,7 +81,7 @@ define([
{
name: "Amplitude",
control: "numberfield",
- cssClass: "l-input-sm l-numeric",
+ cssClass: "l-numeric",
key: "amplitude",
required: true,
property: [
@@ -92,7 +92,7 @@ define([
{
name: "Offset",
control: "numberfield",
- cssClass: "l-input-sm l-numeric",
+ cssClass: "l-numeric",
key: "offset",
required: true,
property: [
@@ -132,6 +132,17 @@ define([
"telemetry",
"randomness"
]
+ },
+ {
+ name: "Loading Delay (ms)",
+ control: "numberfield",
+ cssClass: "l-input-sm l-numeric",
+ key: "loadDelay",
+ required: true,
+ property: [
+ "telemetry",
+ "loadDelay"
+ ]
}
],
initialize: function (object) {
@@ -141,12 +152,13 @@ define([
offset: 0,
dataRateInHz: 1,
phase: 0,
- randomness: 0
+ randomness: 0,
+ loadDelay: 0
};
}
});
- openmct.telemetry.addProvider(new GeneratorProvider());
+ openmct.telemetry.addProvider(new GeneratorProvider(openmct));
openmct.telemetry.addProvider(new GeneratorMetadataProvider());
openmct.telemetry.addProvider(new SinewaveLimitProvider());
};
diff --git a/example/identity/src/ExampleIdentityService.js b/example/identity/src/ExampleIdentityService.js
deleted file mode 100644
index 3efaa8be1..000000000
--- a/example/identity/src/ExampleIdentityService.js
+++ /dev/null
@@ -1,94 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- function () {
- "use strict";
-
- var DEFAULT_IDENTITY = {
- key: "user",
- name: "Example User"
- },
- DIALOG_STRUCTURE = {
- name: "Identify Yourself",
- sections: [{
- rows: [
- {
- name: "User ID",
- control: "textfield",
- key: "key",
- required: true
- },
- {
- name: "Human name",
- control: "textfield",
- key: "name",
- required: true
- }
- ]
- }]
- };
-
- /**
- * Example implementation of an identity service. This prompts the
- * user to enter a name and user ID; in a more realistic
- * implementation, this would be read from a server, possibly
- * prompting for a user name and password (or similar) as
- * appropriate.
- *
- * @implements {IdentityService}
- * @memberof platform/identity
- */
- function ExampleIdentityProvider(dialogService, $q) {
- this.dialogService = dialogService;
- this.$q = $q;
-
- this.returnUser = this.returnUser.bind(this);
- this.returnUndefined = this.returnUndefined.bind(this);
- }
-
- ExampleIdentityProvider.prototype.getUser = function () {
- if (this.user) {
- return this.$q.when(this.user);
- } else {
- return this.dialogService.getUserInput(DIALOG_STRUCTURE, DEFAULT_IDENTITY)
- .then(this.returnUser, this.returnUndefined);
- }
- };
-
- /**
- * @private
- */
- ExampleIdentityProvider.prototype.returnUser = function (user) {
- return this.user = user;
- };
-
- /**
- * @private
- */
- ExampleIdentityProvider.prototype.returnUndefined = function () {
- return undefined;
- };
-
- return ExampleIdentityProvider;
- }
-);
diff --git a/example/imagery/plugin.js b/example/imagery/plugin.js
index 9198ad313..2f323356d 100644
--- a/example/imagery/plugin.js
+++ b/example/imagery/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -59,7 +59,8 @@ export default function () {
object.configuration = {
imageLocation: '',
imageLoadDelayInMilliSeconds: DEFAULT_IMAGE_LOAD_DELAY_IN_MILISECONDS,
- imageSamples: []
+ imageSamples: [],
+ layers: []
};
object.telemetry = {
@@ -90,7 +91,21 @@ export default function () {
format: 'image',
hints: {
image: 1
- }
+ },
+ layers: [
+ {
+ source: 'dist/imagery/example-imagery-layer-16x9.png',
+ name: '16:9'
+ },
+ {
+ source: 'dist/imagery/example-imagery-layer-safe.png',
+ name: 'Safe'
+ },
+ {
+ source: 'dist/imagery/example-imagery-layer-scale.png',
+ name: 'Scale'
+ }
+ ]
},
{
name: 'Image Download Name',
@@ -153,7 +168,7 @@ function getImageUrlListFromConfig(configuration) {
}
function getImageLoadDelay(domainObject) {
- const imageLoadDelay = domainObject.configuration.imageLoadDelayInMilliSeconds;
+ const imageLoadDelay = Math.trunc(Number(domainObject.configuration.imageLoadDelayInMilliSeconds));
if (!imageLoadDelay) {
openmctInstance.objects.mutate(domainObject, 'configuration.imageLoadDelayInMilliSeconds', DEFAULT_IMAGE_LOAD_DELAY_IN_MILISECONDS);
@@ -175,7 +190,9 @@ function getRealtimeProvider() {
subscribe: (domainObject, callback) => {
const delay = getImageLoadDelay(domainObject);
const interval = setInterval(() => {
- callback(pointForTimestamp(Date.now(), domainObject.name, getImageSamples(domainObject.configuration), delay));
+ const imageSamples = getImageSamples(domainObject.configuration);
+ const datum = pointForTimestamp(Date.now(), domainObject.name, imageSamples, delay);
+ callback(datum);
}, delay);
return () => {
@@ -214,8 +231,9 @@ function getLadProvider() {
},
request: (domainObject, options) => {
const delay = getImageLoadDelay(domainObject);
+ const datum = pointForTimestamp(Date.now(), domainObject.name, getImageSamples(domainObject.configuration), delay);
- return Promise.resolve([pointForTimestamp(Date.now(), domainObject.name, delay)]);
+ return Promise.resolve([datum]);
}
};
}
diff --git a/example/msl/README.md b/example/msl/README.md
deleted file mode 100644
index 0df2e1666..000000000
--- a/example/msl/README.md
+++ /dev/null
@@ -1,20 +0,0 @@
-To use this bundle, add the following paths to /main.js -
-'./platform/features/conductor/bundle',
-'./example/msl/bundle',
-
-An example plugin that integrates with public data from the Curiosity rover.
-The data shown used by this plugin is published by the Centro de
-Astrobiología (CSIC-INTA) at http://cab.inta-csic.es/rems/
-
-Fetching data from this source requires a cross-origin request which will
-fail on most modern browsers due to restrictions on such requests. As such,
-it is proxied through a local proxy defined in app.js. In order to use this
-example you will need to run app.js locally.
-
-This example shows integration with an historical telemetry source, as
-opposed to a real-time data source that is streaming back current information
-about the state of a system. This example is atypical of a historical data
-source in that it fetches all data in one request. The server infrastructure
-of an historical telemetry source should ideally allow queries bounded by
-time and other data attributes.
-
diff --git a/example/msl/bundle.js b/example/msl/bundle.js
deleted file mode 100644
index 80755d8d9..000000000
--- a/example/msl/bundle.js
+++ /dev/null
@@ -1,115 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
- "./src/RemsTelemetryServerAdapter",
- "./src/RemsTelemetryModelProvider",
- "./src/RemsTelemetryProvider"
-], function (
- RemsTelemetryServerAdapter,
- RemsTelemetryModelProvider,
- RemsTelemetryProvider
-) {
- "use strict";
-
- return {
- name: "example/msl",
- definition: {
- "name": "Mars Science Laboratory Data Adapter",
- "extensions": {
- "types": [
- {
- "name": "Mars Science Laboratory",
- "key": "msl.curiosity",
- "cssClass": "icon-object"
- },
- {
- "name": "Instrument",
- "key": "msl.instrument",
- "cssClass": "icon-object",
- "model": {"composition": []}
- },
- {
- "name": "Measurement",
- "key": "msl.measurement",
- "cssClass": "icon-telemetry",
- "model": {"telemetry": {}},
- "telemetry": {
- "source": "rems.source",
- "domains": [
- {
- "name": "Time",
- "key": "utc",
- "format": "utc"
- }
- ]
- }
- }
- ],
- "constants": [
- {
- "key": "REMS_WS_URL",
- "value": "/proxyUrl?url=http://cab.inta-csic.es/rems/wp-content/plugins/marsweather-widget/api.php"
- }
- ],
- "roots": [
- {
- "id": "msl:curiosity"
- }
- ],
- "models": [
- {
- "id": "msl:curiosity",
- "priority": "preferred",
- "model": {
- "type": "msl.curiosity",
- "name": "Mars Science Laboratory",
- "composition": ["msl_tlm:rems"]
- }
- }
- ],
- "services": [
- {
- "key": "rems.adapter",
- "implementation": RemsTelemetryServerAdapter,
- "depends": ["$http", "$log", "REMS_WS_URL"]
- }
- ],
- "components": [
- {
- "provides": "modelService",
- "type": "provider",
- "implementation": RemsTelemetryModelProvider,
- "depends": ["rems.adapter"]
- },
- {
- "provides": "telemetryService",
- "type": "provider",
- "implementation": RemsTelemetryProvider,
- "depends": ["rems.adapter", "$q"]
- }
- ]
- }
- }
- };
-});
-
diff --git a/example/msl/data/rems.json b/example/msl/data/rems.json
deleted file mode 100644
index 085e01a02..000000000
--- a/example/msl/data/rems.json
+++ /dev/null
@@ -1 +0,0 @@
-{"descriptions":{"disclaimer_en":"\t \n\t\t\tThe information contained into this file is provided by Centro de Astrobiologia\n\t\t\t(CAB) and is intended for outreach purposes only. Any other use is discouraged.\n\t\t\tCAB will take no responsibility for any result or publication made base on the \n\t\t\tcontent of this file. To access REMS scientific data, visit PDS at http:\/\/pds.nasa.gov.\n\n\t\t\tThe environmental magnitudes given into this file are obtained from the values\n\t\t\tread by the Rover Environmental Monitoring Station (REMS) on board the Mars Science\n\t\t\tLaboratory (MSL) rover on Mars. This file provides the environmental magnitudes at REMS\n\t\t\tlocation, so MSL rover influences those magnitudes (rover position,\n\t\t\trover temperature, rover orientation, rover shade, dust depositions on the rover, etc.)\n\n\t\t\tREMS does not take measurements continuously and it takes measurements at different\n\t\t\ttimes from one day to another. This fact has influence on the variation of the values\n\t\t\tgiven in this file from one day to another .\n\n\t\t\tFor different reasons (instrument maintenance, instrument calibration, instrument degradation, etc.), \n\t\t\tsome or all of the magnitudes in this file may not be available.\n\t\t","disclaimer_es":"\t\t\n\t\t\tLa informaci\u00f3n contenida en este fichero es facilitada por el Centro de Astrobiolog\u00eda\n\t\t\t(CAB) \u00fanicamente para fines divulgativos. Cualquier otro uso queda desaconsejado.\n\t\t\tCAB no se har\u00e1 responsable de ning\u00fan resultado o publicaci\u00f3n basados en el contenido \n\t\t\tde este fichero. Para acceder a los datos cient\u00edficos de REMS, visite el PDS en\n\t\t\thttp:\/\/pds.nasa.gov.\n\n\t\t\tLas magnitudes ambientales dadas es este fichero se obtienen de los valores le\u00eddos\n\t\t\tpor la Estaci\u00f3n de Monitoreo Ambiental del Rover (REMS, por sus siglas en ingl\u00e9s)\n\t\t\tabordo del Laboratorio Cient\u00edfico de Marte (MSL, por sus siglas en ingl\u00e9s). Este\n\t\t\tfichero da las magnitudes ambientales medidas por REMS, por lo que estas magnitudes \n\t\t\test\u00e1n influenciadas por el rover MSL (posici\u00f3n del rover, temperatura del\n\t\t\trover, orientaci\u00f3n del rover, sombras arrojadas por el rover, deposici\u00f3n de polvo sobre\n\t\t\tel rover, etc.)\n\n\t\t\tREMS no est\u00e1 tomando medidas continuamente y, adem\u00e1s, toma medidas a diferentes horas \n\t\t\tde un d\u00eda para otro. Estos hechos tienen influencia en la variaci\u00f3n de los valores\n\t\t\tdados en este fichero para d\u00edas sucesivos.\n\n\t\t\tPor diferentes razones (mantenimiento del instrumento, calibraci\u00f3n del instrumento, degradaci\u00f3n\n\t\t\tdel instrumento, etc.) puede que alguna o todas las magnitudes de este fichero no est\u00e9n disponibles.\n\t\t","sol_desc_en":"\n\t\t\tThe term sol is used to refer to the duration of a day on Mars. A sol is about 24 hours and 40 minutes. For Curiosity,\n\t\t\tsol 0 corresponds with its landing day on Mars.\n\t\t","sol_desc_es":"\n\t\t\tEl t\u00e9rmino sol hace referencia a la duraci\u00f3n de un d\u00eda en Marte. Un sol dura aproximadamente 24 horas y 40 minutos.\n\t\t\tPara Curiosity, el sol 0 corresponde al d\u00eda de su aterrizaje en Marte.\n\t\t","terrestrial_date_desc_en":"\n\t\t\tOne day on Earth is about 24 hours, while a sol (Martian day) is about 24 hours and 40 minutes.\n\t\t\tSo one single sol starts during one Earth day and finishes during the next Earth day. This \n\t\t\tis the Earth day on current sol at midday.\n\t\t","terrestrial_date_desc_es":"\n\t\t\tUn d\u00eda en la Tierra dura aproximadamente 24 horas, mientras que un sol (d\u00eda marciano) dura aproximadamente\n\t\t\t24 horas y 40 minutos. As\u00ed que un sol empieza durante un d\u00eda terrestre, pero termina durante el siguiente.\n\t\t\tEl d\u00eda terrestre dado es el que se corresponde con el mediod\u00eda del sol actual.\n\t\t","temp_desc_en":"\n\t\t\t\tMars is farther from the Sun than Earth, it makes that Mars is colder than our planet.\n\t\t\t\tMoreover, Martian\u2019s atmosphere, which is extremely tenuous, does not retain the heat; \n\t\t\t\thence the difference between day and night's temperatures is more pronounced than in our planet.\n\t\t\t","temp_desc_es":"\n\t\t\t\tMarte est\u00e1 m\u00e1s lejos del Sol que la Tierra, por lo que Marte es m\u00e1s frio que nuestro planeta.\n\t\t\t\tAdem\u00e1s, lo atm\u00f3sfera marciana, que es muy tenue, no retiene bien el calor, \n\t\t\t\tpor lo que las diferencias de temperatura entre el d\u00eda y la noche son m\u00e1s pronunciadas que\n\t\t\t\ten nuestro planeta.\n\t\t\t","pressure_desc_en":"\n\t\t\t\tPressure is a measure of the total mass in a column of air above us. Because\n\t\t\t\tMartian\u2019s atmosphere is extremely tenuous, pressure on Mars' surface is about\n\t\t\t\t160 times smaller than pressure on Earth. Average pressure on Martian surface \n\t\t\t\tis about 700 Pascals (100000 Pascals on Earth) Curiosity is into Gale crater,\n\t\t\t\twhich is about 5 kilometers (3 miles) depth. For this reason, pressure measured\n\t\t\t\tby REMS is usually higher than average pressure on the entire planet.\n\t\t\t","pressure_desc_es":"\n\t\t\t\tLa presi\u00f3n mide la masa total en una columna de aire sobre nosotros. Debido a que\n\t\t\t\tla atm\u00f3sfera marciana es muy tenue, la presi\u00f3n en la superficie de Marte es unas\n\t\t\t\t160 veces m\u00e1s baja que en la Tierra. La presi\u00f3n media en la superficie de Marte \n\t\t\t\tes de unos 700 Pascales (en la Tierra es de 100000) El rover Curiosity est\u00e1 en el \n\t\t\t\tinterior de un cr\u00e1ter, de unos 5 kil\u00f3metros (3 millas) de profundidad. Por esta\n\t\t\t\traz\u00f3n, la presi\u00f3n medida por REMS normalmente ser\u00e1 mayor que la media en el planeta.\n\t\t\t","abs_humidity_desc_en":"\n\t\t\t\tMartian's atmosphere contains water vapor, and humidity measures the amount \n\t\t\t\tof water vapor present in the air. REMS records the relative humidity, \n\t\t\t\twhich is a measure of the amount of water vapor in the air compared \n\t\t\t\twith the amount of water vapor the air can hold at the temperature it happens\n\t\t\t\tto be when you measure it. Water on Mars is also present as water ice, \n\t\t\t\tfor instance, at Mars poles. Until today, however, proof of liquid water \n\t\t\t\tin present-day Mars remains elusive.\n\t\t\t","abs_humidity_desc_es":"\n\t\t\t\tLa atm\u00f3sfera marciana contiene vapor de agua y la humedad mide \n\t\t\t\tla cantidad de vapor de agua presente en el aire. REMS registra la humedad\n\t\t\t\trelativa, que es una medida de la cantidad de vapor de agua que \n\t\t\t\thay en el aire en comparaci\u00f3n con la m\u00e1xima cantidad de vapor de agua\n\t\t\t\tque puede contener a la misma temperatura. El agua en Marte est\u00e1 presente \n\t\t\t\ttambi\u00e9n en forma de hielo, por ejemplo en los polos. Hasta la fecha, sin embargo, \n\t\t\t\tno se han encontrado pruebas de la existencia de agua l\u00edquida en el presente marciano.\n\t\t\t","wind_desc_en":"\n\t\t\t\tNASA Viking landers and NASA Pathfinder rover showed that the average wind speed\n\t\t\t\ton their location was pretty weak: 1 to 4 meters\/second (4 to 15 kilometers\/hour - \n\t\t\t\t2.5 to 9 miles\/hour). However, during a dust storm, winds can reach 30 meters\/second \n\t\t\t\t(110 kilometers\/hour - 68 miles\/hour) or more.\n\t\t\t","wind_desc_es":"\n\t\t\t\tLas Vikings landers y el rover Pathfinder de la NASA mostraron que la velocidad media\n\t\t\t\tdel viento en su localizaci\u00f3n era bastante baja: de 1 a 4 metros\/segundo (de 4 a \n\t\t\t\t15 kil\u00f3metros por hora - de 2.5 a 9 millas\/hora) Sin embargo, durante una tormenta\n\t\t\t\tde polvo, la velocidad del viento en Marte puede alcanzar los 30 metros\/segundo\n\t\t\t\t(110 kil\u00f3metros por hora - 68 millas\/hora) o m\u00e1s.\n\t\t\t","gts_temp_desc_en":"\n\t\t\t\tMartian's atmosphere is extremely tenuous, about 160 times thinner than Earth's,\n\t\t\t\tso heat from the Sun can easily escape. It makes that there are big differences\n\t\t\t\tbetween ground temperature and air temperature. Imagine you were on the Martian equator \n\t\t\t\tat noon, you would feel like summer at your feet, but winter in your head.\n\t\t\t","gts_temp_desc_es":"\n\t\t\t\tLa atm\u00f3sfera marciana es muy tenue, unas 160 veces m\u00e1s delgada que la de la Tierra,\n\t\t\t\tlo que hace que el calor proveniente del Sol pueda escapar con facilidad. Esto hace\n\t\t\t\tque haya grandes diferencias entre la temperatura del suelo y la del aire. Imagina\n\t\t\t\tpor un momento que est\u00e1s en el ecuador marciano al mediod\u00eda, sentir\u00edas\n\t\t\t\ten tus pies una temperatura veraniega, sin embargo ser\u00eda invierno para tu cabeza.\n\t\t\t","local_uv_irradiance_index_desc_en":"\n\t\t\t\tLocal ultraviolet (UV) irradiance index is an indicator of the intensity of the ultraviolet\n\t\t\t\tradiation from the Sun at Curiosity location. UV radiation is a damaging agent for life. \n\t\t\t\tOn Earth, ozone layer prevents damaging ultraviolet light from reaching the surface, to \n\t\t\t\tthe benefit of both plants and animals. However, on Mars, due to the absence of ozone in the \n\t\t\t\tatmosphere, ultraviolet radiation reaches the Martian surface.\n\t\t\t","local_uv_irradiance_index_desc_es":"\n\t\t\t\tEl \u00edndice de irradiaci\u00f3n ultravioleta local es un indicador de la intensidad de la radiaci\u00f3n\n\t\t\t\tultravioleta (UV) proveniente del Sol en la ubicaci\u00f3n del rover Curiosity. La radiaci\u00f3n UV es \n\t\t\t\tun agente da\u00f1ino para la vida. En la Tierra, la capa de ozono nos protege de ella. Sin embargo\n\t\t\t\ten Marte, debido a la ausencia de ozono en la atm\u00f3sfera, la radiaci\u00f3n ultravioleta alcanza\n\t\t\t\tla superficie.\n\t\t\t","atmo_opacity_desc_en":"\n\t\t\t\tWeather on Mars is more extreme than Earth's. Mars is cooler and with\n\t\t\t\tbigger differences between day and night temperatures. Moreover, \n\t\t\t\tdust storms lash its surface. However, Mars' and Earth's climates have\n\t\t\t\timportant similarities, such as the polar ice caps or seasonal changes.\n\t\t\t\tAs on Earth, on Mars we can have sunny, cloudy or windy days, for example.\n\t\t\t","atmo_opacity_desc_es":"\n\t\t\t\tEl clima en Marte es m\u00e1s extremo que en la Tierra. Marte es m\u00e1s frio y\n\t\t\t\tcon mayores diferencias entre las temperaturas del d\u00eda y de la noche.\n\t\t\t\tAdem\u00e1s, fuertes tormentas de polvo azotan su superficie. Sin embargo,\n\t\t\t\texisten importantes similitudes entre el clima marciano y el terrestre,\n\t\t\t\tcomo la existencia de casquetes polares o los cambios de estaciones.\n\t\t\t\tComo ocurre en la Tierra, en Marte podemos tener d\u00edas soleados, nublados\n\t\t\t\to ventosos, por ejemplo.\n\t\t\t","ls_desc_en":"\n\t\t\t\tA Martian year lasts about two Earth's year, which is the time\n\t\t\t\tMars takes to orbit the Sun. Solar longitude is an angle that\n\t\t\t\tgives the position of Mars on its orbit.\t\t\t\t \n\t\t\t","ls_desc_es":"\n\t\t\t\tUn a\u00f1o marciano dura aproximadamente dos a\u00f1os terrestres, el tiempo\n\t\t\t\tque Marte necesita para dar una vuelta completa alrededor del Sol.\n\t\t\t\tLa longitud solar es un \u00e1ngulo que nos da la posici\u00f3n de Marte en su \n\t\t\t\t\u00f3rbita.\n\t\t\t","season_desc_en":"\n\t\t\t\tA Martian year is divided in 12 months, as Earth's. However, Martian months are\n\t\t\t\tfrom 46 to 67 sols (Martian days) long. The longest one is the month 3 (67 sols),\n\t\t\t\tand the shortest one is the month 9 (46 sols) Martian months mark seasonal changes. \n\t\t\t\tIn the southern hemisphere (Curiosity rover location) the autumn starts in month 1; \n\t\t\t\tthe winter in month 4; the spring in month 7; and the summer in month 10.\n\t\t\t","season_desc_es":"\n\t\t\t\tEl a\u00f1o marciano se divide en 12 meses, como en la Tierra. Sin embargo, los meses\n\t\t\t\ten Marte duran entre 46 y 67 soles (d\u00edas marcianos). El m\u00e1s largo es el mes 3\n\t\t\t\t(67 soles) y el m\u00e1s corto es el mes 9 (46 soles). Los meses marcan los\n\t\t\t\tcambios de estaci\u00f3n. En el hemisferio sur (localizaci\u00f3n del rover Curiosity) el\n\t\t\t\toto\u00f1o comienza en el mes 1; el invierno, en el mes 4; la primavera, en el mes 7; y\n\t\t\t\tel verano, en el mes 10. \n\t\t\t\t\t\t\t\n\t\t\t","sunrise_sunset_desc_en":"\n\t\t\t\tThe duration of a Martian day (sol) is about 24 hours and 40 minutes. The duration\n\t\t\t\tof daylight varies along the Martian year, as on Earth.\n\t\t\t","sunrise_sunset_desc_es":"\n\t\t\t\tUn d\u00eda marciano (sol) dura aproximadamente 24 horas y 40 minutos. La duraci\u00f3n\n\t\t\t\tdel d\u00eda y de la noche var\u00eda a lo largo del a\u00f1o, como ocurre en la Tierra.\n\t\t\t"},"soles":[{"id":"1159","terrestrial_date":"2016-01-19","sol":"1228","ls":"97","season":"Month 4","min_temp":"-88","max_temp":"-28","pressure":"832","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:51","sunset":"17:34","local_uv_irradiance_index":"Moderate","min_gts_temp":"-93","max_gts_temp":"1"},{"id":"1158","terrestrial_date":"2016-01-18","sol":"1227","ls":"96","season":"Month 4","min_temp":"-88","max_temp":"-26","pressure":"833","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:51","sunset":"17:34","local_uv_irradiance_index":"Moderate","min_gts_temp":"-97","max_gts_temp":"2"},{"id":"1157","terrestrial_date":"2016-01-17","sol":"1226","ls":"96","season":"Month 4","min_temp":"-87","max_temp":"-24","pressure":"835","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:51","sunset":"17:34","local_uv_irradiance_index":"Moderate","min_gts_temp":"-97","max_gts_temp":"1"},{"id":"1156","terrestrial_date":"2016-01-16","sol":"1225","ls":"95","season":"Month 4","min_temp":"-87","max_temp":"-28","pressure":"836","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:51","sunset":"17:34","local_uv_irradiance_index":"Moderate","min_gts_temp":"-98","max_gts_temp":"-1"},{"id":"1154","terrestrial_date":"2016-01-15","sol":"1224","ls":"95","season":"Month 4","min_temp":"-88","max_temp":"-29","pressure":"837","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:52","sunset":"17:34","local_uv_irradiance_index":"Moderate","min_gts_temp":"-98","max_gts_temp":"1"},{"id":"1155","terrestrial_date":"2016-01-14","sol":"1223","ls":"94","season":"Month 4","min_temp":"-88","max_temp":"-26","pressure":"839","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:52","sunset":"17:34","local_uv_irradiance_index":"Moderate","min_gts_temp":"-98","max_gts_temp":"0"},{"id":"1153","terrestrial_date":"2016-01-13","sol":"1222","ls":"94","season":"Month 4","min_temp":"-87","max_temp":"-28","pressure":"840","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:52","sunset":"17:34","local_uv_irradiance_index":"Moderate","min_gts_temp":"-94","max_gts_temp":"0"},{"id":"1152","terrestrial_date":"2016-01-12","sol":"1221","ls":"93","season":"Month 4","min_temp":"-87","max_temp":"-28","pressure":"841","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:52","sunset":"17:34","local_uv_irradiance_index":"Moderate","min_gts_temp":"-94","max_gts_temp":"-1"},{"id":"1151","terrestrial_date":"2016-01-11","sol":"1220","ls":"93","season":"Month 4","min_temp":"-87","max_temp":"-28","pressure":"842","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:52","sunset":"17:35","local_uv_irradiance_index":"Moderate","min_gts_temp":"-94","max_gts_temp":"-3"},{"id":"1150","terrestrial_date":"2016-01-10","sol":"1219","ls":"93","season":"Month 4","min_temp":"-87","max_temp":"-27","pressure":"844","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:52","sunset":"17:35","local_uv_irradiance_index":"Moderate","min_gts_temp":"-92","max_gts_temp":"-4"},{"id":"1149","terrestrial_date":"2016-01-09","sol":"1218","ls":"92","season":"Month 4","min_temp":"-86","max_temp":"-32","pressure":"845","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:52","sunset":"17:35","local_uv_irradiance_index":"Moderate","min_gts_temp":"-96","max_gts_temp":"-6"},{"id":"1148","terrestrial_date":"2016-01-08","sol":"1217","ls":"92","season":"Month 4","min_temp":"-88","max_temp":"-27","pressure":"846","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:53","sunset":"17:35","local_uv_irradiance_index":"Moderate","min_gts_temp":"-91","max_gts_temp":"-3"},{"id":"1147","terrestrial_date":"2016-01-07","sol":"1216","ls":"91","season":"Month 4","min_temp":"-86","max_temp":"-28","pressure":"847","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:53","sunset":"17:35","local_uv_irradiance_index":"Moderate","min_gts_temp":"-85","max_gts_temp":"-6"},{"id":"1146","terrestrial_date":"2016-01-06","sol":"1215","ls":"91","season":"Month 4","min_temp":"-85","max_temp":"-27","pressure":"848","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:53","sunset":"17:35","local_uv_irradiance_index":"High","min_gts_temp":"-84","max_gts_temp":"-3"},{"id":"1144","terrestrial_date":"2016-01-05","sol":"1214","ls":"90","season":"Month 4","min_temp":"-85","max_temp":"-24","pressure":"849","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:53","sunset":"17:35","local_uv_irradiance_index":"Moderate","min_gts_temp":"-86","max_gts_temp":"-12"},{"id":"1145","terrestrial_date":"2016-01-04","sol":"1213","ls":"90","season":"Month 4","min_temp":"-84","max_temp":"-27","pressure":"851","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:53","sunset":"17:36","local_uv_irradiance_index":"Moderate","min_gts_temp":"-87","max_gts_temp":"-12"},{"id":"1143","terrestrial_date":"2016-01-03","sol":"1212","ls":"89","season":"Month 3","min_temp":"-86","max_temp":"-28","pressure":"851","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:53","sunset":"17:36","local_uv_irradiance_index":"Moderate","min_gts_temp":"-86","max_gts_temp":"-13"},{"id":"1142","terrestrial_date":"2016-01-02","sol":"1211","ls":"89","season":"Month 3","min_temp":"-83","max_temp":"-22","pressure":"853","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:53","sunset":"17:36","local_uv_irradiance_index":"Moderate","min_gts_temp":"-86","max_gts_temp":"-12"},{"id":"1140","terrestrial_date":"2016-01-01","sol":"1210","ls":"88","season":"Month 3","min_temp":"-85","max_temp":"-23","pressure":"854","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:54","sunset":"17:36","local_uv_irradiance_index":"Moderate","min_gts_temp":"-84","max_gts_temp":"-12"},{"id":"1141","terrestrial_date":"2015-12-31","sol":"1209","ls":"88","season":"Month 3","min_temp":"-85","max_temp":"-24","pressure":"855","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:54","sunset":"17:36","local_uv_irradiance_index":"Moderate","min_gts_temp":"-88","max_gts_temp":"-12"},{"id":"1139","terrestrial_date":"2015-12-30","sol":"1208","ls":"88","season":"Month 3","min_temp":"-85","max_temp":"-24","pressure":"857","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:54","sunset":"17:36","local_uv_irradiance_index":"Moderate","min_gts_temp":"-87","max_gts_temp":"-12"},{"id":"1138","terrestrial_date":"2015-12-29","sol":"1207","ls":"87","season":"Month 3","min_temp":"-85","max_temp":"-25","pressure":"858","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:54","sunset":"17:36","local_uv_irradiance_index":"Moderate","min_gts_temp":"-88","max_gts_temp":"-13"},{"id":"1137","terrestrial_date":"2015-12-28","sol":"1206","ls":"87","season":"Month 3","min_temp":"-85","max_temp":"-24","pressure":"859","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:54","sunset":"17:37","local_uv_irradiance_index":"Moderate","min_gts_temp":"-87","max_gts_temp":"-13"},{"id":"1134","terrestrial_date":"2015-12-27","sol":"1205","ls":"86","season":"Month 3","min_temp":"-86","max_temp":"-26","pressure":"861","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:54","sunset":"17:37","local_uv_irradiance_index":"Moderate","min_gts_temp":"-88","max_gts_temp":"-13"},{"id":"1135","terrestrial_date":"2015-12-26","sol":"1204","ls":"86","season":"Month 3","min_temp":"-84","max_temp":"-25","pressure":"862","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:55","sunset":"17:37","local_uv_irradiance_index":"Moderate","min_gts_temp":"-87","max_gts_temp":"-12"},{"id":"1136","terrestrial_date":"2015-12-25","sol":"1203","ls":"85","season":"Month 3","min_temp":"-85","max_temp":"-23","pressure":"863","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:55","sunset":"17:37","local_uv_irradiance_index":"Moderate","min_gts_temp":"-87","max_gts_temp":"-13"},{"id":"1133","terrestrial_date":"2015-12-24","sol":"1202","ls":"85","season":"Month 3","min_temp":"-85","max_temp":"-22","pressure":"864","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:55","sunset":"17:37","local_uv_irradiance_index":"Moderate","min_gts_temp":"-87","max_gts_temp":"-12"},{"id":"1132","terrestrial_date":"2015-12-23","sol":"1201","ls":"84","season":"Month 3","min_temp":"-85","max_temp":"-29","pressure":"866","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:55","sunset":"17:37","local_uv_irradiance_index":"Moderate","min_gts_temp":"-88","max_gts_temp":"-13"},{"id":"1131","terrestrial_date":"2015-12-22","sol":"1200","ls":"84","season":"Month 3","min_temp":"-84","max_temp":"-27","pressure":"866","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:55","sunset":"17:38","local_uv_irradiance_index":"Moderate","min_gts_temp":"-88","max_gts_temp":"-12"},{"id":"1130","terrestrial_date":"2015-12-21","sol":"1199","ls":"84","season":"Month 3","min_temp":"-86","max_temp":"-29","pressure":"868","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:55","sunset":"17:38","local_uv_irradiance_index":"Moderate","min_gts_temp":"-88","max_gts_temp":"-14"},{"id":"1128","terrestrial_date":"2015-12-20","sol":"1198","ls":"83","season":"Month 3","min_temp":"-86","max_temp":"-23","pressure":"868","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:55","sunset":"17:38","local_uv_irradiance_index":"Moderate","min_gts_temp":"-89","max_gts_temp":"-13"},{"id":"1127","terrestrial_date":"2015-12-18","sol":"1197","ls":"83","season":"Month 3","min_temp":"-85","max_temp":"-26","pressure":"869","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:56","sunset":"17:38","local_uv_irradiance_index":"Moderate","min_gts_temp":"-88","max_gts_temp":"-13"},{"id":"1129","terrestrial_date":"2015-12-17","sol":"1196","ls":"82","season":"Month 3","min_temp":"-86","max_temp":"-27","pressure":"871","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:56","sunset":"17:38","local_uv_irradiance_index":"Moderate","min_gts_temp":"-86","max_gts_temp":"-7"},{"id":"1126","terrestrial_date":"2015-12-16","sol":"1195","ls":"82","season":"Month 3","min_temp":"-86","max_temp":"-32","pressure":"872","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:56","sunset":"17:39","local_uv_irradiance_index":"Moderate","min_gts_temp":"-87","max_gts_temp":"-6"},{"id":"1125","terrestrial_date":"2015-12-15","sol":"1194","ls":"81","season":"Month 3","min_temp":"-84","max_temp":"-29","pressure":"873","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:56","sunset":"17:39","local_uv_irradiance_index":"Moderate","min_gts_temp":"-87","max_gts_temp":"-5"},{"id":"1124","terrestrial_date":"2015-12-14","sol":"1193","ls":"81","season":"Month 3","min_temp":"-84","max_temp":"-28","pressure":"875","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:56","sunset":"17:39","local_uv_irradiance_index":"Moderate","min_gts_temp":"-86","max_gts_temp":"-10"},{"id":"1123","terrestrial_date":"2015-12-13","sol":"1192","ls":"80","season":"Month 3","min_temp":"-86","max_temp":"-24","pressure":"876","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:56","sunset":"17:39","local_uv_irradiance_index":"High","min_gts_temp":"-87","max_gts_temp":"-10"},{"id":"1122","terrestrial_date":"2015-12-12","sol":"1191","ls":"80","season":"Month 3","min_temp":"-87","max_temp":"-28","pressure":"876","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:57","sunset":"17:39","local_uv_irradiance_index":"High","min_gts_temp":"-83","max_gts_temp":"-11"},{"id":"1121","terrestrial_date":"2015-12-11","sol":"1190","ls":"79","season":"Month 3","min_temp":"-87","max_temp":"-29","pressure":"877","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:57","sunset":"17:39","local_uv_irradiance_index":"High","min_gts_temp":"-84","max_gts_temp":"-12"},{"id":"1120","terrestrial_date":"2015-12-10","sol":"1189","ls":"79","season":"Month 3","min_temp":"-87","max_temp":"-24","pressure":"879","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:57","sunset":"17:40","local_uv_irradiance_index":"Moderate","min_gts_temp":"-85","max_gts_temp":"-10"},{"id":"1119","terrestrial_date":"2015-12-09","sol":"1188","ls":"79","season":"Month 3","min_temp":"-90","max_temp":"-31","pressure":"881","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:57","sunset":"17:40","local_uv_irradiance_index":"Moderate","min_gts_temp":"-86","max_gts_temp":"-11"},{"id":"1118","terrestrial_date":"2015-12-08","sol":"1187","ls":"78","season":"Month 3","min_temp":"-84","max_temp":"-29","pressure":"881","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:57","sunset":"17:40","local_uv_irradiance_index":"Moderate","min_gts_temp":"-91","max_gts_temp":"-8"},{"id":"1117","terrestrial_date":"2015-12-07","sol":"1186","ls":"78","season":"Month 3","min_temp":"-86","max_temp":"-28","pressure":"881","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:57","sunset":"17:40","local_uv_irradiance_index":"High","min_gts_temp":"-87","max_gts_temp":"-8"},{"id":"1116","terrestrial_date":"2015-12-06","sol":"1185","ls":"77","season":"Month 3","min_temp":"-84","max_temp":"-24","pressure":"881","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:58","sunset":"17:40","local_uv_irradiance_index":"High","min_gts_temp":"-94","max_gts_temp":"-3"},{"id":"1115","terrestrial_date":"2015-12-05","sol":"1184","ls":"77","season":"Month 3","min_temp":"-85","max_temp":"-28","pressure":"882","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:58","sunset":"17:41","local_uv_irradiance_index":"Moderate","min_gts_temp":"-94","max_gts_temp":"-4"},{"id":"1113","terrestrial_date":"2015-12-04","sol":"1183","ls":"76","season":"Month 3","min_temp":"-86","max_temp":"-26","pressure":"883","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:58","sunset":"17:41","local_uv_irradiance_index":"Moderate","min_gts_temp":"-91","max_gts_temp":"-5"},{"id":"1114","terrestrial_date":"2015-12-03","sol":"1182","ls":"76","season":"Month 3","min_temp":"-85","max_temp":"-29","pressure":"884","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:58","sunset":"17:41","local_uv_irradiance_index":"Moderate","min_gts_temp":"-91","max_gts_temp":"-6"},{"id":"1112","terrestrial_date":"2015-12-02","sol":"1181","ls":"75","season":"Month 3","min_temp":"-86","max_temp":"-24","pressure":"885","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:58","sunset":"17:41","local_uv_irradiance_index":"Moderate","min_gts_temp":"-86","max_gts_temp":"-7"},{"id":"1111","terrestrial_date":"2015-12-01","sol":"1180","ls":"75","season":"Month 3","min_temp":"-86","max_temp":"-27","pressure":"886","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:58","sunset":"17:41","local_uv_irradiance_index":"Moderate","min_gts_temp":"-87","max_gts_temp":"-11"},{"id":"1110","terrestrial_date":"2015-11-30","sol":"1179","ls":"75","season":"Month 3","min_temp":"-85","max_temp":"-25","pressure":"887","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:59","sunset":"17:42","local_uv_irradiance_index":"Moderate","min_gts_temp":"-88","max_gts_temp":"-12"},{"id":"1109","terrestrial_date":"2015-11-29","sol":"1178","ls":"74","season":"Month 3","min_temp":"-86","max_temp":"-26","pressure":"888","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:59","sunset":"17:42","local_uv_irradiance_index":"Moderate","min_gts_temp":"-88","max_gts_temp":"-13"},{"id":"1108","terrestrial_date":"2015-11-28","sol":"1177","ls":"74","season":"Month 3","min_temp":"-84","max_temp":"-27","pressure":"888","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:59","sunset":"17:42","local_uv_irradiance_index":"Moderate","min_gts_temp":"-89","max_gts_temp":"-12"},{"id":"1107","terrestrial_date":"2015-11-27","sol":"1176","ls":"73","season":"Month 3","min_temp":"-84","max_temp":"-31","pressure":"890","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:59","sunset":"17:42","local_uv_irradiance_index":"Moderate","min_gts_temp":"-89","max_gts_temp":"-12"},{"id":"1106","terrestrial_date":"2015-11-26","sol":"1175","ls":"73","season":"Month 3","min_temp":"-85","max_temp":"-23","pressure":"890","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:59","sunset":"17:43","local_uv_irradiance_index":"Moderate","min_gts_temp":"-86","max_gts_temp":"-11"},{"id":"1105","terrestrial_date":"2015-11-25","sol":"1174","ls":"72","season":"Month 3","min_temp":"-85","max_temp":"-25","pressure":"891","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:00","sunset":"17:43","local_uv_irradiance_index":"Moderate","min_gts_temp":"-86","max_gts_temp":"-8"},{"id":"1103","terrestrial_date":"2015-11-24","sol":"1173","ls":"72","season":"Month 3","min_temp":"-87","max_temp":"-31","pressure":"892","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:00","sunset":"17:43","local_uv_irradiance_index":"Moderate","min_gts_temp":"-83","max_gts_temp":"-14"},{"id":"1104","terrestrial_date":"2015-11-23","sol":"1172","ls":"71","season":"Month 3","min_temp":"-87","max_temp":"-26","pressure":"892","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:00","sunset":"17:43","local_uv_irradiance_index":"High","min_gts_temp":"-91","max_gts_temp":"-13"},{"id":"1099","terrestrial_date":"2015-11-22","sol":"1171","ls":"71","season":"Month 3","min_temp":"-87","max_temp":"-29","pressure":"892","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:00","sunset":"17:44","local_uv_irradiance_index":"High","min_gts_temp":"-90","max_gts_temp":"-15"},{"id":"1101","terrestrial_date":"2015-11-21","sol":"1170","ls":"71","season":"Month 3","min_temp":"-85","max_temp":"-31","pressure":"893","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:00","sunset":"17:44","local_uv_irradiance_index":"High","min_gts_temp":"-89","max_gts_temp":"-15"},{"id":"1100","terrestrial_date":"2015-11-20","sol":"1169","ls":"70","season":"Month 3","min_temp":"-89","max_temp":"-32","pressure":"894","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:00","sunset":"17:44","local_uv_irradiance_index":"High","min_gts_temp":"-89","max_gts_temp":"-16"},{"id":"1102","terrestrial_date":"2015-11-19","sol":"1168","ls":"70","season":"Month 3","min_temp":"-84","max_temp":"-29","pressure":"894","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:01","sunset":"17:44","local_uv_irradiance_index":"Moderate","min_gts_temp":"-86","max_gts_temp":"-10"},{"id":"1098","terrestrial_date":"2015-11-18","sol":"1167","ls":"69","season":"Month 3","min_temp":"-84","max_temp":"-25","pressure":"894","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:01","sunset":"17:45","local_uv_irradiance_index":"Moderate","min_gts_temp":"-85","max_gts_temp":"-13"},{"id":"1097","terrestrial_date":"2015-11-17","sol":"1166","ls":"69","season":"Month 3","min_temp":"-84","max_temp":"-31","pressure":"895","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:01","sunset":"17:45","local_uv_irradiance_index":"Moderate","min_gts_temp":"-78","max_gts_temp":"-12"},{"id":"1096","terrestrial_date":"2015-11-16","sol":"1165","ls":"68","season":"Month 3","min_temp":"-84","max_temp":"-35","pressure":"897","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:01","sunset":"17:45","local_uv_irradiance_index":"Moderate","min_gts_temp":"-89","max_gts_temp":"-61"},{"id":"1095","terrestrial_date":"2015-11-15","sol":"1164","ls":"68","season":"Month 3","min_temp":"-86","max_temp":"-35","pressure":"896","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:01","sunset":"17:45","local_uv_irradiance_index":"Moderate","min_gts_temp":"-88","max_gts_temp":"-14"},{"id":"1094","terrestrial_date":"2015-11-14","sol":"1163","ls":"67","season":"Month 3","min_temp":"-86","max_temp":"-29","pressure":"896","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:02","sunset":"17:46","local_uv_irradiance_index":"Moderate","min_gts_temp":"-87","max_gts_temp":"-12"},{"id":"1093","terrestrial_date":"2015-11-13","sol":"1162","ls":"67","season":"Month 3","min_temp":"-84","max_temp":"-27","pressure":"897","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:02","sunset":"17:46","local_uv_irradiance_index":"Moderate","min_gts_temp":"-84","max_gts_temp":"-12"},{"id":"1092","terrestrial_date":"2015-11-12","sol":"1161","ls":"66","season":"Month 3","min_temp":"-83","max_temp":"-26","pressure":"898","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:02","sunset":"17:46","local_uv_irradiance_index":"Moderate","min_gts_temp":"-84","max_gts_temp":"-13"},{"id":"1091","terrestrial_date":"2015-11-10","sol":"1160","ls":"66","season":"Month 3","min_temp":"-80","max_temp":"-28","pressure":"899","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:02","sunset":"17:46","local_uv_irradiance_index":"Moderate","min_gts_temp":"-88","max_gts_temp":"-15"},{"id":"1090","terrestrial_date":"2015-11-09","sol":"1159","ls":"66","season":"Month 3","min_temp":"-82","max_temp":"-28","pressure":"898","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:02","sunset":"17:47","local_uv_irradiance_index":"Moderate","min_gts_temp":"-84","max_gts_temp":"-14"},{"id":"1089","terrestrial_date":"2015-11-08","sol":"1158","ls":"65","season":"Month 3","min_temp":"-82","max_temp":"-28","pressure":"898","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:03","sunset":"17:47","local_uv_irradiance_index":"Moderate","min_gts_temp":"-87","max_gts_temp":"-12"},{"id":"1087","terrestrial_date":"2015-11-07","sol":"1157","ls":"65","season":"Month 3","min_temp":"-82","max_temp":"-32","pressure":"900","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:03","sunset":"17:47","local_uv_irradiance_index":"Moderate","min_gts_temp":"-84","max_gts_temp":"-14"},{"id":"1085","terrestrial_date":"2015-11-06","sol":"1156","ls":"64","season":"Month 3","min_temp":"-84","max_temp":"-30","pressure":"900","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:03","sunset":"17:47","local_uv_irradiance_index":"Moderate","min_gts_temp":"-85","max_gts_temp":"-13"},{"id":"1084","terrestrial_date":"2015-11-05","sol":"1155","ls":"64","season":"Month 3","min_temp":"-84","max_temp":"-26","pressure":"900","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:03","sunset":"17:48","local_uv_irradiance_index":"Moderate","min_gts_temp":"-83","max_gts_temp":"-13"},{"id":"1088","terrestrial_date":"2015-11-04","sol":"1154","ls":"63","season":"Month 3","min_temp":"-84","max_temp":"-29","pressure":"900","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:04","sunset":"17:48","local_uv_irradiance_index":"Moderate","min_gts_temp":"-83","max_gts_temp":"-13"},{"id":"1083","terrestrial_date":"2015-11-03","sol":"1153","ls":"63","season":"Month 3","min_temp":"-82","max_temp":"-31","pressure":"900","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:04","sunset":"17:48","local_uv_irradiance_index":"Moderate","min_gts_temp":"-83","max_gts_temp":"-16"},{"id":"1086","terrestrial_date":"2015-11-02","sol":"1152","ls":"62","season":"Month 3","min_temp":"-80","max_temp":"-30","pressure":"901","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:04","sunset":"17:49","local_uv_irradiance_index":"Moderate","min_gts_temp":"-85","max_gts_temp":"-17"},{"id":"1082","terrestrial_date":"2015-11-01","sol":"1151","ls":"62","season":"Month 3","min_temp":"-82","max_temp":"-31","pressure":"901","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:04","sunset":"17:49","local_uv_irradiance_index":"Moderate","min_gts_temp":"-78","max_gts_temp":"-17"},{"id":"1081","terrestrial_date":"2015-10-31","sol":"1150","ls":"62","season":"Month 3","min_temp":"-82","max_temp":"-29","pressure":"902","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:04","sunset":"17:49","local_uv_irradiance_index":"Moderate","min_gts_temp":"-86","max_gts_temp":"-17"},{"id":"1079","terrestrial_date":"2015-10-30","sol":"1149","ls":"61","season":"Month 3","min_temp":"-81","max_temp":"-23","pressure":"902","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:05","sunset":"17:49","local_uv_irradiance_index":"Moderate","min_gts_temp":"-83","max_gts_temp":"-17"},{"id":"1080","terrestrial_date":"2015-10-29","sol":"1148","ls":"61","season":"Month 3","min_temp":"-80","max_temp":"-28","pressure":"901","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:05","sunset":"17:50","local_uv_irradiance_index":"Moderate","min_gts_temp":"-83","max_gts_temp":"-15"},{"id":"1078","terrestrial_date":"2015-10-28","sol":"1147","ls":"60","season":"Month 3","min_temp":"-80","max_temp":"-29","pressure":"901","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:05","sunset":"17:50","local_uv_irradiance_index":"Moderate","min_gts_temp":"-85","max_gts_temp":"-17"},{"id":"1077","terrestrial_date":"2015-10-27","sol":"1146","ls":"60","season":"Month 3","min_temp":"-82","max_temp":"-28","pressure":"902","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:05","sunset":"17:50","local_uv_irradiance_index":"Moderate","min_gts_temp":"-83","max_gts_temp":"-16"},{"id":"1076","terrestrial_date":"2015-10-26","sol":"1145","ls":"59","season":"Month 2","min_temp":"-82","max_temp":"-29","pressure":"903","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:06","sunset":"17:51","local_uv_irradiance_index":"Moderate","min_gts_temp":"-84","max_gts_temp":"-12"},{"id":"1075","terrestrial_date":"2015-10-25","sol":"1144","ls":"59","season":"Month 2","min_temp":"-82","max_temp":"-28","pressure":"902","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:06","sunset":"17:51","local_uv_irradiance_index":"Moderate","min_gts_temp":"-78","max_gts_temp":"-10"},{"id":"1074","terrestrial_date":"2015-10-24","sol":"1143","ls":"58","season":"Month 2","min_temp":"-82","max_temp":"-29","pressure":"901","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:06","sunset":"17:51","local_uv_irradiance_index":"Moderate","min_gts_temp":"-78","max_gts_temp":"-17"},{"id":"1073","terrestrial_date":"2015-10-23","sol":"1142","ls":"58","season":"Month 2","min_temp":"-82","max_temp":"-23","pressure":"902","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:06","sunset":"17:52","local_uv_irradiance_index":"Moderate","min_gts_temp":"-79","max_gts_temp":"-14"},{"id":"1072","terrestrial_date":"2015-10-22","sol":"1141","ls":"57","season":"Month 2","min_temp":"-82","max_temp":"-28","pressure":"902","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:06","sunset":"17:52","local_uv_irradiance_index":"Moderate","min_gts_temp":"-80","max_gts_temp":"-12"},{"id":"1071","terrestrial_date":"2015-10-21","sol":"1140","ls":"57","season":"Month 2","min_temp":"-81","max_temp":"-26","pressure":"901","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:07","sunset":"17:52","local_uv_irradiance_index":"Moderate","min_gts_temp":"-80","max_gts_temp":"-16"},{"id":"1070","terrestrial_date":"2015-10-20","sol":"1139","ls":"57","season":"Month 2","min_temp":"-80","max_temp":"-25","pressure":"901","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:07","sunset":"17:53","local_uv_irradiance_index":"Moderate","min_gts_temp":"-80","max_gts_temp":"-14"},{"id":"1067","terrestrial_date":"2015-10-19","sol":"1138","ls":"56","season":"Month 2","min_temp":"-82","max_temp":"-29","pressure":"901","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:07","sunset":"17:53","local_uv_irradiance_index":"Moderate","min_gts_temp":"-78","max_gts_temp":"-15"},{"id":"1068","terrestrial_date":"2015-10-18","sol":"1137","ls":"56","season":"Month 2","min_temp":"-81","max_temp":"-26","pressure":"901","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:07","sunset":"17:53","local_uv_irradiance_index":"Moderate","min_gts_temp":"-79","max_gts_temp":"-14"},{"id":"1066","terrestrial_date":"2015-10-17","sol":"1136","ls":"55","season":"Month 2","min_temp":"-82","max_temp":"-23","pressure":"901","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:08","sunset":"17:53","local_uv_irradiance_index":"Moderate","min_gts_temp":"-74","max_gts_temp":"-14"},{"id":"1069","terrestrial_date":"2015-10-16","sol":"1135","ls":"55","season":"Month 2","min_temp":"-81","max_temp":"-27","pressure":"901","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:08","sunset":"17:54","local_uv_irradiance_index":"Moderate","min_gts_temp":"-81","max_gts_temp":"-15"},{"id":"1065","terrestrial_date":"2015-10-15","sol":"1134","ls":"54","season":"Month 2","min_temp":"-80","max_temp":"-27","pressure":"902","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:08","sunset":"17:54","local_uv_irradiance_index":"Moderate","min_gts_temp":"-81","max_gts_temp":"-14"},{"id":"1064","terrestrial_date":"2015-10-14","sol":"1133","ls":"54","season":"Month 2","min_temp":"-82","max_temp":"-26","pressure":"902","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:08","sunset":"17:54","local_uv_irradiance_index":"Moderate","min_gts_temp":"-78","max_gts_temp":"-14"},{"id":"1063","terrestrial_date":"2015-10-13","sol":"1132","ls":"53","season":"Month 2","min_temp":"-82","max_temp":"-27","pressure":"902","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:09","sunset":"17:55","local_uv_irradiance_index":"Moderate","min_gts_temp":"-77","max_gts_temp":"-14"},{"id":"1062","terrestrial_date":"2015-10-12","sol":"1131","ls":"53","season":"Month 2","min_temp":"-81","max_temp":"-28","pressure":"902","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:09","sunset":"17:55","local_uv_irradiance_index":"Moderate","min_gts_temp":"-77","max_gts_temp":"-14"},{"id":"1061","terrestrial_date":"2015-10-11","sol":"1130","ls":"53","season":"Month 2","min_temp":"-81","max_temp":"-25","pressure":"902","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:09","sunset":"17:56","local_uv_irradiance_index":"Moderate","min_gts_temp":"-78","max_gts_temp":"-13"},{"id":"1060","terrestrial_date":"2015-10-10","sol":"1129","ls":"52","season":"Month 2","min_temp":"-80","max_temp":"-23","pressure":"902","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:09","sunset":"17:56","local_uv_irradiance_index":"Moderate","min_gts_temp":"-78","max_gts_temp":"-14"},{"id":"1059","terrestrial_date":"2015-10-09","sol":"1128","ls":"52","season":"Month 2","min_temp":"-79","max_temp":"-27","pressure":"902","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:10","sunset":"17:56","local_uv_irradiance_index":"Moderate","min_gts_temp":"-78","max_gts_temp":"-13"},{"id":"1058","terrestrial_date":"2015-10-08","sol":"1127","ls":"51","season":"Month 2","min_temp":"-80","max_temp":"-27","pressure":"901","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:10","sunset":"17:57","local_uv_irradiance_index":"Moderate","min_gts_temp":"-77","max_gts_temp":"-13"},{"id":"1057","terrestrial_date":"2015-10-07","sol":"1126","ls":"51","season":"Month 2","min_temp":"-79","max_temp":"-21","pressure":"901","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:10","sunset":"17:57","local_uv_irradiance_index":"Moderate","min_gts_temp":"-78","max_gts_temp":"-12"},{"id":"1056","terrestrial_date":"2015-10-06","sol":"1125","ls":"50","season":"Month 2","min_temp":"-79","max_temp":"-22","pressure":"900","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:10","sunset":"17:57","local_uv_irradiance_index":"High","min_gts_temp":"-78","max_gts_temp":"-13"},{"id":"1055","terrestrial_date":"2015-10-04","sol":"1124","ls":"50","season":"Month 2","min_temp":"-80","max_temp":"-26","pressure":"900","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:11","sunset":"17:58","local_uv_irradiance_index":"High","min_gts_temp":"-78","max_gts_temp":"-12"},{"id":"1054","terrestrial_date":"2015-10-03","sol":"1123","ls":"49","season":"Month 2","min_temp":"-79","max_temp":"-27","pressure":"900","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:11","sunset":"17:58","local_uv_irradiance_index":"Moderate","min_gts_temp":"-78","max_gts_temp":"-12"},{"id":"1053","terrestrial_date":"2015-10-02","sol":"1122","ls":"49","season":"Month 2","min_temp":"-79","max_temp":"-26","pressure":"901","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:11","sunset":"17:58","local_uv_irradiance_index":"High","min_gts_temp":"-78","max_gts_temp":"-13"},{"id":"1052","terrestrial_date":"2015-10-01","sol":"1121","ls":"48","season":"Month 2","min_temp":"-81","max_temp":"-25","pressure":"901","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:12","sunset":"17:59","local_uv_irradiance_index":"Moderate","min_gts_temp":"-77","max_gts_temp":"-13"},{"id":"1051","terrestrial_date":"2015-09-30","sol":"1120","ls":"48","season":"Month 2","min_temp":"-80","max_temp":"-24","pressure":"900","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:12","sunset":"17:59","local_uv_irradiance_index":"Moderate","min_gts_temp":"-75","max_gts_temp":"-11"},{"id":"1050","terrestrial_date":"2015-09-29","sol":"1119","ls":"48","season":"Month 2","min_temp":"-81","max_temp":"-22","pressure":"899","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:12","sunset":"17:59","local_uv_irradiance_index":"Moderate","min_gts_temp":"-76","max_gts_temp":"-12"},{"id":"1049","terrestrial_date":"2015-09-28","sol":"1118","ls":"47","season":"Month 2","min_temp":"-80","max_temp":"-25","pressure":"898","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:12","sunset":"18:00","local_uv_irradiance_index":"Moderate","min_gts_temp":"-76","max_gts_temp":"-13"},{"id":"1048","terrestrial_date":"2015-09-27","sol":"1117","ls":"47","season":"Month 2","min_temp":"-79","max_temp":"-25","pressure":"898","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:13","sunset":"18:00","local_uv_irradiance_index":"Moderate","min_gts_temp":"-78","max_gts_temp":"-12"},{"id":"1046","terrestrial_date":"2015-09-26","sol":"1116","ls":"46","season":"Month 2","min_temp":"-79","max_temp":"-26","pressure":"898","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:13","sunset":"18:01","local_uv_irradiance_index":"Moderate","min_gts_temp":"-77","max_gts_temp":"-13"},{"id":"1047","terrestrial_date":"2015-09-25","sol":"1115","ls":"46","season":"Month 2","min_temp":"-80","max_temp":"-21","pressure":"897","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:13","sunset":"18:01","local_uv_irradiance_index":"Moderate","min_gts_temp":"-76","max_gts_temp":"-11"},{"id":"1045","terrestrial_date":"2015-09-24","sol":"1114","ls":"45","season":"Month 2","min_temp":"-79","max_temp":"-25","pressure":"896","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:13","sunset":"18:01","local_uv_irradiance_index":"Moderate","min_gts_temp":"-75","max_gts_temp":"-12"},{"id":"1044","terrestrial_date":"2015-09-23","sol":"1113","ls":"45","season":"Month 2","min_temp":"-79","max_temp":"-24","pressure":"895","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:14","sunset":"18:02","local_uv_irradiance_index":"Moderate","min_gts_temp":"-74","max_gts_temp":"-12"},{"id":"1043","terrestrial_date":"2015-09-22","sol":"1112","ls":"44","season":"Month 2","min_temp":"-80","max_temp":"-24","pressure":"895","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:14","sunset":"18:02","local_uv_irradiance_index":"High","min_gts_temp":"-78","max_gts_temp":"-10"},{"id":"1042","terrestrial_date":"2015-09-21","sol":"1111","ls":"44","season":"Month 2","min_temp":"-79","max_temp":"-26","pressure":"896","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:14","sunset":"18:02","local_uv_irradiance_index":"High","min_gts_temp":"-79","max_gts_temp":"-13"},{"id":"1041","terrestrial_date":"2015-09-20","sol":"1110","ls":"43","season":"Month 2","min_temp":"-81","max_temp":"-25","pressure":"895","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:15","sunset":"18:03","local_uv_irradiance_index":"Moderate","min_gts_temp":"-82","max_gts_temp":"-8"},{"id":"1039","terrestrial_date":"2015-09-19","sol":"1109","ls":"43","season":"Month 2","min_temp":"-82","max_temp":"-26","pressure":"895","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:15","sunset":"18:03","local_uv_irradiance_index":"Moderate","min_gts_temp":"-79","max_gts_temp":"-7"},{"id":"1038","terrestrial_date":"2015-09-18","sol":"1108","ls":"42","season":"Month 2","min_temp":"-78","max_temp":"-25","pressure":"895","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:15","sunset":"18:04","local_uv_irradiance_index":"Moderate","min_gts_temp":"-81","max_gts_temp":"-6"},{"id":"1040","terrestrial_date":"2015-09-17","sol":"1107","ls":"42","season":"Month 2","min_temp":"-79","max_temp":"-24","pressure":"893","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:15","sunset":"18:04","local_uv_irradiance_index":"Moderate","min_gts_temp":"-80","max_gts_temp":"-7"},{"id":"1037","terrestrial_date":"2015-09-16","sol":"1106","ls":"42","season":"Month 2","min_temp":"-82","max_temp":"-22","pressure":"893","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:16","sunset":"18:04","local_uv_irradiance_index":"Moderate","min_gts_temp":"-82","max_gts_temp":"-8"},{"id":"1036","terrestrial_date":"2015-09-15","sol":"1105","ls":"41","season":"Month 2","min_temp":"-78","max_temp":"-25","pressure":"892","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:16","sunset":"18:05","local_uv_irradiance_index":"Moderate","min_gts_temp":"-83","max_gts_temp":"-9"},{"id":"1035","terrestrial_date":"2015-09-14","sol":"1104","ls":"41","season":"Month 2","min_temp":"-79","max_temp":"-25","pressure":"893","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:16","sunset":"18:05","local_uv_irradiance_index":"Moderate","min_gts_temp":"-84","max_gts_temp":"-6"},{"id":"1033","terrestrial_date":"2015-09-13","sol":"1103","ls":"40","season":"Month 2","min_temp":"-81","max_temp":"-26","pressure":"893","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:17","sunset":"18:06","local_uv_irradiance_index":"Moderate","min_gts_temp":"-81","max_gts_temp":"-5"},{"id":"1032","terrestrial_date":"2015-09-12","sol":"1102","ls":"40","season":"Month 2","min_temp":"-84","max_temp":"-25","pressure":"892","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:17","sunset":"18:06","local_uv_irradiance_index":"Moderate","min_gts_temp":"-83","max_gts_temp":"-6"},{"id":"1034","terrestrial_date":"2015-09-11","sol":"1101","ls":"39","season":"Month 2","min_temp":"-80","max_temp":"-23","pressure":"891","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:17","sunset":"18:06","local_uv_irradiance_index":"Moderate","min_gts_temp":"-81","max_gts_temp":"-4"},{"id":"1031","terrestrial_date":"2015-09-10","sol":"1100","ls":"39","season":"Month 2","min_temp":"-79","max_temp":"-22","pressure":"890","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:17","sunset":"18:07","local_uv_irradiance_index":"Moderate","min_gts_temp":"-85","max_gts_temp":"-8"},{"id":"1030","terrestrial_date":"2015-09-09","sol":"1099","ls":"38","season":"Month 2","min_temp":"-78","max_temp":"-24","pressure":"891","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:18","sunset":"18:07","local_uv_irradiance_index":"Moderate","min_gts_temp":"-80","max_gts_temp":"-7"},{"id":"1029","terrestrial_date":"2015-09-08","sol":"1098","ls":"38","season":"Month 2","min_temp":"-77","max_temp":"-26","pressure":"890","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:18","sunset":"18:08","local_uv_irradiance_index":"Moderate","min_gts_temp":"-79","max_gts_temp":"-10"},{"id":"1028","terrestrial_date":"2015-09-07","sol":"1097","ls":"37","season":"Month 2","min_temp":"-78","max_temp":"-27","pressure":"889","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:18","sunset":"18:08","local_uv_irradiance_index":"Moderate","min_gts_temp":"-79","max_gts_temp":"-8"},{"id":"1025","terrestrial_date":"2015-09-06","sol":"1096","ls":"37","season":"Month 2","min_temp":"-79","max_temp":"-24","pressure":"888","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:19","sunset":"18:08","local_uv_irradiance_index":"Moderate","min_gts_temp":"-78","max_gts_temp":"-7"},{"id":"1027","terrestrial_date":"2015-09-05","sol":"1095","ls":"36","season":"Month 2","min_temp":"-79","max_temp":"-24","pressure":"888","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:19","sunset":"18:09","local_uv_irradiance_index":"Moderate","min_gts_temp":"-77","max_gts_temp":"-8"},{"id":"1026","terrestrial_date":"2015-09-04","sol":"1094","ls":"36","season":"Month 2","min_temp":"-80","max_temp":"-20","pressure":"888","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:19","sunset":"18:09","local_uv_irradiance_index":"High","min_gts_temp":"-75","max_gts_temp":"-7"},{"id":"1024","terrestrial_date":"2015-09-03","sol":"1093","ls":"36","season":"Month 2","min_temp":"-77","max_temp":"-21","pressure":"887","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:20","sunset":"18:10","local_uv_irradiance_index":"High","min_gts_temp":"-79","max_gts_temp":"-4"},{"id":"1023","terrestrial_date":"2015-09-02","sol":"1092","ls":"35","season":"Month 2","min_temp":"-79","max_temp":"-23","pressure":"886","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:20","sunset":"18:10","local_uv_irradiance_index":"High","min_gts_temp":"-78","max_gts_temp":"-8"},{"id":"1022","terrestrial_date":"2015-09-01","sol":"1091","ls":"35","season":"Month 2","min_temp":"-77","max_temp":"-21","pressure":"886","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:20","sunset":"18:10","local_uv_irradiance_index":"High","min_gts_temp":"-77","max_gts_temp":"-7"},{"id":"1021","terrestrial_date":"2015-08-31","sol":"1090","ls":"34","season":"Month 2","min_temp":"-78","max_temp":"-20","pressure":"886","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:20","sunset":"18:11","local_uv_irradiance_index":"High","min_gts_temp":"-76","max_gts_temp":"-6"},{"id":"1018","terrestrial_date":"2015-08-30","sol":"1089","ls":"34","season":"Month 2","min_temp":"-76","max_temp":"-19","pressure":"885","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:21","sunset":"18:11","local_uv_irradiance_index":"High","min_gts_temp":"-76","max_gts_temp":"-7"},{"id":"1020","terrestrial_date":"2015-08-29","sol":"1088","ls":"33","season":"Month 2","min_temp":"-77","max_temp":"-19","pressure":"885","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:21","sunset":"18:12","local_uv_irradiance_index":"High","min_gts_temp":"-76","max_gts_temp":"-7"},{"id":"1019","terrestrial_date":"2015-08-27","sol":"1087","ls":"33","season":"Month 2","min_temp":"-78","max_temp":"-19","pressure":"885","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:21","sunset":"18:12","local_uv_irradiance_index":"High","min_gts_temp":"-83","max_gts_temp":"-3"},{"id":"1016","terrestrial_date":"2015-08-26","sol":"1086","ls":"32","season":"Month 2","min_temp":"-77","max_temp":"-25","pressure":"885","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:22","sunset":"18:13","local_uv_irradiance_index":"High","min_gts_temp":"-82","max_gts_temp":"-2"},{"id":"1017","terrestrial_date":"2015-08-25","sol":"1085","ls":"32","season":"Month 2","min_temp":"-79","max_temp":"-23","pressure":"886","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:22","sunset":"18:13","local_uv_irradiance_index":"Moderate","min_gts_temp":"-78","max_gts_temp":"-10"},{"id":"1015","terrestrial_date":"2015-08-24","sol":"1084","ls":"31","season":"Month 2","min_temp":"-78","max_temp":"-22","pressure":"885","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:22","sunset":"18:13","local_uv_irradiance_index":"High","min_gts_temp":"-77","max_gts_temp":"-9"},{"id":"1014","terrestrial_date":"2015-08-23","sol":"1083","ls":"31","season":"Month 2","min_temp":"-80","max_temp":"-20","pressure":"883","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:23","sunset":"18:14","local_uv_irradiance_index":"Moderate","min_gts_temp":"-80","max_gts_temp":"-7"},{"id":"1012","terrestrial_date":"2015-08-22","sol":"1082","ls":"30","season":"Month 2","min_temp":"-79","max_temp":"-17","pressure":"882","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:23","sunset":"18:14","local_uv_irradiance_index":"Moderate","min_gts_temp":"-81","max_gts_temp":"-7"},{"id":"1011","terrestrial_date":"2015-08-21","sol":"1081","ls":"30","season":"Month 2","min_temp":"-77","max_temp":"-18","pressure":"882","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:23","sunset":"18:15","local_uv_irradiance_index":"Moderate","min_gts_temp":"-82","max_gts_temp":"-7"},{"id":"1013","terrestrial_date":"2015-08-20","sol":"1080","ls":"29","season":"Month 1","min_temp":"-77","max_temp":"-17","pressure":"882","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:23","sunset":"18:15","local_uv_irradiance_index":"Moderate","min_gts_temp":"-84","max_gts_temp":"-3"},{"id":"1010","terrestrial_date":"2015-08-19","sol":"1079","ls":"29","season":"Month 1","min_temp":"-80","max_temp":"-21","pressure":"882","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:24","sunset":"18:16","local_uv_irradiance_index":"Moderate","min_gts_temp":"-83","max_gts_temp":"-3"},{"id":"1009","terrestrial_date":"2015-08-18","sol":"1078","ls":"29","season":"Month 1","min_temp":"-80","max_temp":"-18","pressure":"881","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:24","sunset":"18:16","local_uv_irradiance_index":"Moderate","min_gts_temp":"-82","max_gts_temp":"1"},{"id":"1008","terrestrial_date":"2015-08-17","sol":"1077","ls":"28","season":"Month 1","min_temp":"-79","max_temp":"-16","pressure":"880","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:24","sunset":"18:16","local_uv_irradiance_index":"Moderate","min_gts_temp":"-81","max_gts_temp":"-2"},{"id":"1007","terrestrial_date":"2015-08-16","sol":"1076","ls":"28","season":"Month 1","min_temp":"-81","max_temp":"-14","pressure":"880","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:25","sunset":"18:17","local_uv_irradiance_index":"Moderate","min_gts_temp":"-81","max_gts_temp":"-1"},{"id":"1006","terrestrial_date":"2015-08-15","sol":"1075","ls":"27","season":"Month 1","min_temp":"-77","max_temp":"-13","pressure":"879","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:25","sunset":"18:17","local_uv_irradiance_index":"Moderate","min_gts_temp":"-82","max_gts_temp":"-3"},{"id":"1005","terrestrial_date":"2015-08-14","sol":"1074","ls":"27","season":"Month 1","min_temp":"-77","max_temp":"-14","pressure":"878","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:25","sunset":"18:18","local_uv_irradiance_index":"High","min_gts_temp":"-87","max_gts_temp":"-2"},{"id":"1004","terrestrial_date":"2015-08-13","sol":"1073","ls":"26","season":"Month 1","min_temp":"-82","max_temp":"-15","pressure":"879","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:26","sunset":"18:18","local_uv_irradiance_index":"Moderate","min_gts_temp":"-77","max_gts_temp":"1"},{"id":"1003","terrestrial_date":"2015-08-12","sol":"1072","ls":"26","season":"Month 1","min_temp":"-78","max_temp":"-15","pressure":"879","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:26","sunset":"18:19","local_uv_irradiance_index":"High","min_gts_temp":"-79","max_gts_temp":"2"},{"id":"1002","terrestrial_date":"2015-08-11","sol":"1071","ls":"25","season":"Month 1","min_temp":"-78","max_temp":"-16","pressure":"878","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:26","sunset":"18:19","local_uv_irradiance_index":"High","min_gts_temp":"-68","max_gts_temp":"-4"},{"id":"1001","terrestrial_date":"2015-08-10","sol":"1070","ls":"25","season":"Month 1","min_temp":"-80","max_temp":"-14","pressure":"877","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:27","sunset":"18:19","local_uv_irradiance_index":"High","min_gts_temp":"-26","max_gts_temp":"-5"},{"id":"999","terrestrial_date":"2015-08-09","sol":"1069","ls":"24","season":"Month 1","min_temp":"-77","max_temp":"-22","pressure":"876","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:27","sunset":"18:20","local_uv_irradiance_index":"High","min_gts_temp":"-79","max_gts_temp":"-4"},{"id":"1000","terrestrial_date":"2015-08-08","sol":"1068","ls":"24","season":"Month 1","min_temp":"-76","max_temp":"-23","pressure":"874","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:27","sunset":"18:20","local_uv_irradiance_index":"High","min_gts_temp":"-82","max_gts_temp":"-3"},{"id":"998","terrestrial_date":"2015-08-07","sol":"1067","ls":"23","season":"Month 1","min_temp":"-79","max_temp":"-24","pressure":"875","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:27","sunset":"18:21","local_uv_irradiance_index":"Moderate","min_gts_temp":"-87","max_gts_temp":"1"},{"id":"997","terrestrial_date":"2015-08-06","sol":"1066","ls":"23","season":"Month 1","min_temp":"-78","max_temp":"-14","pressure":"875","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:28","sunset":"18:21","local_uv_irradiance_index":"High","min_gts_temp":"-79","max_gts_temp":"0"},{"id":"995","terrestrial_date":"2015-08-05","sol":"1065","ls":"22","season":"Month 1","min_temp":"-78","max_temp":"-19","pressure":"873","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:28","sunset":"18:22","local_uv_irradiance_index":"High","min_gts_temp":"-78","max_gts_temp":"2"},{"id":"996","terrestrial_date":"2015-08-04","sol":"1064","ls":"22","season":"Month 1","min_temp":"-77","max_temp":"-22","pressure":"871","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:28","sunset":"18:22","local_uv_irradiance_index":"High","min_gts_temp":"-77","max_gts_temp":"2"},{"id":"994","terrestrial_date":"2015-08-03","sol":"1063","ls":"21","season":"Month 1","min_temp":"-79","max_temp":"-19","pressure":"871","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:29","sunset":"18:22","local_uv_irradiance_index":"High","min_gts_temp":"-77","max_gts_temp":"1"},{"id":"991","terrestrial_date":"2015-08-02","sol":"1062","ls":"21","season":"Month 1","min_temp":"-77","max_temp":"-24","pressure":"871","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:29","sunset":"18:23","local_uv_irradiance_index":"High","min_gts_temp":"-80","max_gts_temp":"2"},{"id":"993","terrestrial_date":"2015-08-01","sol":"1061","ls":"20","season":"Month 1","min_temp":"-77","max_temp":"-23","pressure":"871","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:29","sunset":"18:23","local_uv_irradiance_index":"High","min_gts_temp":"-78","max_gts_temp":"3"},{"id":"992","terrestrial_date":"2015-07-31","sol":"1060","ls":"20","season":"Month 1","min_temp":"-78","max_temp":"-16","pressure":"870","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:30","sunset":"18:24","local_uv_irradiance_index":"High","min_gts_temp":"-78","max_gts_temp":"2"},{"id":"990","terrestrial_date":"2015-07-30","sol":"1059","ls":"19","season":"Month 1","min_temp":"-78","max_temp":"-20","pressure":"870","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:30","sunset":"18:24","local_uv_irradiance_index":"High","min_gts_temp":"-77","max_gts_temp":"2"},{"id":"989","terrestrial_date":"2015-07-29","sol":"1058","ls":"19","season":"Month 1","min_temp":"-78","max_temp":"-22","pressure":"868","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:30","sunset":"18:25","local_uv_irradiance_index":"High","min_gts_temp":"-78","max_gts_temp":"1"},{"id":"988","terrestrial_date":"2015-07-28","sol":"1057","ls":"18","season":"Month 1","min_temp":"-78","max_temp":"-22","pressure":"867","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:30","sunset":"18:25","local_uv_irradiance_index":"High","min_gts_temp":"-80","max_gts_temp":"2"},{"id":"986","terrestrial_date":"2015-07-27","sol":"1056","ls":"18","season":"Month 1","min_temp":"-76","max_temp":"-21","pressure":"868","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:31","sunset":"18:25","local_uv_irradiance_index":"High","min_gts_temp":"-91","max_gts_temp":"4"},{"id":"987","terrestrial_date":"2015-07-26","sol":"1055","ls":"17","season":"Month 1","min_temp":"-77","max_temp":"-20","pressure":"868","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:31","sunset":"18:26","local_uv_irradiance_index":"High","min_gts_temp":"-90","max_gts_temp":"6"},{"id":"985","terrestrial_date":"2015-07-25","sol":"1054","ls":"17","season":"Month 1","min_temp":"-77","max_temp":"-11","pressure":"867","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:31","sunset":"18:26","local_uv_irradiance_index":"High","min_gts_temp":"-89","max_gts_temp":"6"},{"id":"984","terrestrial_date":"2015-07-24","sol":"1053","ls":"17","season":"Month 1","min_temp":"-79","max_temp":"-11","pressure":"865","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:32","sunset":"18:27","local_uv_irradiance_index":"High","min_gts_temp":"-84","max_gts_temp":"8"},{"id":"982","terrestrial_date":"2015-07-23","sol":"1052","ls":"16","season":"Month 1","min_temp":"-78","max_temp":"-12","pressure":"865","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:32","sunset":"18:27","local_uv_irradiance_index":"High","min_gts_temp":"-84","max_gts_temp":"1"},{"id":"983","terrestrial_date":"2015-07-21","sol":"1051","ls":"16","season":"Month 1","min_temp":"-76","max_temp":"-20","pressure":"864","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:32","sunset":"18:28","local_uv_irradiance_index":"High","min_gts_temp":"-91","max_gts_temp":"4"},{"id":"981","terrestrial_date":"2015-07-20","sol":"1050","ls":"15","season":"Month 1","min_temp":"-76","max_temp":"-22","pressure":"863","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:33","sunset":"18:28","local_uv_irradiance_index":"High","min_gts_temp":"-92","max_gts_temp":"6"},{"id":"980","terrestrial_date":"2015-07-19","sol":"1049","ls":"15","season":"Month 1","min_temp":"-75","max_temp":"-21","pressure":"864","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:33","sunset":"18:28","local_uv_irradiance_index":"High","min_gts_temp":"-83","max_gts_temp":"5"},{"id":"976","terrestrial_date":"2015-07-18","sol":"1048","ls":"14","season":"Month 1","min_temp":"-78","max_temp":"-15","pressure":"864","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:33","sunset":"18:29","local_uv_irradiance_index":"High","min_gts_temp":"-80","max_gts_temp":"2"},{"id":"977","terrestrial_date":"2015-07-17","sol":"1047","ls":"14","season":"Month 1","min_temp":"-77","max_temp":"-14","pressure":"863","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:33","sunset":"18:29","local_uv_irradiance_index":"High","min_gts_temp":"-81","max_gts_temp":"2"},{"id":"979","terrestrial_date":"2015-07-16","sol":"1046","ls":"13","season":"Month 1","min_temp":"-77","max_temp":"-12","pressure":"862","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:34","sunset":"18:30","local_uv_irradiance_index":"High","min_gts_temp":"-81","max_gts_temp":"2"},{"id":"978","terrestrial_date":"2015-07-15","sol":"1045","ls":"13","season":"Month 1","min_temp":"-80","max_temp":"-13","pressure":"861","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:34","sunset":"18:30","local_uv_irradiance_index":"High","min_gts_temp":"-82","max_gts_temp":"1"},{"id":"975","terrestrial_date":"2015-07-14","sol":"1044","ls":"12","season":"Month 1","min_temp":"-79","max_temp":"-20","pressure":"860","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:34","sunset":"18:31","local_uv_irradiance_index":"Moderate","min_gts_temp":"-76","max_gts_temp":"-3"},{"id":"974","terrestrial_date":"2015-07-13","sol":"1043","ls":"12","season":"Month 1","min_temp":"-77","max_temp":"-20","pressure":"860","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:35","sunset":"18:31","local_uv_irradiance_index":"Moderate","min_gts_temp":"-76","max_gts_temp":"-3"},{"id":"973","terrestrial_date":"2015-07-12","sol":"1042","ls":"11","season":"Month 1","min_temp":"-76","max_temp":"-20","pressure":"859","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:35","sunset":"18:31","local_uv_irradiance_index":"Moderate","min_gts_temp":"-83","max_gts_temp":"-4"},{"id":"971","terrestrial_date":"2015-07-11","sol":"1041","ls":"11","season":"Month 1","min_temp":"-77","max_temp":"-22","pressure":"859","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:35","sunset":"18:32","local_uv_irradiance_index":"High","min_gts_temp":"-83","max_gts_temp":"0"},{"id":"972","terrestrial_date":"2015-07-10","sol":"1040","ls":"10","season":"Month 1","min_temp":"-77","max_temp":"-21","pressure":"858","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:35","sunset":"18:32","local_uv_irradiance_index":"High","min_gts_temp":"-82","max_gts_temp":"0"},{"id":"970","terrestrial_date":"2015-07-09","sol":"1039","ls":"10","season":"Month 1","min_temp":"-77","max_temp":"-21","pressure":"857","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:36","sunset":"18:33","local_uv_irradiance_index":"High","min_gts_temp":"-84","max_gts_temp":"-1"},{"id":"969","terrestrial_date":"2015-07-08","sol":"1038","ls":"9","season":"Month 1","min_temp":"-76","max_temp":"-21","pressure":"858","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:36","sunset":"18:33","local_uv_irradiance_index":"High","min_gts_temp":"-84","max_gts_temp":"-1"},{"id":"968","terrestrial_date":"2015-07-07","sol":"1037","ls":"9","season":"Month 1","min_temp":"-80","max_temp":"-21","pressure":"856","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:36","sunset":"18:33","local_uv_irradiance_index":"High","min_gts_temp":"-84","max_gts_temp":"-1"},{"id":"967","terrestrial_date":"2015-07-06","sol":"1036","ls":"8","season":"Month 1","min_temp":"-76","max_temp":"-17","pressure":"856","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:36","sunset":"18:34","local_uv_irradiance_index":"High","min_gts_temp":"-82","max_gts_temp":"4"},{"id":"966","terrestrial_date":"2015-07-05","sol":"1035","ls":"8","season":"Month 1","min_temp":"-77","max_temp":"-12","pressure":"857","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:37","sunset":"18:34","local_uv_irradiance_index":"High","min_gts_temp":"-77","max_gts_temp":"6"},{"id":"964","terrestrial_date":"2015-07-04","sol":"1034","ls":"7","season":"Month 1","min_temp":"-76","max_temp":"-14","pressure":"859","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:37","sunset":"18:35","local_uv_irradiance_index":"High","min_gts_temp":"-77","max_gts_temp":"-1"},{"id":"965","terrestrial_date":"2015-07-03","sol":"1033","ls":"7","season":"Month 1","min_temp":"-77","max_temp":"-14","pressure":"856","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:37","sunset":"18:35","local_uv_irradiance_index":"High","min_gts_temp":"-79","max_gts_temp":"-2"},{"id":"962","terrestrial_date":"2015-07-02","sol":"1032","ls":"6","season":"Month 1","min_temp":"-74","max_temp":"-16","pressure":"854","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:38","sunset":"18:35","local_uv_irradiance_index":"High","min_gts_temp":"-79","max_gts_temp":"0"},{"id":"963","terrestrial_date":"2015-07-01","sol":"1031","ls":"6","season":"Month 1","min_temp":"-76","max_temp":"-15","pressure":"854","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:38","sunset":"18:36","local_uv_irradiance_index":"High","min_gts_temp":"-78","max_gts_temp":"0"},{"id":"961","terrestrial_date":"2015-06-30","sol":"1030","ls":"5","season":"Month 1","min_temp":"-77","max_temp":"-15","pressure":"855","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:38","sunset":"18:36","local_uv_irradiance_index":"High","min_gts_temp":"-78","max_gts_temp":"-1"},{"id":"959","terrestrial_date":"2015-06-29","sol":"1029","ls":"5","season":"Month 1","min_temp":"-75","max_temp":"-14","pressure":"856","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:38","sunset":"18:37","local_uv_irradiance_index":"High","min_gts_temp":"-77","max_gts_temp":"-2"},{"id":"960","terrestrial_date":"2015-06-28","sol":"1028","ls":"4","season":"Month 1","min_temp":"-75","max_temp":"-15","pressure":"853","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:39","sunset":"18:37","local_uv_irradiance_index":"High","min_gts_temp":"-77","max_gts_temp":"-2"},{"id":"957","terrestrial_date":"2015-06-26","sol":"1026","ls":"3","season":"Month 1","min_temp":"-78","max_temp":"-16","pressure":"851","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:39","sunset":"18:38","local_uv_irradiance_index":"Low","min_gts_temp":"-78","max_gts_temp":"-1"},{"id":"958","terrestrial_date":"2015-06-25","sol":"1025","ls":"3","season":"Month 1","min_temp":"-74","max_temp":"-14","pressure":"851","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:39","sunset":"18:38","local_uv_irradiance_index":"High","min_gts_temp":"-78","max_gts_temp":"-1"},{"id":"945","terrestrial_date":"2015-06-24","sol":"1024","ls":"2","season":"Month 1","min_temp":"-76","max_temp":"-15","pressure":"851","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:40","sunset":"18:39","local_uv_irradiance_index":"High","min_gts_temp":"-78","max_gts_temp":"-1"},{"id":"943","terrestrial_date":"2015-06-23","sol":"1023","ls":"2","season":"Month 1","min_temp":"-76","max_temp":"-15","pressure":"851","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:40","sunset":"18:39","local_uv_irradiance_index":"High","min_gts_temp":"-77","max_gts_temp":"-1"},{"id":"944","terrestrial_date":"2015-06-22","sol":"1022","ls":"1","season":"Month 1","min_temp":"-75","max_temp":"-12","pressure":"852","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:40","sunset":"18:39","local_uv_irradiance_index":"High","min_gts_temp":"-77","max_gts_temp":"-2"},{"id":"942","terrestrial_date":"2015-06-21","sol":"1021","ls":"1","season":"Month 1","min_temp":"-76","max_temp":"-14","pressure":"850","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:40","sunset":"18:40","local_uv_irradiance_index":"High","min_gts_temp":"-77","max_gts_temp":"-1"},{"id":"938","terrestrial_date":"2015-06-20","sol":"1020","ls":"0","season":"Month 1","min_temp":"-63","max_temp":"-12","pressure":"832","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:41","sunset":"18:40","local_uv_irradiance_index":"High","min_gts_temp":"-65","max_gts_temp":"1"},{"id":"954","terrestrial_date":"2015-06-19","sol":"1019","ls":"0","season":"Month 1","min_temp":"-81","max_temp":"-14","pressure":"849","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:41","sunset":"18:41","local_uv_irradiance_index":"Moderate","min_gts_temp":"-78","max_gts_temp":"-2"},{"id":"956","terrestrial_date":"2015-06-18","sol":"1018","ls":"359","season":"Month 12","min_temp":"-79","max_temp":"-15","pressure":"850","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:41","sunset":"18:41","local_uv_irradiance_index":"High","min_gts_temp":"-78","max_gts_temp":"-1"},{"id":"955","terrestrial_date":"2015-06-17","sol":"1017","ls":"359","season":"Month 12","min_temp":"-78","max_temp":"-12","pressure":"850","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:41","sunset":"18:41","local_uv_irradiance_index":"High","min_gts_temp":"-77","max_gts_temp":"-1"},{"id":"952","terrestrial_date":"2015-06-16","sol":"1016","ls":"358","season":"Month 12","min_temp":"-76","max_temp":"-11","pressure":"847","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:41","sunset":"18:42","local_uv_irradiance_index":"High","min_gts_temp":"-77","max_gts_temp":"0"},{"id":"951","terrestrial_date":"2015-06-14","sol":"1015","ls":"357","season":"Month 12","min_temp":"-75","max_temp":"-13","pressure":"847","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:42","sunset":"18:42","local_uv_irradiance_index":"High","min_gts_temp":"-76","max_gts_temp":"1"},{"id":"950","terrestrial_date":"2015-06-13","sol":"1014","ls":"357","season":"Month 12","min_temp":"-77","max_temp":"-14","pressure":"846","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:42","sunset":"18:42","local_uv_irradiance_index":"High","min_gts_temp":"-77","max_gts_temp":"0"},{"id":"953","terrestrial_date":"2015-06-12","sol":"1013","ls":"356","season":"Month 12","min_temp":"-80","max_temp":"-12","pressure":"846","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:42","sunset":"18:43","local_uv_irradiance_index":"High","min_gts_temp":"-77","max_gts_temp":"0"},{"id":"949","terrestrial_date":"2015-06-11","sol":"1012","ls":"356","season":"Month 12","min_temp":"-81","max_temp":"-16","pressure":"846","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:42","sunset":"18:43","local_uv_irradiance_index":"High","min_gts_temp":"-77","max_gts_temp":"-1"},{"id":"948","terrestrial_date":"2015-06-10","sol":"1011","ls":"355","season":"Month 12","min_temp":"-78","max_temp":"-12","pressure":"845","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:42","sunset":"18:43","local_uv_irradiance_index":"High","min_gts_temp":"-75","max_gts_temp":"0"},{"id":"947","terrestrial_date":"2015-06-09","sol":"1010","ls":"355","season":"Month 12","min_temp":"-74","max_temp":"-11","pressure":"846","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:43","sunset":"18:44","local_uv_irradiance_index":"High","min_gts_temp":"-76","max_gts_temp":"-1"},{"id":"946","terrestrial_date":"2015-06-08","sol":"1009","ls":"354","season":"Month 12","min_temp":"-75","max_temp":"-12","pressure":"845","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:43","sunset":"18:44","local_uv_irradiance_index":"High","min_gts_temp":"-76","max_gts_temp":"1"},{"id":"940","terrestrial_date":"2015-06-07","sol":"1008","ls":"354","season":"Month 12","min_temp":"-79","max_temp":"-12","pressure":"844","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:43","sunset":"18:44","local_uv_irradiance_index":"High","min_gts_temp":"-77","max_gts_temp":"2"},{"id":"937","terrestrial_date":"2015-06-06","sol":"1007","ls":"353","season":"Month 12","min_temp":"-79","max_temp":"-12","pressure":"845","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:43","sunset":"18:45","local_uv_irradiance_index":"High","min_gts_temp":"-77","max_gts_temp":"1"},{"id":"941","terrestrial_date":"2015-06-05","sol":"1006","ls":"353","season":"Month 12","min_temp":"-78","max_temp":"-13","pressure":"844","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:43","sunset":"18:45","local_uv_irradiance_index":"High","min_gts_temp":"-77","max_gts_temp":"0"},{"id":"939","terrestrial_date":"2015-06-04","sol":"1005","ls":"352","season":"Month 12","min_temp":"-75","max_temp":"-12","pressure":"843","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:44","sunset":"18:45","local_uv_irradiance_index":"High","min_gts_temp":"-75","max_gts_temp":"1"},{"id":"935","terrestrial_date":"2015-06-03","sol":"1004","ls":"352","season":"Month 12","min_temp":"-74","max_temp":"-9","pressure":"842","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:44","sunset":"18:46","local_uv_irradiance_index":"High","min_gts_temp":"-75","max_gts_temp":"1"},{"id":"934","terrestrial_date":"2015-06-02","sol":"1003","ls":"351","season":"Month 12","min_temp":"-74","max_temp":"-10","pressure":"843","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:44","sunset":"18:46","local_uv_irradiance_index":"High","min_gts_temp":"-77","max_gts_temp":"1"},{"id":"936","terrestrial_date":"2015-06-01","sol":"1002","ls":"351","season":"Month 12","min_temp":"-77","max_temp":"-12","pressure":"844","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:44","sunset":"18:46","local_uv_irradiance_index":"High","min_gts_temp":"-77","max_gts_temp":"1"},{"id":"933","terrestrial_date":"2015-05-31","sol":"1001","ls":"350","season":"Month 12","min_temp":"-79","max_temp":"-12","pressure":"843","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:44","sunset":"18:47","local_uv_irradiance_index":"High","min_gts_temp":"-77","max_gts_temp":"2"},{"id":"932","terrestrial_date":"2015-05-30","sol":"1000","ls":"350","season":"Month 12","min_temp":"-74","max_temp":"-12","pressure":"841","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:44","sunset":"18:47","local_uv_irradiance_index":"High","min_gts_temp":"-75","max_gts_temp":"1"},{"id":"931","terrestrial_date":"2015-05-28","sol":"998","ls":"349","season":"Month 12","min_temp":"-73","max_temp":"-8","pressure":"842","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:45","sunset":"18:48","local_uv_irradiance_index":"High","min_gts_temp":"-74","max_gts_temp":"1"},{"id":"930","terrestrial_date":"2015-05-27","sol":"997","ls":"348","season":"Month 12","min_temp":"-74","max_temp":"-13","pressure":"843","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:45","sunset":"18:48","local_uv_irradiance_index":"High","min_gts_temp":"-76","max_gts_temp":"1"},{"id":"928","terrestrial_date":"2015-05-26","sol":"996","ls":"348","season":"Month 12","min_temp":"-72","max_temp":"-4","pressure":"842","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:45","sunset":"18:48","local_uv_irradiance_index":"High","min_gts_temp":"-75","max_gts_temp":"2"},{"id":"929","terrestrial_date":"2015-05-25","sol":"995","ls":"347","season":"Month 12","min_temp":"-74","max_temp":"-4","pressure":"842","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:45","sunset":"18:48","local_uv_irradiance_index":"High","min_gts_temp":"-78","max_gts_temp":"5"},{"id":"925","terrestrial_date":"2015-05-24","sol":"994","ls":"346","season":"Month 12","min_temp":"-79","max_temp":"-5","pressure":"842","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:45","sunset":"18:49","local_uv_irradiance_index":"High","min_gts_temp":"-77","max_gts_temp":"3"},{"id":"927","terrestrial_date":"2015-05-23","sol":"993","ls":"346","season":"Month 12","min_temp":"-73","max_temp":"-8","pressure":"840","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:45","sunset":"18:49","local_uv_irradiance_index":"High","min_gts_temp":"-76","max_gts_temp":"3"},{"id":"924","terrestrial_date":"2015-05-22","sol":"992","ls":"345","season":"Month 12","min_temp":"-73","max_temp":"-6","pressure":"842","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:46","sunset":"18:49","local_uv_irradiance_index":"High","min_gts_temp":"-78","max_gts_temp":"4"},{"id":"926","terrestrial_date":"2015-05-21","sol":"991","ls":"345","season":"Month 12","min_temp":"-73","max_temp":"-3","pressure":"840","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:46","sunset":"18:49","local_uv_irradiance_index":"High","min_gts_temp":"-80","max_gts_temp":"4"},{"id":"923","terrestrial_date":"2015-05-20","sol":"990","ls":"344","season":"Month 12","min_temp":"-76","max_temp":"-1","pressure":"839","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:46","sunset":"18:50","local_uv_irradiance_index":"High","min_gts_temp":"-79","max_gts_temp":"9"},{"id":"921","terrestrial_date":"2015-05-19","sol":"989","ls":"344","season":"Month 12","min_temp":"-74","max_temp":"-10","pressure":"841","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:46","sunset":"18:50","local_uv_irradiance_index":"Moderate","min_gts_temp":"-79","max_gts_temp":"9"},{"id":"922","terrestrial_date":"2015-05-18","sol":"988","ls":"343","season":"Month 12","min_temp":"-75","max_temp":"-10","pressure":"840","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:46","sunset":"18:50","local_uv_irradiance_index":"Moderate","min_gts_temp":"-77","max_gts_temp":"8"},{"id":"920","terrestrial_date":"2015-05-17","sol":"987","ls":"343","season":"Month 12","min_temp":"-74","max_temp":"-7","pressure":"839","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:46","sunset":"18:50","local_uv_irradiance_index":"Moderate","min_gts_temp":"-77","max_gts_temp":"9"},{"id":"919","terrestrial_date":"2015-05-16","sol":"986","ls":"342","season":"Month 12","min_temp":"-74","max_temp":"-1","pressure":"840","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:46","sunset":"18:51","local_uv_irradiance_index":"Moderate","min_gts_temp":"-79","max_gts_temp":"9"},{"id":"918","terrestrial_date":"2015-05-15","sol":"985","ls":"342","season":"Month 12","min_temp":"-78","max_temp":"-5","pressure":"840","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:46","sunset":"18:51","local_uv_irradiance_index":"Moderate","min_gts_temp":"-81","max_gts_temp":"9"},{"id":"915","terrestrial_date":"2015-05-14","sol":"984","ls":"341","season":"Month 12","min_temp":"-74","max_temp":"-6","pressure":"840","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:46","sunset":"18:51","local_uv_irradiance_index":"Moderate","min_gts_temp":"-83","max_gts_temp":"11"},{"id":"916","terrestrial_date":"2015-05-13","sol":"983","ls":"340","season":"Month 12","min_temp":"-76","max_temp":"-6","pressure":"840","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:46","sunset":"18:51","local_uv_irradiance_index":"Moderate","min_gts_temp":"-77","max_gts_temp":"12"},{"id":"917","terrestrial_date":"2015-05-12","sol":"982","ls":"340","season":"Month 12","min_temp":"-74","max_temp":"-4","pressure":"840","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:46","sunset":"18:52","local_uv_irradiance_index":"Moderate","min_gts_temp":"-76","max_gts_temp":"7"},{"id":"914","terrestrial_date":"2015-05-11","sol":"981","ls":"339","season":"Month 12","min_temp":"-73","max_temp":"-3","pressure":"841","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:46","sunset":"18:52","local_uv_irradiance_index":"Moderate","min_gts_temp":"-78","max_gts_temp":"8"},{"id":"911","terrestrial_date":"2015-05-10","sol":"980","ls":"339","season":"Month 12","min_temp":"-73","max_temp":"1","pressure":"840","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:47","sunset":"18:52","local_uv_irradiance_index":"Moderate","min_gts_temp":"-78","max_gts_temp":"11"},{"id":"912","terrestrial_date":"2015-05-09","sol":"979","ls":"338","season":"Month 12","min_temp":"-79","max_temp":"-4","pressure":"841","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:47","sunset":"18:52","local_uv_irradiance_index":"Moderate","min_gts_temp":"-80","max_gts_temp":"11"},{"id":"913","terrestrial_date":"2015-05-07","sol":"978","ls":"338","season":"Month 12","min_temp":"-73","max_temp":"-2","pressure":"842","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:47","sunset":"18:52","local_uv_irradiance_index":"Moderate","min_gts_temp":"-78","max_gts_temp":"10"},{"id":"910","terrestrial_date":"2015-05-06","sol":"977","ls":"337","season":"Month 12","min_temp":"-75","max_temp":"-4","pressure":"841","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:47","sunset":"18:53","local_uv_irradiance_index":"Moderate","min_gts_temp":"-77","max_gts_temp":"10"},{"id":"909","terrestrial_date":"2015-05-05","sol":"976","ls":"337","season":"Month 12","min_temp":"-75","max_temp":"-6","pressure":"842","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:47","sunset":"18:53","local_uv_irradiance_index":"Moderate","min_gts_temp":"-77","max_gts_temp":"10"},{"id":"908","terrestrial_date":"2015-05-04","sol":"975","ls":"336","season":"Month 12","min_temp":"-73","max_temp":"-3","pressure":"843","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:47","sunset":"18:53","local_uv_irradiance_index":"High","min_gts_temp":"-78","max_gts_temp":"14"},{"id":"907","terrestrial_date":"2015-05-03","sol":"974","ls":"336","season":"Month 12","min_temp":"-73","max_temp":"-3","pressure":"842","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:47","sunset":"18:53","local_uv_irradiance_index":"High","min_gts_temp":"-80","max_gts_temp":"14"},{"id":"905","terrestrial_date":"2015-05-02","sol":"973","ls":"335","season":"Month 12","min_temp":"-72","max_temp":"-4","pressure":"844","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:47","sunset":"18:53","local_uv_irradiance_index":"High","min_gts_temp":"-81","max_gts_temp":"16"},{"id":"904","terrestrial_date":"2015-05-01","sol":"972","ls":"334","season":"Month 12","min_temp":"-73","max_temp":"-3","pressure":"847","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:47","sunset":"18:53","local_uv_irradiance_index":"High","min_gts_temp":"-80","max_gts_temp":"14"},{"id":"906","terrestrial_date":"2015-04-30","sol":"971","ls":"334","season":"Month 12","min_temp":"-71","max_temp":"-3","pressure":"848","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:47","sunset":"18:54","local_uv_irradiance_index":"Moderate","min_gts_temp":"-76","max_gts_temp":"14"},{"id":"903","terrestrial_date":"2015-04-29","sol":"970","ls":"333","season":"Month 12","min_temp":"-71","max_temp":"-4","pressure":"849","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:47","sunset":"18:54","local_uv_irradiance_index":"High","min_gts_temp":"-77","max_gts_temp":"11"},{"id":"902","terrestrial_date":"2015-04-28","sol":"969","ls":"333","season":"Month 12","min_temp":"-72","max_temp":"-2","pressure":"844","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:47","sunset":"18:54","local_uv_irradiance_index":"High","min_gts_temp":"-76","max_gts_temp":"11"},{"id":"901","terrestrial_date":"2015-04-27","sol":"968","ls":"332","season":"Month 12","min_temp":"-71","max_temp":"-3","pressure":"848","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:47","sunset":"18:54","local_uv_irradiance_index":"High","min_gts_temp":"-75","max_gts_temp":"11"},{"id":"899","terrestrial_date":"2015-04-26","sol":"967","ls":"332","season":"Month 12","min_temp":"-74","max_temp":"-2","pressure":"844","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:47","sunset":"18:54","local_uv_irradiance_index":"High","min_gts_temp":"-76","max_gts_temp":"12"},{"id":"900","terrestrial_date":"2015-04-25","sol":"966","ls":"331","season":"Month 12","min_temp":"-73","max_temp":"-12","pressure":"850","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:47","sunset":"18:54","local_uv_irradiance_index":"High","min_gts_temp":"-75","max_gts_temp":"9"},{"id":"898","terrestrial_date":"2015-04-24","sol":"965","ls":"331","season":"Month 12","min_temp":"-75","max_temp":"-10","pressure":"847","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:47","sunset":"18:54","local_uv_irradiance_index":"High","min_gts_temp":"-75","max_gts_temp":"8"},{"id":"897","terrestrial_date":"2015-04-23","sol":"964","ls":"330","season":"Month 12","min_temp":"-72","max_temp":"-13","pressure":"847","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:47","sunset":"18:54","local_uv_irradiance_index":"High","min_gts_temp":"-77","max_gts_temp":"9"},{"id":"896","terrestrial_date":"2015-04-22","sol":"963","ls":"329","season":"Month 11","min_temp":"-71","max_temp":"-5","pressure":"850","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:46","sunset":"18:55","local_uv_irradiance_index":"High","min_gts_temp":"-77","max_gts_temp":"10"},{"id":"894","terrestrial_date":"2015-04-21","sol":"962","ls":"329","season":"Month 11","min_temp":"-73","max_temp":"-2","pressure":"845","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:46","sunset":"18:55","local_uv_irradiance_index":"High","min_gts_temp":"-80","max_gts_temp":"11"},{"id":"895","terrestrial_date":"2015-04-20","sol":"961","ls":"328","season":"Month 11","min_temp":"-71","max_temp":"-3","pressure":"849","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:46","sunset":"18:55","local_uv_irradiance_index":"High","min_gts_temp":"-80","max_gts_temp":"11"},{"id":"893","terrestrial_date":"2015-04-19","sol":"960","ls":"328","season":"Month 11","min_temp":"-76","max_temp":"-8","pressure":"846","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:46","sunset":"18:55","local_uv_irradiance_index":"High","min_gts_temp":"-86","max_gts_temp":"15"},{"id":"891","terrestrial_date":"2015-04-18","sol":"959","ls":"327","season":"Month 11","min_temp":"-78","max_temp":"-4","pressure":"846","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:46","sunset":"18:55","local_uv_irradiance_index":"High","min_gts_temp":"-84","max_gts_temp":"14"},{"id":"892","terrestrial_date":"2015-04-17","sol":"958","ls":"327","season":"Month 11","min_temp":"-74","max_temp":"-9","pressure":"848","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:46","sunset":"18:55","local_uv_irradiance_index":"High","min_gts_temp":"-83","max_gts_temp":"17"},{"id":"890","terrestrial_date":"2015-04-16","sol":"957","ls":"326","season":"Month 11","min_temp":"--","max_temp":"--","pressure":"--","pressure_string":"--","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"--","sunrise":"06:46","sunset":"18:55","local_uv_irradiance_index":"--","min_gts_temp":"--","max_gts_temp":"--"},{"id":"889","terrestrial_date":"2015-04-13","sol":"954","ls":"324","season":"Month 11","min_temp":"-73","max_temp":"-3","pressure":"847","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:46","sunset":"18:55","local_uv_irradiance_index":"High","min_gts_temp":"-78","max_gts_temp":"11"},{"id":"887","terrestrial_date":"2015-04-12","sol":"953","ls":"324","season":"Month 11","min_temp":"-71","max_temp":"-5","pressure":"847","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:45","sunset":"18:55","local_uv_irradiance_index":"High","min_gts_temp":"-78","max_gts_temp":"12"},{"id":"886","terrestrial_date":"2015-04-11","sol":"952","ls":"323","season":"Month 11","min_temp":"-69","max_temp":"-6","pressure":"847","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:45","sunset":"18:55","local_uv_irradiance_index":"High","min_gts_temp":"-78","max_gts_temp":"14"},{"id":"888","terrestrial_date":"2015-04-10","sol":"951","ls":"323","season":"Month 11","min_temp":"-75","max_temp":"-4","pressure":"849","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:45","sunset":"18:55","local_uv_irradiance_index":"High","min_gts_temp":"-81","max_gts_temp":"14"},{"id":"885","terrestrial_date":"2015-04-09","sol":"950","ls":"322","season":"Month 11","min_temp":"-73","max_temp":"0","pressure":"850","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:45","sunset":"18:55","local_uv_irradiance_index":"High","min_gts_temp":"-71","max_gts_temp":"13"},{"id":"884","terrestrial_date":"2015-04-08","sol":"949","ls":"321","season":"Month 11","min_temp":"-72","max_temp":"-4","pressure":"851","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:45","sunset":"18:55","local_uv_irradiance_index":"High","min_gts_temp":"-75","max_gts_temp":"9"},{"id":"883","terrestrial_date":"2015-04-07","sol":"948","ls":"321","season":"Month 11","min_temp":"-73","max_temp":"-8","pressure":"854","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:45","sunset":"18:55","local_uv_irradiance_index":"High","min_gts_temp":"-75","max_gts_temp":"6"},{"id":"882","terrestrial_date":"2015-04-06","sol":"947","ls":"320","season":"Month 11","min_temp":"-70","max_temp":"-3","pressure":"853","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:45","sunset":"18:55","local_uv_irradiance_index":"High","min_gts_temp":"-73","max_gts_temp":"6"},{"id":"879","terrestrial_date":"2015-04-05","sol":"946","ls":"320","season":"Month 11","min_temp":"-77","max_temp":"-2","pressure":"850","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:44","sunset":"18:55","local_uv_irradiance_index":"High","min_gts_temp":"-74","max_gts_temp":"6"},{"id":"881","terrestrial_date":"2015-04-04","sol":"945","ls":"319","season":"Month 11","min_temp":"-72","max_temp":"-1","pressure":"851","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:44","sunset":"18:55","local_uv_irradiance_index":"High","min_gts_temp":"-74","max_gts_temp":"5"},{"id":"878","terrestrial_date":"2015-04-03","sol":"944","ls":"318","season":"Month 11","min_temp":"-70","max_temp":"0","pressure":"852","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:44","sunset":"18:55","local_uv_irradiance_index":"High","min_gts_temp":"-70","max_gts_temp":"8"},{"id":"877","terrestrial_date":"2015-04-02","sol":"943","ls":"318","season":"Month 11","min_temp":"-73","max_temp":"-1","pressure":"854","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:44","sunset":"18:55","local_uv_irradiance_index":"High","min_gts_temp":"-69","max_gts_temp":"5"},{"id":"880","terrestrial_date":"2015-03-31","sol":"942","ls":"317","season":"Month 11","min_temp":"-74","max_temp":"-1","pressure":"857","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:44","sunset":"18:55","local_uv_irradiance_index":"High","min_gts_temp":"-70","max_gts_temp":"6"},{"id":"875","terrestrial_date":"2015-03-30","sol":"941","ls":"317","season":"Month 11","min_temp":"-74","max_temp":"0","pressure":"857","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:43","sunset":"18:55","local_uv_irradiance_index":"High","min_gts_temp":"-71","max_gts_temp":"6"},{"id":"876","terrestrial_date":"2015-03-29","sol":"940","ls":"316","season":"Month 11","min_temp":"-73","max_temp":"-2","pressure":"861","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:43","sunset":"18:54","local_uv_irradiance_index":"Very_High","min_gts_temp":"-76","max_gts_temp":"8"},{"id":"873","terrestrial_date":"2015-03-28","sol":"939","ls":"316","season":"Month 11","min_temp":"--","max_temp":"--","pressure":"--","pressure_string":"--","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"--","sunrise":"06:43","sunset":"18:54","local_uv_irradiance_index":"--","min_gts_temp":"--","max_gts_temp":"--"},{"id":"874","terrestrial_date":"2015-03-26","sol":"937","ls":"314","season":"Month 11","min_temp":"-73","max_temp":"0","pressure":"863","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:42","sunset":"18:54","local_uv_irradiance_index":"Very_High","min_gts_temp":"-77","max_gts_temp":"10"},{"id":"871","terrestrial_date":"2015-03-25","sol":"936","ls":"314","season":"Month 11","min_temp":"-72","max_temp":"-3","pressure":"865","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:42","sunset":"18:54","local_uv_irradiance_index":"Very_High","min_gts_temp":"-75","max_gts_temp":"9"},{"id":"872","terrestrial_date":"2015-03-24","sol":"935","ls":"313","season":"Month 11","min_temp":"-75","max_temp":"-2","pressure":"862","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:42","sunset":"18:54","local_uv_irradiance_index":"Very_High","min_gts_temp":"-74","max_gts_temp":"10"},{"id":"870","terrestrial_date":"2015-03-23","sol":"934","ls":"313","season":"Month 11","min_temp":"-73","max_temp":"1","pressure":"858","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:41","sunset":"18:54","local_uv_irradiance_index":"Very_High","min_gts_temp":"-75","max_gts_temp":"9"},{"id":"869","terrestrial_date":"2015-03-22","sol":"933","ls":"312","season":"Month 11","min_temp":"-71","max_temp":"-4","pressure":"863","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:41","sunset":"18:54","local_uv_irradiance_index":"Very_High","min_gts_temp":"-75","max_gts_temp":"11"},{"id":"866","terrestrial_date":"2015-03-21","sol":"932","ls":"311","season":"Month 11","min_temp":"-71","max_temp":"1","pressure":"859","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:41","sunset":"18:53","local_uv_irradiance_index":"Very_High","min_gts_temp":"-75","max_gts_temp":"10"},{"id":"867","terrestrial_date":"2015-03-20","sol":"931","ls":"311","season":"Month 11","min_temp":"-69","max_temp":"-1","pressure":"864","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:41","sunset":"18:53","local_uv_irradiance_index":"Very_High","min_gts_temp":"-76","max_gts_temp":"10"},{"id":"868","terrestrial_date":"2015-03-19","sol":"930","ls":"310","season":"Month 11","min_temp":"-75","max_temp":"-1","pressure":"865","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:40","sunset":"18:53","local_uv_irradiance_index":"Very_High","min_gts_temp":"-78","max_gts_temp":"10"},{"id":"865","terrestrial_date":"2015-03-18","sol":"929","ls":"310","season":"Month 11","min_temp":"-75","max_temp":"0","pressure":"866","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:40","sunset":"18:53","local_uv_irradiance_index":"Very_High","min_gts_temp":"-76","max_gts_temp":"11"},{"id":"864","terrestrial_date":"2015-03-17","sol":"928","ls":"309","season":"Month 11","min_temp":"-72","max_temp":"0","pressure":"862","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:40","sunset":"18:53","local_uv_irradiance_index":"Very_High","min_gts_temp":"-74","max_gts_temp":"9"},{"id":"863","terrestrial_date":"2015-03-16","sol":"927","ls":"308","season":"Month 11","min_temp":"-76","max_temp":"-2","pressure":"862","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:39","sunset":"18:52","local_uv_irradiance_index":"Very_High","min_gts_temp":"-75","max_gts_temp":"11"},{"id":"862","terrestrial_date":"2015-03-15","sol":"926","ls":"308","season":"Month 11","min_temp":"-76","max_temp":"1","pressure":"864","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:39","sunset":"18:52","local_uv_irradiance_index":"High","min_gts_temp":"-75","max_gts_temp":"10"},{"id":"860","terrestrial_date":"2015-03-14","sol":"925","ls":"307","season":"Month 11","min_temp":"-71","max_temp":"-2","pressure":"862","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:38","sunset":"18:52","local_uv_irradiance_index":"High","min_gts_temp":"-75","max_gts_temp":"9"},{"id":"861","terrestrial_date":"2015-03-13","sol":"924","ls":"307","season":"Month 11","min_temp":"-74","max_temp":"-1","pressure":"867","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:38","sunset":"18:52","local_uv_irradiance_index":"Very_High","min_gts_temp":"-79","max_gts_temp":"10"},{"id":"859","terrestrial_date":"2015-03-12","sol":"923","ls":"306","season":"Month 11","min_temp":"-72","max_temp":"-5","pressure":"867","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:38","sunset":"18:51","local_uv_irradiance_index":"Very_High","min_gts_temp":"-74","max_gts_temp":"14"},{"id":"858","terrestrial_date":"2015-03-11","sol":"922","ls":"305","season":"Month 11","min_temp":"-73","max_temp":"-2","pressure":"870","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:37","sunset":"18:51","local_uv_irradiance_index":"Very_High","min_gts_temp":"-75","max_gts_temp":"9"},{"id":"857","terrestrial_date":"2015-03-10","sol":"921","ls":"305","season":"Month 11","min_temp":"-70","max_temp":"2","pressure":"867","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:37","sunset":"18:51","local_uv_irradiance_index":"High","min_gts_temp":"-74","max_gts_temp":"9"},{"id":"856","terrestrial_date":"2015-03-09","sol":"920","ls":"304","season":"Month 11","min_temp":"-72","max_temp":"-2","pressure":"868","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:37","sunset":"18:51","local_uv_irradiance_index":"High","min_gts_temp":"-74","max_gts_temp":"9"},{"id":"855","terrestrial_date":"2015-03-08","sol":"919","ls":"304","season":"Month 11","min_temp":"-74","max_temp":"-3","pressure":"870","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:36","sunset":"18:50","local_uv_irradiance_index":"High","min_gts_temp":"-74","max_gts_temp":"9"},{"id":"853","terrestrial_date":"2015-03-07","sol":"918","ls":"303","season":"Month 11","min_temp":"-71","max_temp":"0","pressure":"867","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:36","sunset":"18:50","local_uv_irradiance_index":"High","min_gts_temp":"-76","max_gts_temp":"9"},{"id":"854","terrestrial_date":"2015-03-06","sol":"917","ls":"302","season":"Month 11","min_temp":"-76","max_temp":"-1","pressure":"871","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:35","sunset":"18:50","local_uv_irradiance_index":"High","min_gts_temp":"-75","max_gts_temp":"9"},{"id":"852","terrestrial_date":"2015-03-05","sol":"916","ls":"302","season":"Month 11","min_temp":"-71","max_temp":"-5","pressure":"872","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:35","sunset":"18:49","local_uv_irradiance_index":"High","min_gts_temp":"-75","max_gts_temp":"9"},{"id":"851","terrestrial_date":"2015-03-04","sol":"915","ls":"301","season":"Month 11","min_temp":"-71","max_temp":"-4","pressure":"878","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:34","sunset":"18:49","local_uv_irradiance_index":"High","min_gts_temp":"-74","max_gts_temp":"9"},{"id":"850","terrestrial_date":"2015-03-03","sol":"914","ls":"300","season":"Month 11","min_temp":"-71","max_temp":"0","pressure":"874","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:34","sunset":"18:49","local_uv_irradiance_index":"High","min_gts_temp":"-60","max_gts_temp":"8"},{"id":"848","terrestrial_date":"2015-03-02","sol":"913","ls":"300","season":"Month 11","min_temp":"-71","max_temp":"-4","pressure":"871","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:33","sunset":"18:48","local_uv_irradiance_index":"High","min_gts_temp":"-70","max_gts_temp":"10"},{"id":"849","terrestrial_date":"2015-03-01","sol":"912","ls":"299","season":"Month 10","min_temp":"-75","max_temp":"-4","pressure":"875","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:33","sunset":"18:48","local_uv_irradiance_index":"High","min_gts_temp":"-76","max_gts_temp":"8"},{"id":"846","terrestrial_date":"2015-02-28","sol":"911","ls":"299","season":"Month 10","min_temp":"-70","max_temp":"0","pressure":"874","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:33","sunset":"18:48","local_uv_irradiance_index":"High","min_gts_temp":"-75","max_gts_temp":"8"},{"id":"847","terrestrial_date":"2015-02-27","sol":"910","ls":"298","season":"Month 10","min_temp":"-73","max_temp":"0","pressure":"878","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:32","sunset":"18:47","local_uv_irradiance_index":"High","min_gts_temp":"-74","max_gts_temp":"9"},{"id":"845","terrestrial_date":"2015-02-26","sol":"909","ls":"297","season":"Month 10","min_temp":"-70","max_temp":"-2","pressure":"878","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:32","sunset":"18:47","local_uv_irradiance_index":"High","min_gts_temp":"-75","max_gts_temp":"8"},{"id":"844","terrestrial_date":"2015-02-25","sol":"908","ls":"297","season":"Month 10","min_temp":"-71","max_temp":"-3","pressure":"883","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:31","sunset":"18:46","local_uv_irradiance_index":"High","min_gts_temp":"-75","max_gts_temp":"8"},{"id":"842","terrestrial_date":"2015-02-24","sol":"907","ls":"296","season":"Month 10","min_temp":"-75","max_temp":"-1","pressure":"879","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:31","sunset":"18:46","local_uv_irradiance_index":"High","min_gts_temp":"-75","max_gts_temp":"8"},{"id":"843","terrestrial_date":"2015-02-22","sol":"906","ls":"296","season":"Month 10","min_temp":"-76","max_temp":"-3","pressure":"878","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:30","sunset":"18:46","local_uv_irradiance_index":"High","min_gts_temp":"-75","max_gts_temp":"9"},{"id":"840","terrestrial_date":"2015-02-21","sol":"905","ls":"295","season":"Month 10","min_temp":"-71","max_temp":"-2","pressure":"880","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:29","sunset":"18:45","local_uv_irradiance_index":"High","min_gts_temp":"-75","max_gts_temp":"8"},{"id":"841","terrestrial_date":"2015-02-20","sol":"904","ls":"294","season":"Month 10","min_temp":"-71","max_temp":"0","pressure":"878","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:29","sunset":"18:45","local_uv_irradiance_index":"High","min_gts_temp":"-74","max_gts_temp":"8"},{"id":"839","terrestrial_date":"2015-02-19","sol":"903","ls":"294","season":"Month 10","min_temp":"-71","max_temp":"-2","pressure":"882","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:28","sunset":"18:44","local_uv_irradiance_index":"High","min_gts_temp":"-73","max_gts_temp":"6"},{"id":"838","terrestrial_date":"2015-02-18","sol":"902","ls":"293","season":"Month 10","min_temp":"-68","max_temp":"0","pressure":"883","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:28","sunset":"18:44","local_uv_irradiance_index":"High","min_gts_temp":"-70","max_gts_temp":"5"},{"id":"837","terrestrial_date":"2015-02-17","sol":"901","ls":"292","season":"Month 10","min_temp":"-71","max_temp":"-2","pressure":"885","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:27","sunset":"18:43","local_uv_irradiance_index":"High","min_gts_temp":"-74","max_gts_temp":"11"},{"id":"836","terrestrial_date":"2015-02-16","sol":"900","ls":"292","season":"Month 10","min_temp":"-70","max_temp":"1","pressure":"883","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:27","sunset":"18:43","local_uv_irradiance_index":"High","min_gts_temp":"-75","max_gts_temp":"7"},{"id":"835","terrestrial_date":"2015-02-15","sol":"899","ls":"291","season":"Month 10","min_temp":"-75","max_temp":"-1","pressure":"881","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:26","sunset":"18:42","local_uv_irradiance_index":"High","min_gts_temp":"-73","max_gts_temp":"7"},{"id":"834","terrestrial_date":"2015-02-14","sol":"898","ls":"291","season":"Month 10","min_temp":"-72","max_temp":"2","pressure":"883","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:26","sunset":"18:42","local_uv_irradiance_index":"Very_High","min_gts_temp":"-75","max_gts_temp":"7"},{"id":"832","terrestrial_date":"2015-02-13","sol":"897","ls":"290","season":"Month 10","min_temp":"-72","max_temp":"5","pressure":"884","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:25","sunset":"18:41","local_uv_irradiance_index":"High","min_gts_temp":"-74","max_gts_temp":"7"},{"id":"833","terrestrial_date":"2015-02-12","sol":"896","ls":"289","season":"Month 10","min_temp":"-70","max_temp":"1","pressure":"888","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:24","sunset":"18:41","local_uv_irradiance_index":"High","min_gts_temp":"-73","max_gts_temp":"8"},{"id":"831","terrestrial_date":"2015-02-11","sol":"895","ls":"289","season":"Month 10","min_temp":"-72","max_temp":"-4","pressure":"889","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:24","sunset":"18:40","local_uv_irradiance_index":"High","min_gts_temp":"-74","max_gts_temp":"5"},{"id":"830","terrestrial_date":"2015-02-10","sol":"894","ls":"288","season":"Month 10","min_temp":"-75","max_temp":"-4","pressure":"891","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:23","sunset":"18:40","local_uv_irradiance_index":"Moderate","min_gts_temp":"-74","max_gts_temp":"4"},{"id":"829","terrestrial_date":"2015-02-09","sol":"893","ls":"287","season":"Month 10","min_temp":"-72","max_temp":"2","pressure":"890","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:23","sunset":"18:39","local_uv_irradiance_index":"High","min_gts_temp":"-73","max_gts_temp":"4"},{"id":"828","terrestrial_date":"2015-02-08","sol":"892","ls":"287","season":"Month 10","min_temp":"-71","max_temp":"-3","pressure":"892","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:22","sunset":"18:39","local_uv_irradiance_index":"High","min_gts_temp":"-74","max_gts_temp":"5"},{"id":"827","terrestrial_date":"2015-02-07","sol":"891","ls":"286","season":"Month 10","min_temp":"-72","max_temp":"-3","pressure":"892","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:21","sunset":"18:38","local_uv_irradiance_index":"High","min_gts_temp":"-74","max_gts_temp":"5"},{"id":"826","terrestrial_date":"2015-02-06","sol":"890","ls":"286","season":"Month 10","min_temp":"-72","max_temp":"-4","pressure":"892","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:21","sunset":"18:38","local_uv_irradiance_index":"High","min_gts_temp":"-73","max_gts_temp":"5"},{"id":"825","terrestrial_date":"2015-02-05","sol":"889","ls":"285","season":"Month 10","min_temp":"-70","max_temp":"2","pressure":"895","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:20","sunset":"18:37","local_uv_irradiance_index":"High","min_gts_temp":"-74","max_gts_temp":"4"},{"id":"824","terrestrial_date":"2015-02-04","sol":"888","ls":"284","season":"Month 10","min_temp":"-70","max_temp":"-5","pressure":"896","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:20","sunset":"18:37","local_uv_irradiance_index":"High","min_gts_temp":"-74","max_gts_temp":"3"},{"id":"822","terrestrial_date":"2015-02-03","sol":"887","ls":"284","season":"Month 10","min_temp":"-73","max_temp":"-4","pressure":"897","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:19","sunset":"18:36","local_uv_irradiance_index":"Moderate","min_gts_temp":"-73","max_gts_temp":"5"},{"id":"823","terrestrial_date":"2015-02-02","sol":"886","ls":"283","season":"Month 10","min_temp":"-73","max_temp":"-6","pressure":"894","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:18","sunset":"18:35","local_uv_irradiance_index":"Moderate","min_gts_temp":"-73","max_gts_temp":"4"},{"id":"819","terrestrial_date":"2015-02-01","sol":"885","ls":"282","season":"Month 10","min_temp":"-71","max_temp":"-4","pressure":"895","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:18","sunset":"18:35","local_uv_irradiance_index":"Moderate","min_gts_temp":"-73","max_gts_temp":"4"},{"id":"821","terrestrial_date":"2015-01-31","sol":"884","ls":"282","season":"Month 10","min_temp":"-71","max_temp":"-5","pressure":"897","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:17","sunset":"18:34","local_uv_irradiance_index":"Moderate","min_gts_temp":"-74","max_gts_temp":"5"},{"id":"820","terrestrial_date":"2015-01-30","sol":"883","ls":"281","season":"Month 10","min_temp":"-74","max_temp":"-4","pressure":"897","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:16","sunset":"18:34","local_uv_irradiance_index":"Moderate","min_gts_temp":"-74","max_gts_temp":"3"},{"id":"818","terrestrial_date":"2015-01-29","sol":"882","ls":"280","season":"Month 10","min_temp":"-76","max_temp":"-6","pressure":"901","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:16","sunset":"18:33","local_uv_irradiance_index":"Moderate","min_gts_temp":"-73","max_gts_temp":"3"},{"id":"817","terrestrial_date":"2015-01-28","sol":"881","ls":"280","season":"Month 10","min_temp":"-73","max_temp":"-4","pressure":"899","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:15","sunset":"18:32","local_uv_irradiance_index":"Moderate","min_gts_temp":"-74","max_gts_temp":"4"},{"id":"816","terrestrial_date":"2015-01-27","sol":"880","ls":"279","season":"Month 10","min_temp":"--","max_temp":"--","pressure":"--","pressure_string":"--","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"--","sunrise":"06:14","sunset":"18:32","local_uv_irradiance_index":"--","min_gts_temp":"--","max_gts_temp":"--"},{"id":"815","terrestrial_date":"2015-01-19","sol":"872","ls":"274","season":"Month 10","min_temp":"-72","max_temp":"1","pressure":"902","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:09","sunset":"18:27","local_uv_irradiance_index":"Moderate","min_gts_temp":"-72","max_gts_temp":"5"},{"id":"814","terrestrial_date":"2015-01-18","sol":"871","ls":"273","season":"Month 10","min_temp":"-70","max_temp":"-7","pressure":"903","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:08","sunset":"18:26","local_uv_irradiance_index":"Moderate","min_gts_temp":"-60","max_gts_temp":"3"},{"id":"813","terrestrial_date":"2015-01-17","sol":"870","ls":"273","season":"Month 10","min_temp":"-74","max_temp":"-6","pressure":"909","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:07","sunset":"18:25","local_uv_irradiance_index":"Moderate","min_gts_temp":"-75","max_gts_temp":"4"},{"id":"812","terrestrial_date":"2015-01-15","sol":"869","ls":"272","season":"Month 10","min_temp":"-72","max_temp":"-4","pressure":"906","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:07","sunset":"18:25","local_uv_irradiance_index":"Moderate","min_gts_temp":"-72","max_gts_temp":"5"},{"id":"811","terrestrial_date":"2015-01-14","sol":"868","ls":"271","season":"Month 10","min_temp":"-71","max_temp":"-4","pressure":"908","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:06","sunset":"18:24","local_uv_irradiance_index":"Moderate","min_gts_temp":"-71","max_gts_temp":"3"},{"id":"810","terrestrial_date":"2015-01-13","sol":"867","ls":"271","season":"Month 10","min_temp":"-71","max_temp":"-2","pressure":"910","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:05","sunset":"18:23","local_uv_irradiance_index":"Moderate","min_gts_temp":"-72","max_gts_temp":"4"},{"id":"809","terrestrial_date":"2015-01-12","sol":"866","ls":"270","season":"Month 10","min_temp":"-71","max_temp":"-4","pressure":"911","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:05","sunset":"18:22","local_uv_irradiance_index":"Moderate","min_gts_temp":"-73","max_gts_temp":"4"},{"id":"808","terrestrial_date":"2015-01-11","sol":"865","ls":"270","season":"Month 10","min_temp":"-70","max_temp":"-5","pressure":"913","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:04","sunset":"18:22","local_uv_irradiance_index":"Moderate","min_gts_temp":"-75","max_gts_temp":"4"},{"id":"806","terrestrial_date":"2015-01-10","sol":"864","ls":"269","season":"Month 9","min_temp":"-70","max_temp":"-1","pressure":"914","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:03","sunset":"18:21","local_uv_irradiance_index":"High","min_gts_temp":"-76","max_gts_temp":"3"},{"id":"805","terrestrial_date":"2015-01-09","sol":"863","ls":"268","season":"Month 9","min_temp":"-72","max_temp":"-13","pressure":"916","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:02","sunset":"18:20","local_uv_irradiance_index":"High","min_gts_temp":"-72","max_gts_temp":"3"},{"id":"807","terrestrial_date":"2015-01-08","sol":"862","ls":"268","season":"Month 9","min_temp":"-70","max_temp":"-2","pressure":"917","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:02","sunset":"18:20","local_uv_irradiance_index":"Moderate","min_gts_temp":"-73","max_gts_temp":"11"},{"id":"804","terrestrial_date":"2015-01-07","sol":"861","ls":"267","season":"Month 9","min_temp":"-70","max_temp":"-7","pressure":"917","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:01","sunset":"18:19","local_uv_irradiance_index":"Moderate","min_gts_temp":"-71","max_gts_temp":"3"},{"id":"803","terrestrial_date":"2015-01-06","sol":"860","ls":"266","season":"Month 9","min_temp":"-69","max_temp":"-8","pressure":"916","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:00","sunset":"18:18","local_uv_irradiance_index":"Moderate","min_gts_temp":"-71","max_gts_temp":"3"},{"id":"802","terrestrial_date":"2015-01-05","sol":"859","ls":"266","season":"Month 9","min_temp":"-72","max_temp":"-8","pressure":"915","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:00","sunset":"18:18","local_uv_irradiance_index":"Moderate","min_gts_temp":"-73","max_gts_temp":"3"},{"id":"800","terrestrial_date":"2015-01-04","sol":"858","ls":"265","season":"Month 9","min_temp":"-73","max_temp":"-6","pressure":"913","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:59","sunset":"18:17","local_uv_irradiance_index":"Moderate","min_gts_temp":"-72","max_gts_temp":"4"},{"id":"801","terrestrial_date":"2015-01-03","sol":"857","ls":"264","season":"Month 9","min_temp":"-76","max_temp":"0","pressure":"911","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:58","sunset":"18:16","local_uv_irradiance_index":"Moderate","min_gts_temp":"-72","max_gts_temp":"3"},{"id":"795","terrestrial_date":"2015-01-02","sol":"856","ls":"264","season":"Month 9","min_temp":"-68","max_temp":"-3","pressure":"912","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:58","sunset":"18:15","local_uv_irradiance_index":"Moderate","min_gts_temp":"-70","max_gts_temp":"3"},{"id":"799","terrestrial_date":"2015-01-01","sol":"855","ls":"263","season":"Month 9","min_temp":"-69","max_temp":"0","pressure":"912","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:57","sunset":"18:15","local_uv_irradiance_index":"Low","min_gts_temp":"-70","max_gts_temp":"3"},{"id":"798","terrestrial_date":"2014-12-31","sol":"854","ls":"262","season":"Month 9","min_temp":"-66","max_temp":"3","pressure":"910","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:56","sunset":"18:14","local_uv_irradiance_index":"Low","min_gts_temp":"-70","max_gts_temp":"2"},{"id":"796","terrestrial_date":"2014-12-30","sol":"853","ls":"262","season":"Month 9","min_temp":"-67","max_temp":"0","pressure":"913","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:55","sunset":"18:13","local_uv_irradiance_index":"Low","min_gts_temp":"-69","max_gts_temp":"2"},{"id":"797","terrestrial_date":"2014-12-29","sol":"852","ls":"261","season":"Month 9","min_temp":"-73","max_temp":"-3","pressure":"916","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:55","sunset":"18:12","local_uv_irradiance_index":"Moderate","min_gts_temp":"-72","max_gts_temp":"4"},{"id":"794","terrestrial_date":"2014-12-28","sol":"851","ls":"260","season":"Month 9","min_temp":"-68","max_temp":"-6","pressure":"919","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:54","sunset":"18:12","local_uv_irradiance_index":"Low","min_gts_temp":"-71","max_gts_temp":"2"},{"id":"789","terrestrial_date":"2014-12-27","sol":"850","ls":"260","season":"Month 9","min_temp":"-68","max_temp":"-1","pressure":"918","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:53","sunset":"18:11","local_uv_irradiance_index":"Low","min_gts_temp":"-70","max_gts_temp":"2"},{"id":"787","terrestrial_date":"2014-12-26","sol":"849","ls":"259","season":"Month 9","min_temp":"-69","max_temp":"-1","pressure":"918","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:53","sunset":"18:10","local_uv_irradiance_index":"Moderate","min_gts_temp":"-72","max_gts_temp":"2"},{"id":"792","terrestrial_date":"2014-12-25","sol":"848","ls":"258","season":"Month 9","min_temp":"-69","max_temp":"-4","pressure":"918","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:52","sunset":"18:10","local_uv_irradiance_index":"Moderate","min_gts_temp":"-73","max_gts_temp":"3"},{"id":"791","terrestrial_date":"2014-12-24","sol":"847","ls":"258","season":"Month 9","min_temp":"-69","max_temp":"-8","pressure":"918","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:51","sunset":"18:09","local_uv_irradiance_index":"Moderate","min_gts_temp":"-73","max_gts_temp":"4"},{"id":"785","terrestrial_date":"2014-12-23","sol":"846","ls":"257","season":"Month 9","min_temp":"-74","max_temp":"-7","pressure":"925","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:51","sunset":"18:08","local_uv_irradiance_index":"Moderate","min_gts_temp":"-73","max_gts_temp":"3"},{"id":"786","terrestrial_date":"2014-12-22","sol":"845","ls":"257","season":"Month 9","min_temp":"-73","max_temp":"0","pressure":"920","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:50","sunset":"18:07","local_uv_irradiance_index":"Moderate","min_gts_temp":"-71","max_gts_temp":"3"},{"id":"788","terrestrial_date":"2014-12-21","sol":"844","ls":"256","season":"Month 9","min_temp":"-68","max_temp":"5","pressure":"917","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:49","sunset":"18:07","local_uv_irradiance_index":"Low","min_gts_temp":"-70","max_gts_temp":"3"},{"id":"793","terrestrial_date":"2014-12-20","sol":"843","ls":"255","season":"Month 9","min_temp":"-69","max_temp":"1","pressure":"916","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:49","sunset":"18:06","local_uv_irradiance_index":"Low","min_gts_temp":"-72","max_gts_temp":"4"},{"id":"790","terrestrial_date":"2014-12-19","sol":"842","ls":"255","season":"Month 9","min_temp":"-69","max_temp":"-6","pressure":"914","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:48","sunset":"18:05","local_uv_irradiance_index":"Moderate","min_gts_temp":"-70","max_gts_temp":"5"},{"id":"784","terrestrial_date":"2014-12-18","sol":"841","ls":"254","season":"Month 9","min_temp":"-75","max_temp":"-7","pressure":"914","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:47","sunset":"18:04","local_uv_irradiance_index":"Low","min_gts_temp":"-71","max_gts_temp":"5"},{"id":"783","terrestrial_date":"2014-12-17","sol":"840","ls":"253","season":"Month 9","min_temp":"-74","max_temp":"-4","pressure":"913","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:47","sunset":"18:04","local_uv_irradiance_index":"Low","min_gts_temp":"-71","max_gts_temp":"3"},{"id":"781","terrestrial_date":"2014-12-16","sol":"839","ls":"253","season":"Month 9","min_temp":"-70","max_temp":"-1","pressure":"917","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:46","sunset":"18:03","local_uv_irradiance_index":"Low","min_gts_temp":"-70","max_gts_temp":"2"},{"id":"782","terrestrial_date":"2014-12-15","sol":"838","ls":"252","season":"Month 9","min_temp":"-69","max_temp":"-1","pressure":"917","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:45","sunset":"18:02","local_uv_irradiance_index":"Low","min_gts_temp":"-70","max_gts_temp":"4"},{"id":"779","terrestrial_date":"2014-12-14","sol":"837","ls":"251","season":"Month 9","min_temp":"-69","max_temp":"4","pressure":"916","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:45","sunset":"18:02","local_uv_irradiance_index":"Moderate","min_gts_temp":"-71","max_gts_temp":"6"},{"id":"778","terrestrial_date":"2014-12-13","sol":"836","ls":"251","season":"Month 9","min_temp":"-69","max_temp":"-1","pressure":"916","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:44","sunset":"18:01","local_uv_irradiance_index":"Moderate","min_gts_temp":"-72","max_gts_temp":"5"},{"id":"780","terrestrial_date":"2014-12-12","sol":"835","ls":"250","season":"Month 9","min_temp":"-69","max_temp":"-1","pressure":"917","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:44","sunset":"18:00","local_uv_irradiance_index":"High","min_gts_temp":"-72","max_gts_temp":"4"},{"id":"777","terrestrial_date":"2014-12-11","sol":"834","ls":"249","season":"Month 9","min_temp":"-68","max_temp":"-3","pressure":"918","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:43","sunset":"18:00","local_uv_irradiance_index":"High","min_gts_temp":"-72","max_gts_temp":"4"},{"id":"776","terrestrial_date":"2014-12-09","sol":"833","ls":"249","season":"Month 9","min_temp":"-69","max_temp":"-1","pressure":"923","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:42","sunset":"17:59","local_uv_irradiance_index":"Moderate","min_gts_temp":"-72","max_gts_temp":"4"},{"id":"775","terrestrial_date":"2014-12-08","sol":"832","ls":"248","season":"Month 9","min_temp":"-68","max_temp":"-2","pressure":"924","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:42","sunset":"17:58","local_uv_irradiance_index":"Moderate","min_gts_temp":"-70","max_gts_temp":"4"},{"id":"774","terrestrial_date":"2014-12-07","sol":"831","ls":"247","season":"Month 9","min_temp":"-68","max_temp":"-6","pressure":"920","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:41","sunset":"17:57","local_uv_irradiance_index":"Moderate","min_gts_temp":"-70","max_gts_temp":"3"},{"id":"772","terrestrial_date":"2014-12-06","sol":"830","ls":"247","season":"Month 9","min_temp":"-68","max_temp":"-1","pressure":"918","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:41","sunset":"17:57","local_uv_irradiance_index":"Moderate","min_gts_temp":"-70","max_gts_temp":"3"},{"id":"773","terrestrial_date":"2014-12-05","sol":"829","ls":"246","season":"Month 9","min_temp":"-71","max_temp":"-2","pressure":"914","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:40","sunset":"17:56","local_uv_irradiance_index":"Moderate","min_gts_temp":"-70","max_gts_temp":"3"},{"id":"771","terrestrial_date":"2014-12-04","sol":"828","ls":"245","season":"Month 9","min_temp":"-71","max_temp":"-1","pressure":"914","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:39","sunset":"17:55","local_uv_irradiance_index":"Moderate","min_gts_temp":"-69","max_gts_temp":"3"},{"id":"769","terrestrial_date":"2014-12-03","sol":"827","ls":"245","season":"Month 9","min_temp":"-68","max_temp":"0","pressure":"911","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:39","sunset":"17:55","local_uv_irradiance_index":"Moderate","min_gts_temp":"-68","max_gts_temp":"2"},{"id":"770","terrestrial_date":"2014-12-02","sol":"826","ls":"244","season":"Month 9","min_temp":"-68","max_temp":"0","pressure":"914","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:38","sunset":"17:54","local_uv_irradiance_index":"Moderate","min_gts_temp":"-70","max_gts_temp":"3"},{"id":"767","terrestrial_date":"2014-12-01","sol":"825","ls":"243","season":"Month 9","min_temp":"-67","max_temp":"1","pressure":"914","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:38","sunset":"17:53","local_uv_irradiance_index":"Moderate","min_gts_temp":"-69","max_gts_temp":"3"},{"id":"768","terrestrial_date":"2014-11-30","sol":"824","ls":"243","season":"Month 9","min_temp":"-67","max_temp":"2","pressure":"914","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:37","sunset":"17:53","local_uv_irradiance_index":"Moderate","min_gts_temp":"-69","max_gts_temp":"2"},{"id":"766","terrestrial_date":"2014-11-29","sol":"823","ls":"242","season":"Month 9","min_temp":"-69","max_temp":"0","pressure":"912","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:37","sunset":"17:52","local_uv_irradiance_index":"Moderate","min_gts_temp":"-71","max_gts_temp":"2"},{"id":"764","terrestrial_date":"2014-11-28","sol":"822","ls":"242","season":"Month 9","min_temp":"-71","max_temp":"1","pressure":"911","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:36","sunset":"17:51","local_uv_irradiance_index":"Moderate","min_gts_temp":"-71","max_gts_temp":"3"},{"id":"762","terrestrial_date":"2014-11-27","sol":"821","ls":"241","season":"Month 9","min_temp":"-68","max_temp":"2","pressure":"910","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:35","sunset":"17:51","local_uv_irradiance_index":"Moderate","min_gts_temp":"-69","max_gts_temp":"1"},{"id":"765","terrestrial_date":"2014-11-26","sol":"820","ls":"240","season":"Month 9","min_temp":"-68","max_temp":"-1","pressure":"910","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:35","sunset":"17:50","local_uv_irradiance_index":"Moderate","min_gts_temp":"-69","max_gts_temp":"2"},{"id":"763","terrestrial_date":"2014-11-25","sol":"819","ls":"240","season":"Month 9","min_temp":"-71","max_temp":"0","pressure":"913","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:34","sunset":"17:50","local_uv_irradiance_index":"Moderate","min_gts_temp":"-70","max_gts_temp":"4"},{"id":"760","terrestrial_date":"2014-11-24","sol":"818","ls":"239","season":"Month 8","min_temp":"-68","max_temp":"0","pressure":"914","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:34","sunset":"17:49","local_uv_irradiance_index":"Moderate","min_gts_temp":"-72","max_gts_temp":"4"},{"id":"761","terrestrial_date":"2014-11-23","sol":"817","ls":"238","season":"Month 8","min_temp":"-69","max_temp":"0","pressure":"913","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:33","sunset":"17:48","local_uv_irradiance_index":"Moderate","min_gts_temp":"-70","max_gts_temp":"2"},{"id":"758","terrestrial_date":"2014-11-22","sol":"816","ls":"238","season":"Month 8","min_temp":"-74","max_temp":"-7","pressure":"909","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:33","sunset":"17:48","local_uv_irradiance_index":"Moderate","min_gts_temp":"-69","max_gts_temp":"3"},{"id":"759","terrestrial_date":"2014-11-21","sol":"815","ls":"237","season":"Month 8","min_temp":"-69","max_temp":"-8","pressure":"905","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:32","sunset":"17:47","local_uv_irradiance_index":"Moderate","min_gts_temp":"-69","max_gts_temp":"3"},{"id":"757","terrestrial_date":"2014-11-20","sol":"814","ls":"236","season":"Month 8","min_temp":"-68","max_temp":"-3","pressure":"902","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:32","sunset":"17:46","local_uv_irradiance_index":"Moderate","min_gts_temp":"-70","max_gts_temp":"3"},{"id":"756","terrestrial_date":"2014-11-19","sol":"813","ls":"236","season":"Month 8","min_temp":"-67","max_temp":"2","pressure":"904","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:31","sunset":"17:46","local_uv_irradiance_index":"Moderate","min_gts_temp":"-69","max_gts_temp":"2"},{"id":"755","terrestrial_date":"2014-11-18","sol":"812","ls":"235","season":"Month 8","min_temp":"-75","max_temp":"3","pressure":"902","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:31","sunset":"17:45","local_uv_irradiance_index":"Moderate","min_gts_temp":"-72","max_gts_temp":"4"},{"id":"754","terrestrial_date":"2014-11-17","sol":"811","ls":"234","season":"Month 8","min_temp":"-71","max_temp":"-6","pressure":"900","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:30","sunset":"17:45","local_uv_irradiance_index":"Moderate","min_gts_temp":"-71","max_gts_temp":"4"},{"id":"751","terrestrial_date":"2014-11-16","sol":"810","ls":"234","season":"Month 8","min_temp":"-69","max_temp":"3","pressure":"897","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:30","sunset":"17:44","local_uv_irradiance_index":"Moderate","min_gts_temp":"-71","max_gts_temp":"3"},{"id":"753","terrestrial_date":"2014-11-15","sol":"809","ls":"233","season":"Month 8","min_temp":"-70","max_temp":"-2","pressure":"895","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:30","sunset":"17:43","local_uv_irradiance_index":"Moderate","min_gts_temp":"-70","max_gts_temp":"4"},{"id":"752","terrestrial_date":"2014-11-14","sol":"808","ls":"232","season":"Month 8","min_temp":"-67","max_temp":"7","pressure":"893","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:29","sunset":"17:43","local_uv_irradiance_index":"Moderate","min_gts_temp":"-71","max_gts_temp":"3"},{"id":"749","terrestrial_date":"2014-11-13","sol":"807","ls":"232","season":"Month 8","min_temp":"-71","max_temp":"1","pressure":"892","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:29","sunset":"17:42","local_uv_irradiance_index":"Moderate","min_gts_temp":"-73","max_gts_temp":"11"},{"id":"750","terrestrial_date":"2014-11-12","sol":"806","ls":"231","season":"Month 8","min_temp":"-69","max_temp":"2","pressure":"893","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:28","sunset":"17:42","local_uv_irradiance_index":"Moderate","min_gts_temp":"-75","max_gts_temp":"12"},{"id":"748","terrestrial_date":"2014-11-11","sol":"805","ls":"231","season":"Month 8","min_temp":"-70","max_temp":"1","pressure":"893","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:28","sunset":"17:41","local_uv_irradiance_index":"Moderate","min_gts_temp":"-75","max_gts_temp":"12"},{"id":"747","terrestrial_date":"2014-11-10","sol":"804","ls":"230","season":"Month 8","min_temp":"-73","max_temp":"0","pressure":"891","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:28","sunset":"17:41","local_uv_irradiance_index":"Moderate","min_gts_temp":"-73","max_gts_temp":"12"},{"id":"744","terrestrial_date":"2014-11-09","sol":"803","ls":"229","season":"Month 8","min_temp":"-69","max_temp":"0","pressure":"888","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:27","sunset":"17:40","local_uv_irradiance_index":"Moderate","min_gts_temp":"-73","max_gts_temp":"13"},{"id":"745","terrestrial_date":"2014-11-08","sol":"802","ls":"229","season":"Month 8","min_temp":"-69","max_temp":"7","pressure":"887","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:27","sunset":"17:40","local_uv_irradiance_index":"Moderate","min_gts_temp":"-73","max_gts_temp":"13"},{"id":"746","terrestrial_date":"2014-11-07","sol":"801","ls":"228","season":"Month 8","min_temp":"-70","max_temp":"3","pressure":"884","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:26","sunset":"17:39","local_uv_irradiance_index":"Moderate","min_gts_temp":"-73","max_gts_temp":"13"},{"id":"742","terrestrial_date":"2014-11-06","sol":"800","ls":"227","season":"Month 8","min_temp":"-67","max_temp":"-2","pressure":"884","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:26","sunset":"17:39","local_uv_irradiance_index":"Moderate","min_gts_temp":"-73","max_gts_temp":"13"},{"id":"743","terrestrial_date":"2014-11-05","sol":"799","ls":"227","season":"Month 8","min_temp":"-70","max_temp":"-4","pressure":"884","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:26","sunset":"17:38","local_uv_irradiance_index":"Moderate","min_gts_temp":"-74","max_gts_temp":"14"},{"id":"741","terrestrial_date":"2014-11-04","sol":"798","ls":"226","season":"Month 8","min_temp":"-67","max_temp":"0","pressure":"883","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:25","sunset":"17:38","local_uv_irradiance_index":"Moderate","min_gts_temp":"-72","max_gts_temp":"10"},{"id":"740","terrestrial_date":"2014-11-03","sol":"797","ls":"225","season":"Month 8","min_temp":"-65","max_temp":"-1","pressure":"879","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:25","sunset":"17:37","local_uv_irradiance_index":"Moderate","min_gts_temp":"-67","max_gts_temp":"11"},{"id":"737","terrestrial_date":"2014-11-01","sol":"796","ls":"225","season":"Month 8","min_temp":"-65","max_temp":"1","pressure":"877","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:25","sunset":"17:37","local_uv_irradiance_index":"Moderate","min_gts_temp":"-67","max_gts_temp":"5"},{"id":"738","terrestrial_date":"2014-10-31","sol":"795","ls":"224","season":"Month 8","min_temp":"-68","max_temp":"1","pressure":"875","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:24","sunset":"17:36","local_uv_irradiance_index":"Moderate","min_gts_temp":"-68","max_gts_temp":"6"},{"id":"739","terrestrial_date":"2014-10-30","sol":"794","ls":"223","season":"Month 8","min_temp":"-68","max_temp":"-5","pressure":"874","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:24","sunset":"17:36","local_uv_irradiance_index":"Moderate","min_gts_temp":"-68","max_gts_temp":"6"},{"id":"736","terrestrial_date":"2014-10-29","sol":"793","ls":"223","season":"Month 8","min_temp":"-71","max_temp":"0","pressure":"877","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:24","sunset":"17:35","local_uv_irradiance_index":"Moderate","min_gts_temp":"-69","max_gts_temp":"4"},{"id":"735","terrestrial_date":"2014-10-28","sol":"792","ls":"222","season":"Month 8","min_temp":"-71","max_temp":"0","pressure":"873","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:23","sunset":"17:35","local_uv_irradiance_index":"Moderate","min_gts_temp":"-69","max_gts_temp":"3"},{"id":"734","terrestrial_date":"2014-10-27","sol":"791","ls":"222","season":"Month 8","min_temp":"-70","max_temp":"0","pressure":"867","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:23","sunset":"17:34","local_uv_irradiance_index":"Moderate","min_gts_temp":"-70","max_gts_temp":"4"},{"id":"733","terrestrial_date":"2014-10-26","sol":"790","ls":"221","season":"Month 8","min_temp":"-68","max_temp":"2","pressure":"864","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:23","sunset":"17:34","local_uv_irradiance_index":"Moderate","min_gts_temp":"-69","max_gts_temp":"7"},{"id":"730","terrestrial_date":"2014-10-25","sol":"789","ls":"220","season":"Month 8","min_temp":"-68","max_temp":"-4","pressure":"862","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:22","sunset":"17:33","local_uv_irradiance_index":"Moderate","min_gts_temp":"-69","max_gts_temp":"5"},{"id":"731","terrestrial_date":"2014-10-24","sol":"788","ls":"220","season":"Month 8","min_temp":"-68","max_temp":"-5","pressure":"862","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:22","sunset":"17:33","local_uv_irradiance_index":"Moderate","min_gts_temp":"-70","max_gts_temp":"5"},{"id":"732","terrestrial_date":"2014-10-23","sol":"787","ls":"219","season":"Month 8","min_temp":"-73","max_temp":"-7","pressure":"860","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:22","sunset":"17:33","local_uv_irradiance_index":"Moderate","min_gts_temp":"-70","max_gts_temp":"5"},{"id":"729","terrestrial_date":"2014-10-22","sol":"786","ls":"218","season":"Month 8","min_temp":"-74","max_temp":"-4","pressure":"861","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:22","sunset":"17:32","local_uv_irradiance_index":"Moderate","min_gts_temp":"-69","max_gts_temp":"5"},{"id":"728","terrestrial_date":"2014-10-21","sol":"785","ls":"218","season":"Month 8","min_temp":"-74","max_temp":"-1","pressure":"858","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:21","sunset":"17:32","local_uv_irradiance_index":"Moderate","min_gts_temp":"-70","max_gts_temp":"6"},{"id":"726","terrestrial_date":"2014-10-20","sol":"784","ls":"217","season":"Month 8","min_temp":"-69","max_temp":"-4","pressure":"857","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:21","sunset":"17:31","local_uv_irradiance_index":"Moderate","min_gts_temp":"-70","max_gts_temp":"9"},{"id":"727","terrestrial_date":"2014-10-19","sol":"783","ls":"216","season":"Month 8","min_temp":"-69","max_temp":"6","pressure":"853","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:21","sunset":"17:31","local_uv_irradiance_index":"Moderate","min_gts_temp":"-70","max_gts_temp":"8"},{"id":"724","terrestrial_date":"2014-10-18","sol":"782","ls":"216","season":"Month 8","min_temp":"-71","max_temp":"9","pressure":"854","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:21","sunset":"17:31","local_uv_irradiance_index":"Moderate","min_gts_temp":"-70","max_gts_temp":"8"},{"id":"725","terrestrial_date":"2014-10-17","sol":"781","ls":"215","season":"Month 8","min_temp":"-74","max_temp":"-5","pressure":"849","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:21","sunset":"17:30","local_uv_irradiance_index":"Moderate","min_gts_temp":"-73","max_gts_temp":"9"},{"id":"723","terrestrial_date":"2014-10-16","sol":"780","ls":"215","season":"Month 8","min_temp":"-72","max_temp":"0","pressure":"852","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:20","sunset":"17:30","local_uv_irradiance_index":"High","min_gts_temp":"-72","max_gts_temp":"14"},{"id":"722","terrestrial_date":"2014-10-15","sol":"779","ls":"214","season":"Month 8","min_temp":"-71","max_temp":"6","pressure":"845","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:20","sunset":"17:30","local_uv_irradiance_index":"High","min_gts_temp":"-73","max_gts_temp":"14"},{"id":"721","terrestrial_date":"2014-10-14","sol":"778","ls":"213","season":"Month 8","min_temp":"-70","max_temp":"6","pressure":"846","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:20","sunset":"17:29","local_uv_irradiance_index":"High","min_gts_temp":"-72","max_gts_temp":"13"},{"id":"720","terrestrial_date":"2014-10-13","sol":"777","ls":"213","season":"Month 8","min_temp":"-72","max_temp":"8","pressure":"841","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:20","sunset":"17:29","local_uv_irradiance_index":"High","min_gts_temp":"-73","max_gts_temp":"14"},{"id":"718","terrestrial_date":"2014-10-12","sol":"776","ls":"212","season":"Month 8","min_temp":"-72","max_temp":"5","pressure":"841","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:20","sunset":"17:29","local_uv_irradiance_index":"High","min_gts_temp":"-72","max_gts_temp":"13"},{"id":"717","terrestrial_date":"2014-10-11","sol":"775","ls":"211","season":"Month 8","min_temp":"-71","max_temp":"3","pressure":"838","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:19","sunset":"17:28","local_uv_irradiance_index":"High","min_gts_temp":"-74","max_gts_temp":"14"},{"id":"719","terrestrial_date":"2014-10-10","sol":"774","ls":"211","season":"Month 8","min_temp":"-73","max_temp":"1","pressure":"838","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:19","sunset":"17:28","local_uv_irradiance_index":"High","min_gts_temp":"-72","max_gts_temp":"14"},{"id":"716","terrestrial_date":"2014-10-09","sol":"773","ls":"210","season":"Month 8","min_temp":"-70","max_temp":"8","pressure":"838","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:19","sunset":"17:28","local_uv_irradiance_index":"High","min_gts_temp":"-72","max_gts_temp":"14"},{"id":"715","terrestrial_date":"2014-10-08","sol":"772","ls":"210","season":"Month 8","min_temp":"-71","max_temp":"8","pressure":"835","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:19","sunset":"17:27","local_uv_irradiance_index":"High","min_gts_temp":"-72","max_gts_temp":"13"},{"id":"714","terrestrial_date":"2014-10-07","sol":"771","ls":"209","season":"Month 7","min_temp":"-71","max_temp":"9","pressure":"836","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:19","sunset":"17:27","local_uv_irradiance_index":"High","min_gts_temp":"-73","max_gts_temp":"14"},{"id":"713","terrestrial_date":"2014-10-06","sol":"770","ls":"208","season":"Month 7","min_temp":"-70","max_temp":"2","pressure":"829","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:19","sunset":"17:27","local_uv_irradiance_index":"High","min_gts_temp":"-73","max_gts_temp":"14"},{"id":"711","terrestrial_date":"2014-10-05","sol":"769","ls":"208","season":"Month 7","min_temp":"-73","max_temp":"4","pressure":"829","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:19","sunset":"17:26","local_uv_irradiance_index":"High","min_gts_temp":"-75","max_gts_temp":"15"},{"id":"712","terrestrial_date":"2014-10-04","sol":"768","ls":"207","season":"Month 7","min_temp":"-73","max_temp":"2","pressure":"826","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:19","sunset":"17:26","local_uv_irradiance_index":"High","min_gts_temp":"-72","max_gts_temp":"14"},{"id":"710","terrestrial_date":"2014-10-03","sol":"767","ls":"206","season":"Month 7","min_temp":"-71","max_temp":"9","pressure":"824","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:26","local_uv_irradiance_index":"High","min_gts_temp":"-73","max_gts_temp":"14"},{"id":"709","terrestrial_date":"2014-10-02","sol":"766","ls":"206","season":"Month 7","min_temp":"-70","max_temp":"8","pressure":"824","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:26","local_uv_irradiance_index":"High","min_gts_temp":"-72","max_gts_temp":"14"},{"id":"708","terrestrial_date":"2014-10-01","sol":"765","ls":"205","season":"Month 7","min_temp":"-73","max_temp":"5","pressure":"820","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:25","local_uv_irradiance_index":"High","min_gts_temp":"-74","max_gts_temp":"14"},{"id":"707","terrestrial_date":"2014-09-30","sol":"764","ls":"205","season":"Month 7","min_temp":"-73","max_temp":"4","pressure":"821","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:25","local_uv_irradiance_index":"High","min_gts_temp":"-74","max_gts_temp":"14"},{"id":"706","terrestrial_date":"2014-09-29","sol":"763","ls":"204","season":"Month 7","min_temp":"-78","max_temp":"6","pressure":"817","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:25","local_uv_irradiance_index":"High","min_gts_temp":"-75","max_gts_temp":"15"},{"id":"703","terrestrial_date":"2014-09-28","sol":"762","ls":"203","season":"Month 7","min_temp":"-75","max_temp":"7","pressure":"814","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:25","local_uv_irradiance_index":"High","min_gts_temp":"-73","max_gts_temp":"14"},{"id":"705","terrestrial_date":"2014-09-27","sol":"761","ls":"203","season":"Month 7","min_temp":"-72","max_temp":"7","pressure":"814","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:24","local_uv_irradiance_index":"High","min_gts_temp":"-73","max_gts_temp":"14"},{"id":"704","terrestrial_date":"2014-09-25","sol":"760","ls":"202","season":"Month 7","min_temp":"-70","max_temp":"11","pressure":"810","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:24","local_uv_irradiance_index":"High","min_gts_temp":"-73","max_gts_temp":"14"},{"id":"702","terrestrial_date":"2014-09-24","sol":"759","ls":"201","season":"Month 7","min_temp":"-77","max_temp":"8","pressure":"809","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:24","local_uv_irradiance_index":"High","min_gts_temp":"-75","max_gts_temp":"13"},{"id":"701","terrestrial_date":"2014-09-23","sol":"758","ls":"201","season":"Month 7","min_temp":"-71","max_temp":"3","pressure":"810","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:24","local_uv_irradiance_index":"High","min_gts_temp":"-76","max_gts_temp":"14"},{"id":"700","terrestrial_date":"2014-09-22","sol":"757","ls":"200","season":"Month 7","min_temp":"-73","max_temp":"5","pressure":"807","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:24","local_uv_irradiance_index":"High","min_gts_temp":"-75","max_gts_temp":"14"},{"id":"699","terrestrial_date":"2014-09-21","sol":"756","ls":"200","season":"Month 7","min_temp":"-73","max_temp":"3","pressure":"806","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:23","local_uv_irradiance_index":"Moderate","min_gts_temp":"-77","max_gts_temp":"15"},{"id":"697","terrestrial_date":"2014-09-20","sol":"755","ls":"199","season":"Month 7","min_temp":"-74","max_temp":"2","pressure":"806","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:23","local_uv_irradiance_index":"High","min_gts_temp":"-75","max_gts_temp":"14"},{"id":"696","terrestrial_date":"2014-09-19","sol":"754","ls":"198","season":"Month 7","min_temp":"-73","max_temp":"6","pressure":"802","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:23","local_uv_irradiance_index":"High","min_gts_temp":"-75","max_gts_temp":"14"},{"id":"698","terrestrial_date":"2014-09-18","sol":"753","ls":"198","season":"Month 7","min_temp":"-73","max_temp":"8","pressure":"800","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:23","local_uv_irradiance_index":"High","min_gts_temp":"-70","max_gts_temp":"12"},{"id":"694","terrestrial_date":"2014-09-17","sol":"752","ls":"197","season":"Month 7","min_temp":"-71","max_temp":"-3","pressure":"798","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:23","local_uv_irradiance_index":"High","min_gts_temp":"-71","max_gts_temp":"8"},{"id":"695","terrestrial_date":"2014-09-16","sol":"751","ls":"197","season":"Month 7","min_temp":"-72","max_temp":"-6","pressure":"797","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:22","local_uv_irradiance_index":"High","min_gts_temp":"-73","max_gts_temp":"13"},{"id":"693","terrestrial_date":"2014-09-15","sol":"750","ls":"196","season":"Month 7","min_temp":"-72","max_temp":"4","pressure":"796","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:22","local_uv_irradiance_index":"High","min_gts_temp":"-71","max_gts_temp":"8"},{"id":"692","terrestrial_date":"2014-09-14","sol":"749","ls":"195","season":"Month 7","min_temp":"-73","max_temp":"3","pressure":"794","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:22","local_uv_irradiance_index":"High","min_gts_temp":"-70","max_gts_temp":"7"},{"id":"690","terrestrial_date":"2014-09-13","sol":"748","ls":"195","season":"Month 7","min_temp":"-71","max_temp":"5","pressure":"791","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:22","local_uv_irradiance_index":"High","min_gts_temp":"-69","max_gts_temp":"10"},{"id":"691","terrestrial_date":"2014-09-12","sol":"747","ls":"194","season":"Month 7","min_temp":"-73","max_temp":"6","pressure":"789","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:22","local_uv_irradiance_index":"High","min_gts_temp":"-75","max_gts_temp":"15"},{"id":"688","terrestrial_date":"2014-09-11","sol":"746","ls":"194","season":"Month 7","min_temp":"-76","max_temp":"2","pressure":"788","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:22","local_uv_irradiance_index":"High","min_gts_temp":"-79","max_gts_temp":"14"},{"id":"689","terrestrial_date":"2014-09-10","sol":"745","ls":"193","season":"Month 7","min_temp":"-74","max_temp":"1","pressure":"787","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:22","local_uv_irradiance_index":"High","min_gts_temp":"-79","max_gts_temp":"13"},{"id":"687","terrestrial_date":"2014-09-09","sol":"744","ls":"192","season":"Month 7","min_temp":"-75","max_temp":"-2","pressure":"786","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:21","local_uv_irradiance_index":"High","min_gts_temp":"-74","max_gts_temp":"10"},{"id":"686","terrestrial_date":"2014-09-08","sol":"743","ls":"192","season":"Month 7","min_temp":"-72","max_temp":"0","pressure":"786","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:21","local_uv_irradiance_index":"High","min_gts_temp":"-67","max_gts_temp":"14"},{"id":"684","terrestrial_date":"2014-09-07","sol":"742","ls":"191","season":"Month 7","min_temp":"-72","max_temp":"0","pressure":"784","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:21","local_uv_irradiance_index":"High","min_gts_temp":"-68","max_gts_temp":"4"},{"id":"683","terrestrial_date":"2014-09-06","sol":"741","ls":"191","season":"Month 7","min_temp":"-70","max_temp":"-1","pressure":"784","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:21","local_uv_irradiance_index":"High","min_gts_temp":"-68","max_gts_temp":"4"},{"id":"685","terrestrial_date":"2014-09-05","sol":"740","ls":"190","season":"Month 7","min_temp":"-76","max_temp":"-2","pressure":"782","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:21","local_uv_irradiance_index":"High","min_gts_temp":"-65","max_gts_temp":"2"},{"id":"682","terrestrial_date":"2014-09-04","sol":"739","ls":"189","season":"Month 7","min_temp":"-77","max_temp":"-1","pressure":"779","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:21","local_uv_irradiance_index":"High","min_gts_temp":"-72","max_gts_temp":"4"},{"id":"681","terrestrial_date":"2014-09-03","sol":"738","ls":"189","season":"Month 7","min_temp":"-75","max_temp":"0","pressure":"778","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:21","local_uv_irradiance_index":"High","min_gts_temp":"-75","max_gts_temp":"7"},{"id":"680","terrestrial_date":"2014-09-02","sol":"737","ls":"188","season":"Month 7","min_temp":"-74","max_temp":"3","pressure":"777","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:21","local_uv_irradiance_index":"High","min_gts_temp":"-76","max_gts_temp":"9"},{"id":"679","terrestrial_date":"2014-09-01","sol":"736","ls":"188","season":"Month 7","min_temp":"-76","max_temp":"0","pressure":"776","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:21","local_uv_irradiance_index":"High","min_gts_temp":"-76","max_gts_temp":"9"},{"id":"678","terrestrial_date":"2014-08-31","sol":"735","ls":"187","season":"Month 7","min_temp":"-73","max_temp":"1","pressure":"776","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:21","local_uv_irradiance_index":"High","min_gts_temp":"-76","max_gts_temp":"14"},{"id":"675","terrestrial_date":"2014-08-30","sol":"734","ls":"186","season":"Month 7","min_temp":"-73","max_temp":"-3","pressure":"776","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:20","local_uv_irradiance_index":"High","min_gts_temp":"-76","max_gts_temp":"10"},{"id":"674","terrestrial_date":"2014-08-29","sol":"733","ls":"186","season":"Month 7","min_temp":"-74","max_temp":"-2","pressure":"773","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:19","sunset":"17:20","local_uv_irradiance_index":"High","min_gts_temp":"-79","max_gts_temp":"15"},{"id":"677","terrestrial_date":"2014-08-28","sol":"732","ls":"185","season":"Month 7","min_temp":"-75","max_temp":"-8","pressure":"773","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:19","sunset":"17:20","local_uv_irradiance_index":"High","min_gts_temp":"-77","max_gts_temp":"13"},{"id":"673","terrestrial_date":"2014-08-27","sol":"731","ls":"185","season":"Month 7","min_temp":"-75","max_temp":"-4","pressure":"771","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:19","sunset":"17:20","local_uv_irradiance_index":"High","min_gts_temp":"-81","max_gts_temp":"12"},{"id":"676","terrestrial_date":"2014-08-26","sol":"730","ls":"184","season":"Month 7","min_temp":"-76","max_temp":"3","pressure":"770","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:19","sunset":"17:20","local_uv_irradiance_index":"High","min_gts_temp":"-81","max_gts_temp":"12"},{"id":"672","terrestrial_date":"2014-08-25","sol":"729","ls":"183","season":"Month 7","min_temp":"-75","max_temp":"3","pressure":"768","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:19","sunset":"17:20","local_uv_irradiance_index":"High","min_gts_temp":"-81","max_gts_temp":"13"},{"id":"670","terrestrial_date":"2014-08-24","sol":"728","ls":"183","season":"Month 7","min_temp":"-74","max_temp":"1","pressure":"766","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:19","sunset":"17:20","local_uv_irradiance_index":"High","min_gts_temp":"-81","max_gts_temp":"14"},{"id":"671","terrestrial_date":"2014-08-23","sol":"727","ls":"182","season":"Month 7","min_temp":"-76","max_temp":"1","pressure":"766","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:19","sunset":"17:20","local_uv_irradiance_index":"High","min_gts_temp":"-84","max_gts_temp":"14"},{"id":"668","terrestrial_date":"2014-08-22","sol":"726","ls":"182","season":"Month 7","min_temp":"-75","max_temp":"1","pressure":"766","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:19","sunset":"17:20","local_uv_irradiance_index":"High","min_gts_temp":"-80","max_gts_temp":"14"},{"id":"669","terrestrial_date":"2014-08-21","sol":"725","ls":"181","season":"Month 7","min_temp":"-73","max_temp":"-5","pressure":"764","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:19","sunset":"17:20","local_uv_irradiance_index":"High","min_gts_temp":"-80","max_gts_temp":"13"},{"id":"667","terrestrial_date":"2014-08-19","sol":"724","ls":"181","season":"Month 7","min_temp":"-74","max_temp":"1","pressure":"763","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:20","sunset":"17:20","local_uv_irradiance_index":"High","min_gts_temp":"-81","max_gts_temp":"14"},{"id":"666","terrestrial_date":"2014-08-18","sol":"723","ls":"180","season":"Month 7","min_temp":"-73","max_temp":"2","pressure":"761","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:20","sunset":"17:20","local_uv_irradiance_index":"High","min_gts_temp":"-81","max_gts_temp":"14"},{"id":"665","terrestrial_date":"2014-08-17","sol":"722","ls":"179","season":"Month 6","min_temp":"-74","max_temp":"1","pressure":"760","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:20","sunset":"17:20","local_uv_irradiance_index":"High","min_gts_temp":"-81","max_gts_temp":"14"},{"id":"664","terrestrial_date":"2014-08-16","sol":"721","ls":"179","season":"Month 6","min_temp":"-73","max_temp":"-5","pressure":"761","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:20","sunset":"17:20","local_uv_irradiance_index":"High","min_gts_temp":"-81","max_gts_temp":"12"},{"id":"663","terrestrial_date":"2014-08-15","sol":"720","ls":"178","season":"Month 6","min_temp":"-74","max_temp":"-1","pressure":"760","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:20","sunset":"17:20","local_uv_irradiance_index":"High","min_gts_temp":"-81","max_gts_temp":"13"},{"id":"662","terrestrial_date":"2014-08-14","sol":"719","ls":"178","season":"Month 6","min_temp":"-77","max_temp":"-1","pressure":"758","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:20","sunset":"17:20","local_uv_irradiance_index":"High","min_gts_temp":"-82","max_gts_temp":"12"},{"id":"660","terrestrial_date":"2014-08-13","sol":"718","ls":"177","season":"Month 6","min_temp":"-77","max_temp":"3","pressure":"757","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:20","sunset":"17:20","local_uv_irradiance_index":"High","min_gts_temp":"-81","max_gts_temp":"12"},{"id":"661","terrestrial_date":"2014-08-12","sol":"717","ls":"176","season":"Month 6","min_temp":"-76","max_temp":"5","pressure":"756","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:21","sunset":"17:20","local_uv_irradiance_index":"High","min_gts_temp":"-74","max_gts_temp":"11"},{"id":"659","terrestrial_date":"2014-08-11","sol":"716","ls":"176","season":"Month 6","min_temp":"-75","max_temp":"2","pressure":"755","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:21","sunset":"17:20","local_uv_irradiance_index":"High","min_gts_temp":"-75","max_gts_temp":"6"},{"id":"657","terrestrial_date":"2014-08-10","sol":"715","ls":"175","season":"Month 6","min_temp":"-73","max_temp":"2","pressure":"755","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:21","sunset":"17:20","local_uv_irradiance_index":"Moderate","min_gts_temp":"-75","max_gts_temp":"3"},{"id":"655","terrestrial_date":"2014-08-09","sol":"714","ls":"175","season":"Month 6","min_temp":"-76","max_temp":"2","pressure":"754","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:21","sunset":"17:20","local_uv_irradiance_index":"Moderate","min_gts_temp":"-79","max_gts_temp":"8"},{"id":"656","terrestrial_date":"2014-08-08","sol":"713","ls":"174","season":"Month 6","min_temp":"-76","max_temp":"-12","pressure":"754","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:21","sunset":"17:20","local_uv_irradiance_index":"High","min_gts_temp":"-87","max_gts_temp":"19"},{"id":"658","terrestrial_date":"2014-08-07","sol":"712","ls":"174","season":"Month 6","min_temp":"-77","max_temp":"-10","pressure":"752","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:21","sunset":"17:20","local_uv_irradiance_index":"High","min_gts_temp":"-87","max_gts_temp":"18"},{"id":"654","terrestrial_date":"2014-08-06","sol":"711","ls":"173","season":"Month 6","min_temp":"-75","max_temp":"-11","pressure":"751","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:22","sunset":"17:20","local_uv_irradiance_index":"High","min_gts_temp":"-86","max_gts_temp":"18"},{"id":"653","terrestrial_date":"2014-08-05","sol":"710","ls":"172","season":"Month 6","min_temp":"-77","max_temp":"-12","pressure":"751","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:22","sunset":"17:20","local_uv_irradiance_index":"High","min_gts_temp":"-88","max_gts_temp":"15"},{"id":"652","terrestrial_date":"2014-08-04","sol":"709","ls":"172","season":"Month 6","min_temp":"-73","max_temp":"-11","pressure":"750","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:22","sunset":"17:20","local_uv_irradiance_index":"High","min_gts_temp":"-78","max_gts_temp":"16"},{"id":"649","terrestrial_date":"2014-08-03","sol":"708","ls":"171","season":"Month 6","min_temp":"-76","max_temp":"1","pressure":"749","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:22","sunset":"17:20","local_uv_irradiance_index":"High","min_gts_temp":"-76","max_gts_temp":"5"},{"id":"651","terrestrial_date":"2014-08-02","sol":"707","ls":"171","season":"Month 6","min_temp":"-76","max_temp":"1","pressure":"749","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:22","sunset":"17:20","local_uv_irradiance_index":"High","min_gts_temp":"-77","max_gts_temp":"5"},{"id":"650","terrestrial_date":"2014-08-01","sol":"706","ls":"170","season":"Month 6","min_temp":"-76","max_temp":"0","pressure":"748","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:22","sunset":"17:20","local_uv_irradiance_index":"High","min_gts_temp":"-82","max_gts_temp":"8"},{"id":"648","terrestrial_date":"2014-07-31","sol":"705","ls":"170","season":"Month 6","min_temp":"-76","max_temp":"-9","pressure":"746","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:23","sunset":"17:20","local_uv_irradiance_index":"High","min_gts_temp":"-84","max_gts_temp":"14"},{"id":"647","terrestrial_date":"2014-07-30","sol":"704","ls":"169","season":"Month 6","min_temp":"-75","max_temp":"-5","pressure":"747","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:23","sunset":"17:20","local_uv_irradiance_index":"High","min_gts_temp":"-85","max_gts_temp":"12"},{"id":"646","terrestrial_date":"2014-07-29","sol":"703","ls":"169","season":"Month 6","min_temp":"-75","max_temp":"-6","pressure":"747","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:23","sunset":"17:20","local_uv_irradiance_index":"High","min_gts_temp":"-78","max_gts_temp":"9"},{"id":"645","terrestrial_date":"2014-07-28","sol":"702","ls":"168","season":"Month 6","min_temp":"-76","max_temp":"-2","pressure":"745","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:23","sunset":"17:20","local_uv_irradiance_index":"High","min_gts_temp":"-81","max_gts_temp":"8"},{"id":"644","terrestrial_date":"2014-07-27","sol":"701","ls":"167","season":"Month 6","min_temp":"-77","max_temp":"-1","pressure":"745","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:23","sunset":"17:20","local_uv_irradiance_index":"High","min_gts_temp":"-82","max_gts_temp":"8"},{"id":"643","terrestrial_date":"2014-07-26","sol":"700","ls":"167","season":"Month 6","min_temp":"-75","max_temp":"-2","pressure":"745","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:24","sunset":"17:20","local_uv_irradiance_index":"High","min_gts_temp":"-83","max_gts_temp":"8"},{"id":"642","terrestrial_date":"2014-07-25","sol":"699","ls":"166","season":"Month 6","min_temp":"-76","max_temp":"-2","pressure":"744","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:24","sunset":"17:20","local_uv_irradiance_index":"High","min_gts_temp":"-81","max_gts_temp":"9"},{"id":"641","terrestrial_date":"2014-07-24","sol":"698","ls":"166","season":"Month 6","min_temp":"-77","max_temp":"-3","pressure":"743","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:24","sunset":"17:20","local_uv_irradiance_index":"High","min_gts_temp":"-83","max_gts_temp":"9"},{"id":"640","terrestrial_date":"2014-07-23","sol":"697","ls":"165","season":"Month 6","min_temp":"-74","max_temp":"-4","pressure":"743","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:24","sunset":"17:20","local_uv_irradiance_index":"High","min_gts_temp":"-84","max_gts_temp":"8"},{"id":"639","terrestrial_date":"2014-07-22","sol":"696","ls":"165","season":"Month 6","min_temp":"-75","max_temp":"-2","pressure":"743","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:24","sunset":"17:20","local_uv_irradiance_index":"High","min_gts_temp":"-86","max_gts_temp":"9"},{"id":"638","terrestrial_date":"2014-07-21","sol":"695","ls":"164","season":"Month 6","min_temp":"-76","max_temp":"-1","pressure":"741","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:25","sunset":"17:20","local_uv_irradiance_index":"High","min_gts_temp":"-75","max_gts_temp":"11"},{"id":"635","terrestrial_date":"2014-07-20","sol":"694","ls":"164","season":"Month 6","min_temp":"-75","max_temp":"-1","pressure":"740","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:25","sunset":"17:20","local_uv_irradiance_index":"High","min_gts_temp":"-75","max_gts_temp":"2"},{"id":"636","terrestrial_date":"2014-07-19","sol":"693","ls":"163","season":"Month 6","min_temp":"-76","max_temp":"-2","pressure":"741","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:25","sunset":"17:20","local_uv_irradiance_index":"High","min_gts_temp":"-75","max_gts_temp":"2"},{"id":"637","terrestrial_date":"2014-07-18","sol":"692","ls":"162","season":"Month 6","min_temp":"-76","max_temp":"-4","pressure":"742","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:25","sunset":"17:21","local_uv_irradiance_index":"High","min_gts_temp":"-80","max_gts_temp":"6"},{"id":"634","terrestrial_date":"2014-07-17","sol":"691","ls":"162","season":"Month 6","min_temp":"-76","max_temp":"-8","pressure":"741","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:25","sunset":"17:21","local_uv_irradiance_index":"High","min_gts_temp":"-81","max_gts_temp":"4"},{"id":"633","terrestrial_date":"2014-07-16","sol":"690","ls":"161","season":"Month 6","min_temp":"-77","max_temp":"-7","pressure":"741","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:26","sunset":"17:21","local_uv_irradiance_index":"High","min_gts_temp":"-77","max_gts_temp":"5"},{"id":"632","terrestrial_date":"2014-07-15","sol":"689","ls":"161","season":"Month 6","min_temp":"-76","max_temp":"-6","pressure":"742","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:26","sunset":"17:21","local_uv_irradiance_index":"High","min_gts_temp":"-77","max_gts_temp":"6"},{"id":"631","terrestrial_date":"2014-07-14","sol":"688","ls":"160","season":"Month 6","min_temp":"-77","max_temp":"-5","pressure":"741","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:26","sunset":"17:21","local_uv_irradiance_index":"High","min_gts_temp":"-78","max_gts_temp":"8"},{"id":"630","terrestrial_date":"2014-07-12","sol":"687","ls":"160","season":"Month 6","min_temp":"-77","max_temp":"-2","pressure":"739","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:26","sunset":"17:21","local_uv_irradiance_index":"High","min_gts_temp":"-78","max_gts_temp":"3"},{"id":"629","terrestrial_date":"2014-07-11","sol":"686","ls":"159","season":"Month 6","min_temp":"-76","max_temp":"-4","pressure":"738","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:26","sunset":"17:21","local_uv_irradiance_index":"High","min_gts_temp":"-78","max_gts_temp":"2"},{"id":"628","terrestrial_date":"2014-07-10","sol":"685","ls":"159","season":"Month 6","min_temp":"-77","max_temp":"-2","pressure":"740","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:27","sunset":"17:21","local_uv_irradiance_index":"High","min_gts_temp":"-85","max_gts_temp":"8"},{"id":"627","terrestrial_date":"2014-07-09","sol":"684","ls":"158","season":"Month 6","min_temp":"-75","max_temp":"-9","pressure":"741","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:27","sunset":"17:21","local_uv_irradiance_index":"Moderate","min_gts_temp":"-82","max_gts_temp":"6"},{"id":"626","terrestrial_date":"2014-07-08","sol":"683","ls":"158","season":"Month 6","min_temp":"-79","max_temp":"-7","pressure":"741","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:27","sunset":"17:21","local_uv_irradiance_index":"High","min_gts_temp":"-80","max_gts_temp":"8"},{"id":"625","terrestrial_date":"2014-07-07","sol":"682","ls":"157","season":"Month 6","min_temp":"-77","max_temp":"-16","pressure":"739","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:27","sunset":"17:21","local_uv_irradiance_index":"High","min_gts_temp":"-80","max_gts_temp":"2"},{"id":"623","terrestrial_date":"2014-07-06","sol":"681","ls":"156","season":"Month 6","min_temp":"-79","max_temp":"-17","pressure":"738","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:28","sunset":"17:21","local_uv_irradiance_index":"Moderate","min_gts_temp":"-80","max_gts_temp":"2"},{"id":"620","terrestrial_date":"2014-07-05","sol":"680","ls":"156","season":"Month 6","min_temp":"-76","max_temp":"-15","pressure":"740","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:28","sunset":"17:21","local_uv_irradiance_index":"Moderate","min_gts_temp":"-80","max_gts_temp":"1"},{"id":"624","terrestrial_date":"2014-07-04","sol":"679","ls":"155","season":"Month 6","min_temp":"-77","max_temp":"-13","pressure":"738","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:28","sunset":"17:21","local_uv_irradiance_index":"Moderate","min_gts_temp":"-80","max_gts_temp":"1"},{"id":"621","terrestrial_date":"2014-07-03","sol":"678","ls":"155","season":"Month 6","min_temp":"-77","max_temp":"-17","pressure":"739","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:28","sunset":"17:22","local_uv_irradiance_index":"Moderate","min_gts_temp":"-79","max_gts_temp":"-1"},{"id":"622","terrestrial_date":"2014-07-02","sol":"677","ls":"154","season":"Month 6","min_temp":"-78","max_temp":"-12","pressure":"737","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:28","sunset":"17:22","local_uv_irradiance_index":"Moderate","min_gts_temp":"-79","max_gts_temp":"0"},{"id":"619","terrestrial_date":"2014-07-01","sol":"676","ls":"154","season":"Month 6","min_temp":"-80","max_temp":"-17","pressure":"738","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:29","sunset":"17:22","local_uv_irradiance_index":"High","min_gts_temp":"-90","max_gts_temp":"9"},{"id":"618","terrestrial_date":"2014-06-30","sol":"675","ls":"153","season":"Month 6","min_temp":"-80","max_temp":"-12","pressure":"740","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:29","sunset":"17:22","local_uv_irradiance_index":"High","min_gts_temp":"-91","max_gts_temp":"10"},{"id":"617","terrestrial_date":"2014-06-29","sol":"674","ls":"153","season":"Month 6","min_temp":"-80","max_temp":"-17","pressure":"739","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:29","sunset":"17:22","local_uv_irradiance_index":"High","min_gts_temp":"-94","max_gts_temp":"7"},{"id":"616","terrestrial_date":"2014-06-28","sol":"673","ls":"152","season":"Month 6","min_temp":"-78","max_temp":"-16","pressure":"738","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:29","sunset":"17:22","local_uv_irradiance_index":"High","min_gts_temp":"-96","max_gts_temp":"8"},{"id":"615","terrestrial_date":"2014-06-27","sol":"672","ls":"152","season":"Month 6","min_temp":"-78","max_temp":"-16","pressure":"739","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:30","sunset":"17:22","local_uv_irradiance_index":"High","min_gts_temp":"-83","max_gts_temp":"7"},{"id":"614","terrestrial_date":"2014-06-26","sol":"671","ls":"151","season":"Month 6","min_temp":"-79","max_temp":"-15","pressure":"740","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:30","sunset":"17:22","local_uv_irradiance_index":"High","min_gts_temp":"-80","max_gts_temp":"6"},{"id":"612","terrestrial_date":"2014-06-25","sol":"670","ls":"151","season":"Month 6","min_temp":"-79","max_temp":"-7","pressure":"737","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:30","sunset":"17:22","local_uv_irradiance_index":"High","min_gts_temp":"-85","max_gts_temp":"4"},{"id":"613","terrestrial_date":"2014-06-24","sol":"669","ls":"150","season":"Month 6","min_temp":"-84","max_temp":"-4","pressure":"735","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:30","sunset":"17:22","local_uv_irradiance_index":"High","min_gts_temp":"-91","max_gts_temp":"8"},{"id":"611","terrestrial_date":"2014-06-23","sol":"668","ls":"150","season":"Month 6","min_temp":"-75","max_temp":"-4","pressure":"734","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:30","sunset":"17:22","local_uv_irradiance_index":"High","min_gts_temp":"-78","max_gts_temp":"6"},{"id":"606","terrestrial_date":"2014-06-22","sol":"667","ls":"149","season":"Month 5","min_temp":"-76","max_temp":"-7","pressure":"736","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:31","sunset":"17:22","local_uv_irradiance_index":"High","min_gts_temp":"-81","max_gts_temp":"0"},{"id":"608","terrestrial_date":"2014-06-21","sol":"666","ls":"148","season":"Month 5","min_temp":"-77","max_temp":"-7","pressure":"736","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:31","sunset":"17:23","local_uv_irradiance_index":"High","min_gts_temp":"-80","max_gts_temp":"0"},{"id":"610","terrestrial_date":"2014-06-20","sol":"665","ls":"148","season":"Month 5","min_temp":"-78","max_temp":"-4","pressure":"735","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:31","sunset":"17:23","local_uv_irradiance_index":"High","min_gts_temp":"-82","max_gts_temp":"7"},{"id":"607","terrestrial_date":"2014-06-19","sol":"664","ls":"147","season":"Month 5","min_temp":"-78","max_temp":"-8","pressure":"732","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:31","sunset":"17:23","local_uv_irradiance_index":"High","min_gts_temp":"-81","max_gts_temp":"0"},{"id":"609","terrestrial_date":"2014-06-18","sol":"663","ls":"147","season":"Month 5","min_temp":"-77","max_temp":"-10","pressure":"734","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:32","sunset":"17:23","local_uv_irradiance_index":"High","min_gts_temp":"-83","max_gts_temp":"4"},{"id":"605","terrestrial_date":"2014-06-17","sol":"662","ls":"146","season":"Month 5","min_temp":"-77","max_temp":"-11","pressure":"735","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:32","sunset":"17:23","local_uv_irradiance_index":"High","min_gts_temp":"-78","max_gts_temp":"4"},{"id":"604","terrestrial_date":"2014-06-16","sol":"661","ls":"146","season":"Month 5","min_temp":"-76","max_temp":"-10","pressure":"735","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:32","sunset":"17:23","local_uv_irradiance_index":"High","min_gts_temp":"-85","max_gts_temp":"1"},{"id":"601","terrestrial_date":"2014-06-15","sol":"660","ls":"145","season":"Month 5","min_temp":"-78","max_temp":"-12","pressure":"735","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:32","sunset":"17:23","local_uv_irradiance_index":"Moderate","min_gts_temp":"-84","max_gts_temp":"1"},{"id":"603","terrestrial_date":"2014-06-14","sol":"659","ls":"145","season":"Month 5","min_temp":"-77","max_temp":"-14","pressure":"738","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:33","sunset":"17:23","local_uv_irradiance_index":"Moderate","min_gts_temp":"-83","max_gts_temp":"1"},{"id":"602","terrestrial_date":"2014-06-13","sol":"658","ls":"144","season":"Month 5","min_temp":"-79","max_temp":"-10","pressure":"739","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:33","sunset":"17:23","local_uv_irradiance_index":"Moderate","min_gts_temp":"-87","max_gts_temp":"1"},{"id":"600","terrestrial_date":"2014-06-12","sol":"657","ls":"144","season":"Month 5","min_temp":"-79","max_temp":"-6","pressure":"739","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:33","sunset":"17:23","local_uv_irradiance_index":"High","min_gts_temp":"-79","max_gts_temp":"1"},{"id":"599","terrestrial_date":"2014-06-11","sol":"656","ls":"143","season":"Month 5","min_temp":"-80","max_temp":"-13","pressure":"740","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:33","sunset":"17:23","local_uv_irradiance_index":"Moderate","min_gts_temp":"-83","max_gts_temp":"-3"},{"id":"598","terrestrial_date":"2014-06-10","sol":"655","ls":"143","season":"Month 5","min_temp":"-77","max_temp":"-14","pressure":"741","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:33","sunset":"17:24","local_uv_irradiance_index":"High","min_gts_temp":"-81","max_gts_temp":"0"},{"id":"597","terrestrial_date":"2014-06-09","sol":"654","ls":"142","season":"Month 5","min_temp":"-79","max_temp":"-8","pressure":"742","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:34","sunset":"17:24","local_uv_irradiance_index":"High","min_gts_temp":"-79","max_gts_temp":"-4"},{"id":"596","terrestrial_date":"2014-06-08","sol":"653","ls":"142","season":"Month 5","min_temp":"-80","max_temp":"-13","pressure":"744","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:34","sunset":"17:24","local_uv_irradiance_index":"High","min_gts_temp":"-80","max_gts_temp":"-4"},{"id":"594","terrestrial_date":"2014-06-07","sol":"652","ls":"141","season":"Month 5","min_temp":"-79","max_temp":"-12","pressure":"745","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:34","sunset":"17:24","local_uv_irradiance_index":"High","min_gts_temp":"-79","max_gts_temp":"-3"},{"id":"595","terrestrial_date":"2014-06-05","sol":"651","ls":"141","season":"Month 5","min_temp":"-80","max_temp":"-8","pressure":"743","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:34","sunset":"17:24","local_uv_irradiance_index":"High","min_gts_temp":"-84","max_gts_temp":"2"},{"id":"593","terrestrial_date":"2014-06-04","sol":"650","ls":"140","season":"Month 5","min_temp":"-80","max_temp":"-11","pressure":"743","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:35","sunset":"17:24","local_uv_irradiance_index":"High","min_gts_temp":"-85","max_gts_temp":"0"},{"id":"592","terrestrial_date":"2014-06-03","sol":"649","ls":"140","season":"Month 5","min_temp":"-79","max_temp":"-14","pressure":"745","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:35","sunset":"17:24","local_uv_irradiance_index":"High","min_gts_temp":"-85","max_gts_temp":"-3"},{"id":"591","terrestrial_date":"2014-06-02","sol":"648","ls":"139","season":"Month 5","min_temp":"-80","max_temp":"-20","pressure":"746","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:35","sunset":"17:24","local_uv_irradiance_index":"High","min_gts_temp":"-85","max_gts_temp":"-2"},{"id":"590","terrestrial_date":"2014-06-01","sol":"647","ls":"139","season":"Month 5","min_temp":"-79","max_temp":"-19","pressure":"746","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:35","sunset":"17:24","local_uv_irradiance_index":"High","min_gts_temp":"-85","max_gts_temp":"-2"},{"id":"587","terrestrial_date":"2014-05-31","sol":"646","ls":"138","season":"Month 5","min_temp":"-81","max_temp":"-16","pressure":"746","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:35","sunset":"17:24","local_uv_irradiance_index":"High","min_gts_temp":"-87","max_gts_temp":"0"},{"id":"589","terrestrial_date":"2014-05-30","sol":"645","ls":"138","season":"Month 5","min_temp":"-81","max_temp":"-8","pressure":"746","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:36","sunset":"17:25","local_uv_irradiance_index":"High","min_gts_temp":"-86","max_gts_temp":"0"},{"id":"588","terrestrial_date":"2014-05-29","sol":"644","ls":"137","season":"Month 5","min_temp":"-80","max_temp":"-10","pressure":"748","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:36","sunset":"17:25","local_uv_irradiance_index":"High","min_gts_temp":"-84","max_gts_temp":"0"},{"id":"586","terrestrial_date":"2014-05-28","sol":"643","ls":"137","season":"Month 5","min_temp":"-80","max_temp":"-14","pressure":"748","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:36","sunset":"17:25","local_uv_irradiance_index":"High","min_gts_temp":"-88","max_gts_temp":"-2"},{"id":"585","terrestrial_date":"2014-05-27","sol":"642","ls":"136","season":"Month 5","min_temp":"-80","max_temp":"-19","pressure":"749","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:36","sunset":"17:25","local_uv_irradiance_index":"High","min_gts_temp":"-85","max_gts_temp":"-2"},{"id":"584","terrestrial_date":"2014-05-26","sol":"641","ls":"136","season":"Month 5","min_temp":"-81","max_temp":"-19","pressure":"749","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:37","sunset":"17:25","local_uv_irradiance_index":"High","min_gts_temp":"-81","max_gts_temp":"-2"},{"id":"583","terrestrial_date":"2014-05-25","sol":"640","ls":"135","season":"Month 5","min_temp":"-81","max_temp":"-11","pressure":"750","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:37","sunset":"17:25","local_uv_irradiance_index":"Moderate","min_gts_temp":"-81","max_gts_temp":"-7"},{"id":"581","terrestrial_date":"2014-05-24","sol":"639","ls":"135","season":"Month 5","min_temp":"-80","max_temp":"-16","pressure":"750","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:37","sunset":"17:25","local_uv_irradiance_index":"Moderate","min_gts_temp":"-80","max_gts_temp":"-9"},{"id":"579","terrestrial_date":"2014-05-23","sol":"638","ls":"134","season":"Month 5","min_temp":"-80","max_temp":"-12","pressure":"751","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:37","sunset":"17:25","local_uv_irradiance_index":"Moderate","min_gts_temp":"-82","max_gts_temp":"-9"},{"id":"580","terrestrial_date":"2014-05-22","sol":"637","ls":"134","season":"Month 5","min_temp":"-80","max_temp":"-18","pressure":"752","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:37","sunset":"17:25","local_uv_irradiance_index":"Moderate","min_gts_temp":"-85","max_gts_temp":"-5"},{"id":"582","terrestrial_date":"2014-05-21","sol":"636","ls":"133","season":"Month 5","min_temp":"-81","max_temp":"-19","pressure":"752","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:38","sunset":"17:25","local_uv_irradiance_index":"High","min_gts_temp":"-89","max_gts_temp":"0"},{"id":"578","terrestrial_date":"2014-05-20","sol":"635","ls":"133","season":"Month 5","min_temp":"-82","max_temp":"-10","pressure":"754","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:38","sunset":"17:26","local_uv_irradiance_index":"High","min_gts_temp":"-81","max_gts_temp":"-3"},{"id":"577","terrestrial_date":"2014-05-19","sol":"634","ls":"132","season":"Month 5","min_temp":"-82","max_temp":"-20","pressure":"753","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:38","sunset":"17:26","local_uv_irradiance_index":"High","min_gts_temp":"-85","max_gts_temp":"4"},{"id":"574","terrestrial_date":"2014-05-18","sol":"633","ls":"132","season":"Month 5","min_temp":"-81","max_temp":"-15","pressure":"754","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:38","sunset":"17:26","local_uv_irradiance_index":"High","min_gts_temp":"-85","max_gts_temp":"-3"},{"id":"575","terrestrial_date":"2014-05-17","sol":"632","ls":"131","season":"Month 5","min_temp":"-79","max_temp":"-18","pressure":"755","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:38","sunset":"17:26","local_uv_irradiance_index":"High","min_gts_temp":"-86","max_gts_temp":"-4"},{"id":"576","terrestrial_date":"2014-05-16","sol":"631","ls":"131","season":"Month 5","min_temp":"-81","max_temp":"-18","pressure":"756","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:39","sunset":"17:26","local_uv_irradiance_index":"High","min_gts_temp":"-88","max_gts_temp":"-1"},{"id":"573","terrestrial_date":"2014-05-15","sol":"630","ls":"130","season":"Month 5","min_temp":"-82","max_temp":"-20","pressure":"757","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:39","sunset":"17:26","local_uv_irradiance_index":"High","min_gts_temp":"-93","max_gts_temp":"-2"},{"id":"572","terrestrial_date":"2014-05-14","sol":"629","ls":"130","season":"Month 5","min_temp":"-84","max_temp":"-24","pressure":"759","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:39","sunset":"17:26","local_uv_irradiance_index":"Moderate","min_gts_temp":"-94","max_gts_temp":"-3"},{"id":"571","terrestrial_date":"2014-05-13","sol":"628","ls":"129","season":"Month 5","min_temp":"-81","max_temp":"-23","pressure":"759","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:39","sunset":"17:26","local_uv_irradiance_index":"Moderate","min_gts_temp":"-90","max_gts_temp":"-2"},{"id":"570","terrestrial_date":"2014-05-12","sol":"627","ls":"129","season":"Month 5","min_temp":"-82","max_temp":"-19","pressure":"759","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:39","sunset":"17:26","local_uv_irradiance_index":"Moderate","min_gts_temp":"-93","max_gts_temp":"-3"},{"id":"567","terrestrial_date":"2014-05-11","sol":"626","ls":"128","season":"Month 5","min_temp":"-82","max_temp":"-21","pressure":"760","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:40","sunset":"17:26","local_uv_irradiance_index":"Moderate","min_gts_temp":"-94","max_gts_temp":"-3"},{"id":"569","terrestrial_date":"2014-05-10","sol":"625","ls":"128","season":"Month 5","min_temp":"-81","max_temp":"-24","pressure":"761","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:40","sunset":"17:27","local_uv_irradiance_index":"Moderate","min_gts_temp":"-95","max_gts_temp":"-4"},{"id":"568","terrestrial_date":"2014-05-09","sol":"624","ls":"127","season":"Month 5","min_temp":"-82","max_temp":"-19","pressure":"762","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:40","sunset":"17:27","local_uv_irradiance_index":"Moderate","min_gts_temp":"-92","max_gts_temp":"-2"},{"id":"566","terrestrial_date":"2014-05-08","sol":"623","ls":"127","season":"Month 5","min_temp":"-83","max_temp":"-21","pressure":"763","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:40","sunset":"17:27","local_uv_irradiance_index":"Moderate","min_gts_temp":"-92","max_gts_temp":"-2"},{"id":"565","terrestrial_date":"2014-05-07","sol":"622","ls":"126","season":"Month 5","min_temp":"-84","max_temp":"-19","pressure":"763","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:40","sunset":"17:27","local_uv_irradiance_index":"Moderate","min_gts_temp":"-92","max_gts_temp":"-3"},{"id":"564","terrestrial_date":"2014-05-06","sol":"621","ls":"126","season":"Month 5","min_temp":"-82","max_temp":"-21","pressure":"765","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:41","sunset":"17:27","local_uv_irradiance_index":"Moderate","min_gts_temp":"-93","max_gts_temp":"-3"},{"id":"563","terrestrial_date":"2014-05-05","sol":"620","ls":"125","season":"Month 5","min_temp":"-83","max_temp":"-19","pressure":"765","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:41","sunset":"17:27","local_uv_irradiance_index":"Moderate","min_gts_temp":"-96","max_gts_temp":"-4"},{"id":"560","terrestrial_date":"2014-05-04","sol":"619","ls":"125","season":"Month 5","min_temp":"-82","max_temp":"-22","pressure":"766","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:41","sunset":"17:27","local_uv_irradiance_index":"Low","min_gts_temp":"-96","max_gts_temp":"-3"},{"id":"562","terrestrial_date":"2014-05-03","sol":"618","ls":"124","season":"Month 5","min_temp":"-83","max_temp":"-23","pressure":"769","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:41","sunset":"17:27","local_uv_irradiance_index":"Moderate","min_gts_temp":"-94","max_gts_temp":"-3"},{"id":"561","terrestrial_date":"2014-05-02","sol":"617","ls":"124","season":"Month 5","min_temp":"-84","max_temp":"-19","pressure":"769","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:41","sunset":"17:27","local_uv_irradiance_index":"Moderate","min_gts_temp":"-94","max_gts_temp":"-4"},{"id":"559","terrestrial_date":"2014-05-01","sol":"616","ls":"123","season":"Month 5","min_temp":"-82","max_temp":"-19","pressure":"769","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:42","sunset":"17:27","local_uv_irradiance_index":"Moderate","min_gts_temp":"-93","max_gts_temp":"-4"},{"id":"558","terrestrial_date":"2014-04-29","sol":"615","ls":"123","season":"Month 5","min_temp":"-83","max_temp":"-23","pressure":"771","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:42","sunset":"17:28","local_uv_irradiance_index":"Moderate","min_gts_temp":"-93","max_gts_temp":"-4"},{"id":"557","terrestrial_date":"2014-04-28","sol":"614","ls":"122","season":"Month 5","min_temp":"-82","max_temp":"-19","pressure":"772","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:42","sunset":"17:28","local_uv_irradiance_index":"Moderate","min_gts_temp":"-95","max_gts_temp":"-5"},{"id":"556","terrestrial_date":"2014-04-27","sol":"613","ls":"122","season":"Month 5","min_temp":"-84","max_temp":"-21","pressure":"773","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:42","sunset":"17:28","local_uv_irradiance_index":"Moderate","min_gts_temp":"-98","max_gts_temp":"-4"},{"id":"553","terrestrial_date":"2014-04-26","sol":"612","ls":"121","season":"Month 5","min_temp":"-85","max_temp":"-27","pressure":"774","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:42","sunset":"17:28","local_uv_irradiance_index":"Moderate","min_gts_temp":"-95","max_gts_temp":"-4"},{"id":"555","terrestrial_date":"2014-04-25","sol":"611","ls":"121","season":"Month 5","min_temp":"-84","max_temp":"-23","pressure":"775","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:43","sunset":"17:28","local_uv_irradiance_index":"Moderate","min_gts_temp":"-93","max_gts_temp":"-5"},{"id":"554","terrestrial_date":"2014-04-24","sol":"610","ls":"120","season":"Month 5","min_temp":"-85","max_temp":"-20","pressure":"776","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:43","sunset":"17:28","local_uv_irradiance_index":"Moderate","min_gts_temp":"-94","max_gts_temp":"-5"},{"id":"552","terrestrial_date":"2014-04-23","sol":"609","ls":"120","season":"Month 5","min_temp":"-84","max_temp":"-20","pressure":"777","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:43","sunset":"17:28","local_uv_irradiance_index":"Moderate","min_gts_temp":"-93","max_gts_temp":"-5"},{"id":"551","terrestrial_date":"2014-04-22","sol":"608","ls":"119","season":"Month 4","min_temp":"-85","max_temp":"-21","pressure":"777","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:43","sunset":"17:28","local_uv_irradiance_index":"Moderate","min_gts_temp":"-97","max_gts_temp":"-4"},{"id":"550","terrestrial_date":"2014-04-21","sol":"607","ls":"119","season":"Month 4","min_temp":"-83","max_temp":"-28","pressure":"779","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:43","sunset":"17:28","local_uv_irradiance_index":"Low","min_gts_temp":"-96","max_gts_temp":"-5"},{"id":"549","terrestrial_date":"2014-04-20","sol":"606","ls":"118","season":"Month 4","min_temp":"-84","max_temp":"-27","pressure":"780","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:43","sunset":"17:28","local_uv_irradiance_index":"Moderate","min_gts_temp":"-88","max_gts_temp":"-8"},{"id":"547","terrestrial_date":"2014-04-19","sol":"605","ls":"118","season":"Month 4","min_temp":"-83","max_temp":"-22","pressure":"781","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:44","sunset":"17:29","local_uv_irradiance_index":"Moderate","min_gts_temp":"-86","max_gts_temp":"-9"},{"id":"546","terrestrial_date":"2014-04-18","sol":"604","ls":"118","season":"Month 4","min_temp":"-84","max_temp":"-17","pressure":"782","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:44","sunset":"17:29","local_uv_irradiance_index":"Moderate","min_gts_temp":"-87","max_gts_temp":"-9"},{"id":"548","terrestrial_date":"2014-04-17","sol":"603","ls":"117","season":"Month 4","min_temp":"-83","max_temp":"-15","pressure":"783","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:44","sunset":"17:29","local_uv_irradiance_index":"High","min_gts_temp":"-82","max_gts_temp":"-9"},{"id":"545","terrestrial_date":"2014-04-16","sol":"602","ls":"117","season":"Month 4","min_temp":"-82","max_temp":"-22","pressure":"784","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:44","sunset":"17:29","local_uv_irradiance_index":"Moderate","min_gts_temp":"-82","max_gts_temp":"-11"},{"id":"544","terrestrial_date":"2014-04-15","sol":"601","ls":"116","season":"Month 4","min_temp":"-82","max_temp":"-25","pressure":"785","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:44","sunset":"17:29","local_uv_irradiance_index":"Moderate","min_gts_temp":"-74","max_gts_temp":"-8"},{"id":"541","terrestrial_date":"2014-04-14","sol":"600","ls":"116","season":"Month 4","min_temp":"-81","max_temp":"-25","pressure":"787","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:45","sunset":"17:29","local_uv_irradiance_index":"Moderate","min_gts_temp":"-81","max_gts_temp":"-14"},{"id":"543","terrestrial_date":"2014-04-13","sol":"599","ls":"115","season":"Month 4","min_temp":"-84","max_temp":"-20","pressure":"788","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:45","sunset":"17:29","local_uv_irradiance_index":"Moderate","min_gts_temp":"-82","max_gts_temp":"-9"},{"id":"542","terrestrial_date":"2014-04-12","sol":"598","ls":"115","season":"Month 4","min_temp":"-84","max_temp":"-25","pressure":"787","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:45","sunset":"17:29","local_uv_irradiance_index":"Moderate","min_gts_temp":"-82","max_gts_temp":"-10"},{"id":"540","terrestrial_date":"2014-04-11","sol":"597","ls":"114","season":"Month 4","min_temp":"-84","max_temp":"-26","pressure":"790","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:45","sunset":"17:29","local_uv_irradiance_index":"Moderate","min_gts_temp":"-83","max_gts_temp":"-11"},{"id":"539","terrestrial_date":"2014-04-10","sol":"596","ls":"114","season":"Month 4","min_temp":"-82","max_temp":"-24","pressure":"791","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:45","sunset":"17:29","local_uv_irradiance_index":"Moderate","min_gts_temp":"-84","max_gts_temp":"-10"},{"id":"538","terrestrial_date":"2014-04-09","sol":"595","ls":"113","season":"Month 4","min_temp":"-83","max_temp":"-25","pressure":"792","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:45","sunset":"17:30","local_uv_irradiance_index":"Moderate","min_gts_temp":"-86","max_gts_temp":"-12"},{"id":"537","terrestrial_date":"2014-04-08","sol":"594","ls":"113","season":"Month 4","min_temp":"-83","max_temp":"-22","pressure":"793","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:46","sunset":"17:30","local_uv_irradiance_index":"Moderate","min_gts_temp":"-86","max_gts_temp":"-15"},{"id":"536","terrestrial_date":"2014-04-07","sol":"593","ls":"112","season":"Month 4","min_temp":"-83","max_temp":"-23","pressure":"795","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:46","sunset":"17:30","local_uv_irradiance_index":"High","min_gts_temp":"-87","max_gts_temp":"-11"},{"id":"534","terrestrial_date":"2014-04-06","sol":"592","ls":"112","season":"Month 4","min_temp":"-83","max_temp":"-26","pressure":"795","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:46","sunset":"17:30","local_uv_irradiance_index":"Moderate","min_gts_temp":"-85","max_gts_temp":"-11"},{"id":"533","terrestrial_date":"2014-04-05","sol":"591","ls":"111","season":"Month 4","min_temp":"-82","max_temp":"-26","pressure":"795","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:46","sunset":"17:30","local_uv_irradiance_index":"Moderate","min_gts_temp":"-84","max_gts_temp":"-11"},{"id":"535","terrestrial_date":"2014-04-04","sol":"590","ls":"111","season":"Month 4","min_temp":"-82","max_temp":"-26","pressure":"797","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:46","sunset":"17:30","local_uv_irradiance_index":"Moderate","min_gts_temp":"-85","max_gts_temp":"-11"},{"id":"531","terrestrial_date":"2014-04-03","sol":"589","ls":"110","season":"Month 4","min_temp":"-85","max_temp":"-27","pressure":"798","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:46","sunset":"17:30","local_uv_irradiance_index":"Moderate","min_gts_temp":"-94","max_gts_temp":"-10"},{"id":"532","terrestrial_date":"2014-04-02","sol":"588","ls":"110","season":"Month 4","min_temp":"-84","max_temp":"-24","pressure":"799","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:47","sunset":"17:30","local_uv_irradiance_index":"Moderate","min_gts_temp":"-88","max_gts_temp":"-8"},{"id":"530","terrestrial_date":"2014-04-01","sol":"587","ls":"109","season":"Month 4","min_temp":"-85","max_temp":"-28","pressure":"801","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:47","sunset":"17:30","local_uv_irradiance_index":"Moderate","min_gts_temp":"-86","max_gts_temp":"-9"},{"id":"529","terrestrial_date":"2014-03-31","sol":"586","ls":"109","season":"Month 4","min_temp":"-82","max_temp":"-24","pressure":"802","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:47","sunset":"17:30","local_uv_irradiance_index":"Moderate","min_gts_temp":"-88","max_gts_temp":"-8"},{"id":"527","terrestrial_date":"2014-03-30","sol":"585","ls":"109","season":"Month 4","min_temp":"-83","max_temp":"-26","pressure":"802","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:47","sunset":"17:31","local_uv_irradiance_index":"Moderate","min_gts_temp":"-88","max_gts_temp":"-9"},{"id":"528","terrestrial_date":"2014-03-29","sol":"584","ls":"108","season":"Month 4","min_temp":"-82","max_temp":"-25","pressure":"804","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:47","sunset":"17:31","local_uv_irradiance_index":"Moderate","min_gts_temp":"-89","max_gts_temp":"-8"},{"id":"526","terrestrial_date":"2014-03-28","sol":"583","ls":"108","season":"Month 4","min_temp":"-82","max_temp":"-27","pressure":"806","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:47","sunset":"17:31","local_uv_irradiance_index":"Moderate","min_gts_temp":"-91","max_gts_temp":"-10"},{"id":"525","terrestrial_date":"2014-03-27","sol":"582","ls":"107","season":"Month 4","min_temp":"-84","max_temp":"-27","pressure":"807","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:47","sunset":"17:31","local_uv_irradiance_index":"Moderate","min_gts_temp":"-91","max_gts_temp":"-9"},{"id":"524","terrestrial_date":"2014-03-26","sol":"581","ls":"107","season":"Month 4","min_temp":"-84","max_temp":"-28","pressure":"808","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:48","sunset":"17:31","local_uv_irradiance_index":"Moderate","min_gts_temp":"-88","max_gts_temp":"-9"},{"id":"523","terrestrial_date":"2014-03-25","sol":"580","ls":"106","season":"Month 4","min_temp":"-83","max_temp":"-24","pressure":"810","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:48","sunset":"17:31","local_uv_irradiance_index":"Moderate","min_gts_temp":"-87","max_gts_temp":"-10"},{"id":"522","terrestrial_date":"2014-03-24","sol":"579","ls":"106","season":"Month 4","min_temp":"-82","max_temp":"-22","pressure":"811","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:48","sunset":"17:31","local_uv_irradiance_index":"Moderate","min_gts_temp":"-88","max_gts_temp":"-8"},{"id":"521","terrestrial_date":"2014-03-22","sol":"578","ls":"105","season":"Month 4","min_temp":"-83","max_temp":"-23","pressure":"812","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:48","sunset":"17:31","local_uv_irradiance_index":"Moderate","min_gts_temp":"-87","max_gts_temp":"-9"},{"id":"519","terrestrial_date":"2014-03-21","sol":"577","ls":"105","season":"Month 4","min_temp":"-84","max_temp":"-27","pressure":"813","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:48","sunset":"17:31","local_uv_irradiance_index":"Moderate","min_gts_temp":"-90","max_gts_temp":"-11"},{"id":"520","terrestrial_date":"2014-03-20","sol":"576","ls":"104","season":"Month 4","min_temp":"-84","max_temp":"-26","pressure":"815","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:48","sunset":"17:32","local_uv_irradiance_index":"Moderate","min_gts_temp":"-90","max_gts_temp":"-8"},{"id":"518","terrestrial_date":"2014-03-19","sol":"575","ls":"104","season":"Month 4","min_temp":"-82","max_temp":"-26","pressure":"816","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:49","sunset":"17:32","local_uv_irradiance_index":"Moderate","min_gts_temp":"-88","max_gts_temp":"-10"},{"id":"517","terrestrial_date":"2014-03-18","sol":"574","ls":"103","season":"Month 4","min_temp":"-85","max_temp":"-23","pressure":"817","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:49","sunset":"17:32","local_uv_irradiance_index":"High","min_gts_temp":"-87","max_gts_temp":"-9"},{"id":"516","terrestrial_date":"2014-03-17","sol":"573","ls":"103","season":"Month 4","min_temp":"-84","max_temp":"-27","pressure":"819","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:49","sunset":"17:32","local_uv_irradiance_index":"Moderate","min_gts_temp":"-87","max_gts_temp":"-10"},{"id":"514","terrestrial_date":"2014-03-16","sol":"572","ls":"102","season":"Month 4","min_temp":"-85","max_temp":"-27","pressure":"820","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:49","sunset":"17:32","local_uv_irradiance_index":"Moderate","min_gts_temp":"-89","max_gts_temp":"-8"},{"id":"515","terrestrial_date":"2014-03-15","sol":"571","ls":"102","season":"Month 4","min_temp":"-84","max_temp":"-26","pressure":"821","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:49","sunset":"17:32","local_uv_irradiance_index":"Moderate","min_gts_temp":"-92","max_gts_temp":"-8"},{"id":"513","terrestrial_date":"2014-03-14","sol":"570","ls":"102","season":"Month 4","min_temp":"-84","max_temp":"-26","pressure":"823","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:49","sunset":"17:32","local_uv_irradiance_index":"Moderate","min_gts_temp":"-91","max_gts_temp":"-7"},{"id":"512","terrestrial_date":"2014-03-13","sol":"569","ls":"101","season":"Month 4","min_temp":"-85","max_temp":"-27","pressure":"825","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:49","sunset":"17:32","local_uv_irradiance_index":"Moderate","min_gts_temp":"-90","max_gts_temp":"-7"},{"id":"511","terrestrial_date":"2014-03-12","sol":"568","ls":"101","season":"Month 4","min_temp":"-86","max_temp":"-28","pressure":"825","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:50","sunset":"17:32","local_uv_irradiance_index":"High","min_gts_temp":"-86","max_gts_temp":"-9"},{"id":"510","terrestrial_date":"2014-03-11","sol":"567","ls":"100","season":"Month 4","min_temp":"-86","max_temp":"-28","pressure":"827","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:50","sunset":"17:33","local_uv_irradiance_index":"High","min_gts_temp":"-87","max_gts_temp":"-13"},{"id":"507","terrestrial_date":"2014-03-10","sol":"566","ls":"100","season":"Month 4","min_temp":"-86","max_temp":"-27","pressure":"829","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:50","sunset":"17:33","local_uv_irradiance_index":"High","min_gts_temp":"-86","max_gts_temp":"-13"},{"id":"508","terrestrial_date":"2014-03-09","sol":"565","ls":"99","season":"Month 4","min_temp":"-85","max_temp":"-27","pressure":"830","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:50","sunset":"17:33","local_uv_irradiance_index":"High","min_gts_temp":"-91","max_gts_temp":"-8"},{"id":"509","terrestrial_date":"2014-03-08","sol":"564","ls":"99","season":"Month 4","min_temp":"-86","max_temp":"-27","pressure":"831","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:50","sunset":"17:33","local_uv_irradiance_index":"Moderate","min_gts_temp":"-92","max_gts_temp":"-11"},{"id":"505","terrestrial_date":"2014-03-07","sol":"563","ls":"98","season":"Month 4","min_temp":"-87","max_temp":"-31","pressure":"833","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:50","sunset":"17:33","local_uv_irradiance_index":"Moderate","min_gts_temp":"-88","max_gts_temp":"-9"},{"id":"506","terrestrial_date":"2014-03-06","sol":"562","ls":"98","season":"Month 4","min_temp":"-85","max_temp":"-23","pressure":"834","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:51","sunset":"17:33","local_uv_irradiance_index":"Moderate","min_gts_temp":"-92","max_gts_temp":"-11"},{"id":"504","terrestrial_date":"2014-03-05","sol":"561","ls":"97","season":"Month 4","min_temp":"-85","max_temp":"-23","pressure":"835","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:51","sunset":"17:33","local_uv_irradiance_index":"Moderate","min_gts_temp":"-89","max_gts_temp":"-5"},{"id":"503","terrestrial_date":"2014-03-04","sol":"560","ls":"97","season":"Month 4","min_temp":"-86","max_temp":"-23","pressure":"836","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:51","sunset":"17:33","local_uv_irradiance_index":"Moderate","min_gts_temp":"-97","max_gts_temp":"-6"},{"id":"502","terrestrial_date":"2014-03-03","sol":"559","ls":"96","season":"Month 4","min_temp":"-85","max_temp":"-27","pressure":"838","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:51","sunset":"17:34","local_uv_irradiance_index":"High","min_gts_temp":"-93","max_gts_temp":"-3"},{"id":"500","terrestrial_date":"2014-03-02","sol":"558","ls":"96","season":"Month 4","min_temp":"-85","max_temp":"-26","pressure":"839","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:51","sunset":"17:34","local_uv_irradiance_index":"Moderate","min_gts_temp":"-93","max_gts_temp":"-6"},{"id":"498","terrestrial_date":"2014-03-01","sol":"557","ls":"96","season":"Month 4","min_temp":"-85","max_temp":"-29","pressure":"840","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:51","sunset":"17:34","local_uv_irradiance_index":"Moderate","min_gts_temp":"-92","max_gts_temp":"-6"},{"id":"501","terrestrial_date":"2014-02-28","sol":"556","ls":"95","season":"Month 4","min_temp":"-85","max_temp":"-29","pressure":"842","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:51","sunset":"17:34","local_uv_irradiance_index":"Moderate","min_gts_temp":"-93","max_gts_temp":"-7"},{"id":"499","terrestrial_date":"2014-02-27","sol":"555","ls":"95","season":"Month 4","min_temp":"-85","max_temp":"-31","pressure":"843","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:52","sunset":"17:34","local_uv_irradiance_index":"Moderate","min_gts_temp":"-95","max_gts_temp":"-9"},{"id":"497","terrestrial_date":"2014-02-26","sol":"554","ls":"94","season":"Month 4","min_temp":"-84","max_temp":"-22","pressure":"843","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:52","sunset":"17:34","local_uv_irradiance_index":"Moderate","min_gts_temp":"-96","max_gts_temp":"-7"},{"id":"496","terrestrial_date":"2014-02-25","sol":"553","ls":"94","season":"Month 4","min_temp":"-84","max_temp":"-26","pressure":"845","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:52","sunset":"17:34","local_uv_irradiance_index":"Moderate","min_gts_temp":"-90","max_gts_temp":"-10"},{"id":"493","terrestrial_date":"2014-02-24","sol":"552","ls":"93","season":"Month 4","min_temp":"-86","max_temp":"-29","pressure":"847","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:52","sunset":"17:34","local_uv_irradiance_index":"High","min_gts_temp":"-91","max_gts_temp":"-11"},{"id":"495","terrestrial_date":"2014-02-23","sol":"551","ls":"93","season":"Month 4","min_temp":"-85","max_temp":"-28","pressure":"848","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:52","sunset":"17:35","local_uv_irradiance_index":"Moderate","min_gts_temp":"-92","max_gts_temp":"-11"},{"id":"494","terrestrial_date":"2014-02-22","sol":"550","ls":"92","season":"Month 4","min_temp":"-85","max_temp":"-27","pressure":"850","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:52","sunset":"17:35","local_uv_irradiance_index":"Moderate","min_gts_temp":"-88","max_gts_temp":"-9"},{"id":"492","terrestrial_date":"2014-02-21","sol":"549","ls":"92","season":"Month 4","min_temp":"-87","max_temp":"-23","pressure":"851","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:52","sunset":"17:35","local_uv_irradiance_index":"Moderate","min_gts_temp":"-89","max_gts_temp":"-12"},{"id":"491","terrestrial_date":"2014-02-20","sol":"548","ls":"91","season":"Month 4","min_temp":"-86","max_temp":"-28","pressure":"852","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:53","sunset":"17:35","local_uv_irradiance_index":"High","min_gts_temp":"-87","max_gts_temp":"-10"},{"id":"488","terrestrial_date":"2014-02-19","sol":"547","ls":"91","season":"Month 4","min_temp":"-85","max_temp":"-29","pressure":"853","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:53","sunset":"17:35","local_uv_irradiance_index":"High","min_gts_temp":"-89","max_gts_temp":"-10"},{"id":"489","terrestrial_date":"2014-02-18","sol":"546","ls":"91","season":"Month 4","min_temp":"-85","max_temp":"-34","pressure":"855","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:53","sunset":"17:35","local_uv_irradiance_index":"Moderate","min_gts_temp":"-91","max_gts_temp":"-13"},{"id":"490","terrestrial_date":"2014-02-17","sol":"545","ls":"90","season":"Month 4","min_temp":"-85","max_temp":"-29","pressure":"856","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:53","sunset":"17:35","local_uv_irradiance_index":"Moderate","min_gts_temp":"-87","max_gts_temp":"-14"},{"id":"487","terrestrial_date":"2014-02-16","sol":"544","ls":"90","season":"Month 4","min_temp":"-86","max_temp":"-27","pressure":"857","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:53","sunset":"17:36","local_uv_irradiance_index":"Moderate","min_gts_temp":"-84","max_gts_temp":"-16"},{"id":"486","terrestrial_date":"2014-02-15","sol":"543","ls":"89","season":"Month 3","min_temp":"-84","max_temp":"-26","pressure":"858","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:53","sunset":"17:36","local_uv_irradiance_index":"Moderate","min_gts_temp":"-86","max_gts_temp":"-16"},{"id":"477","terrestrial_date":"2014-02-13","sol":"542","ls":"89","season":"Month 3","min_temp":"-85","max_temp":"-28","pressure":"859","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:53","sunset":"17:36","local_uv_irradiance_index":"Moderate","min_gts_temp":"-91","max_gts_temp":"-11"},{"id":"482","terrestrial_date":"2014-02-12","sol":"541","ls":"88","season":"Month 3","min_temp":"-84","max_temp":"-27","pressure":"861","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:54","sunset":"17:36","local_uv_irradiance_index":"Moderate","min_gts_temp":"-93","max_gts_temp":"-11"},{"id":"481","terrestrial_date":"2014-02-11","sol":"540","ls":"88","season":"Month 3","min_temp":"-84","max_temp":"-29","pressure":"862","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:54","sunset":"17:36","local_uv_irradiance_index":"Moderate","min_gts_temp":"-85","max_gts_temp":"-18"},{"id":"476","terrestrial_date":"2014-02-10","sol":"539","ls":"87","season":"Month 3","min_temp":"-85","max_temp":"-23","pressure":"864","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:54","sunset":"17:36","local_uv_irradiance_index":"Moderate","min_gts_temp":"-82","max_gts_temp":"-17"},{"id":"472","terrestrial_date":"2014-02-09","sol":"538","ls":"87","season":"Month 3","min_temp":"-85","max_temp":"-25","pressure":"865","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:54","sunset":"17:37","local_uv_irradiance_index":"Moderate","min_gts_temp":"-91","max_gts_temp":"-8"},{"id":"469","terrestrial_date":"2014-02-08","sol":"537","ls":"86","season":"Month 3","min_temp":"-83","max_temp":"-28","pressure":"865","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:54","sunset":"17:37","local_uv_irradiance_index":"Moderate","min_gts_temp":"-92","max_gts_temp":"-11"},{"id":"470","terrestrial_date":"2014-02-07","sol":"536","ls":"86","season":"Month 3","min_temp":"-83","max_temp":"-29","pressure":"867","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:54","sunset":"17:37","local_uv_irradiance_index":"Moderate","min_gts_temp":"-94","max_gts_temp":"-12"},{"id":"468","terrestrial_date":"2014-02-06","sol":"535","ls":"86","season":"Month 3","min_temp":"-88","max_temp":"-29","pressure":"868","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:55","sunset":"17:37","local_uv_irradiance_index":"Moderate","min_gts_temp":"-99","max_gts_temp":"-7"},{"id":"467","terrestrial_date":"2014-02-05","sol":"534","ls":"85","season":"Month 3","min_temp":"-86","max_temp":"-29","pressure":"869","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:55","sunset":"17:37","local_uv_irradiance_index":"Moderate","min_gts_temp":"-101","max_gts_temp":"-6"},{"id":"466","terrestrial_date":"2014-02-04","sol":"533","ls":"85","season":"Month 3","min_temp":"-87","max_temp":"-30","pressure":"871","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:55","sunset":"17:37","local_uv_irradiance_index":"Moderate","min_gts_temp":"-98","max_gts_temp":"-7"},{"id":"464","terrestrial_date":"2014-02-03","sol":"532","ls":"84","season":"Month 3","min_temp":"-88","max_temp":"-23","pressure":"872","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:55","sunset":"17:37","local_uv_irradiance_index":"Moderate","min_gts_temp":"-97","max_gts_temp":"-10"},{"id":"465","terrestrial_date":"2014-02-02","sol":"531","ls":"84","season":"Month 3","min_temp":"-87","max_temp":"-22","pressure":"872","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:55","sunset":"17:38","local_uv_irradiance_index":"Moderate","min_gts_temp":"-99","max_gts_temp":"-12"},{"id":"463","terrestrial_date":"2014-02-01","sol":"530","ls":"83","season":"Month 3","min_temp":"-87","max_temp":"-28","pressure":"873","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:55","sunset":"17:38","local_uv_irradiance_index":"Moderate","min_gts_temp":"-99","max_gts_temp":"-11"},{"id":"462","terrestrial_date":"2014-01-31","sol":"529","ls":"83","season":"Month 3","min_temp":"-87","max_temp":"-23","pressure":"875","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:55","sunset":"17:38","local_uv_irradiance_index":"Moderate","min_gts_temp":"-100","max_gts_temp":"-11"},{"id":"461","terrestrial_date":"2014-01-30","sol":"528","ls":"82","season":"Month 3","min_temp":"-86","max_temp":"-26","pressure":"876","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:56","sunset":"17:38","local_uv_irradiance_index":"Moderate","min_gts_temp":"-93","max_gts_temp":"-9"},{"id":"460","terrestrial_date":"2014-01-29","sol":"527","ls":"82","season":"Month 3","min_temp":"-86","max_temp":"-23","pressure":"877","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:56","sunset":"17:38","local_uv_irradiance_index":"High","min_gts_temp":"-94","max_gts_temp":"-7"},{"id":"459","terrestrial_date":"2014-01-28","sol":"526","ls":"82","season":"Month 3","min_temp":"-87","max_temp":"-24","pressure":"878","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:56","sunset":"17:39","local_uv_irradiance_index":"Moderate","min_gts_temp":"-90","max_gts_temp":"-10"},{"id":"456","terrestrial_date":"2014-01-27","sol":"525","ls":"81","season":"Month 3","min_temp":"-87","max_temp":"-29","pressure":"878","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:56","sunset":"17:39","local_uv_irradiance_index":"Moderate","min_gts_temp":"-91","max_gts_temp":"-11"},{"id":"457","terrestrial_date":"2014-01-26","sol":"524","ls":"81","season":"Month 3","min_temp":"-85","max_temp":"-27","pressure":"880","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:56","sunset":"17:39","local_uv_irradiance_index":"Moderate","min_gts_temp":"-96","max_gts_temp":"-7"},{"id":"458","terrestrial_date":"2014-01-25","sol":"523","ls":"80","season":"Month 3","min_temp":"-85","max_temp":"-25","pressure":"881","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:56","sunset":"17:39","local_uv_irradiance_index":"Moderate","min_gts_temp":"-98","max_gts_temp":"-7"},{"id":"455","terrestrial_date":"2014-01-24","sol":"522","ls":"80","season":"Month 3","min_temp":"-85","max_temp":"-26","pressure":"881","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:57","sunset":"17:39","local_uv_irradiance_index":"Moderate","min_gts_temp":"-98","max_gts_temp":"-7"},{"id":"454","terrestrial_date":"2014-01-23","sol":"521","ls":"79","season":"Month 3","min_temp":"-87","max_temp":"-26","pressure":"882","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:57","sunset":"17:39","local_uv_irradiance_index":"Moderate","min_gts_temp":"-95","max_gts_temp":"-7"},{"id":"453","terrestrial_date":"2014-01-22","sol":"520","ls":"79","season":"Month 3","min_temp":"-86","max_temp":"-24","pressure":"884","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:57","sunset":"17:40","local_uv_irradiance_index":"Moderate","min_gts_temp":"-91","max_gts_temp":"-9"},{"id":"452","terrestrial_date":"2014-01-21","sol":"519","ls":"78","season":"Month 3","min_temp":"-86","max_temp":"-25","pressure":"884","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:57","sunset":"17:40","local_uv_irradiance_index":"Moderate","min_gts_temp":"-86","max_gts_temp":"-11"},{"id":"451","terrestrial_date":"2014-01-20","sol":"518","ls":"78","season":"Month 3","min_temp":"-85","max_temp":"-29","pressure":"885","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:57","sunset":"17:40","local_uv_irradiance_index":"Moderate","min_gts_temp":"-100","max_gts_temp":"-9"},{"id":"448","terrestrial_date":"2014-01-19","sol":"517","ls":"77","season":"Month 3","min_temp":"-86","max_temp":"-27","pressure":"885","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:57","sunset":"17:40","local_uv_irradiance_index":"Moderate","min_gts_temp":"-98","max_gts_temp":"-6"},{"id":"449","terrestrial_date":"2014-01-18","sol":"516","ls":"77","season":"Month 3","min_temp":"-86","max_temp":"-25","pressure":"888","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:58","sunset":"17:40","local_uv_irradiance_index":"Moderate","min_gts_temp":"-99","max_gts_temp":"-6"},{"id":"450","terrestrial_date":"2014-01-17","sol":"515","ls":"77","season":"Month 3","min_temp":"-86","max_temp":"-23","pressure":"888","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:58","sunset":"17:41","local_uv_irradiance_index":"Moderate","min_gts_temp":"-89","max_gts_temp":"-6"},{"id":"447","terrestrial_date":"2014-01-16","sol":"514","ls":"76","season":"Month 3","min_temp":"-86","max_temp":"-29","pressure":"888","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:58","sunset":"17:41","local_uv_irradiance_index":"Moderate","min_gts_temp":"-91","max_gts_temp":"-10"},{"id":"445","terrestrial_date":"2014-01-15","sol":"513","ls":"76","season":"Month 3","min_temp":"-86","max_temp":"-29","pressure":"889","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:58","sunset":"17:41","local_uv_irradiance_index":"Moderate","min_gts_temp":"-91","max_gts_temp":"-10"},{"id":"444","terrestrial_date":"2014-01-14","sol":"512","ls":"75","season":"Month 3","min_temp":"-86","max_temp":"-24","pressure":"890","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:58","sunset":"17:41","local_uv_irradiance_index":"Moderate","min_gts_temp":"-94","max_gts_temp":"-9"},{"id":"446","terrestrial_date":"2014-01-13","sol":"511","ls":"75","season":"Month 3","min_temp":"-85","max_temp":"-31","pressure":"892","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:58","sunset":"17:42","local_uv_irradiance_index":"Moderate","min_gts_temp":"-93","max_gts_temp":"-10"},{"id":"441","terrestrial_date":"2014-01-12","sol":"510","ls":"74","season":"Month 3","min_temp":"-85","max_temp":"-31","pressure":"892","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:59","sunset":"17:42","local_uv_irradiance_index":"Moderate","min_gts_temp":"-92","max_gts_temp":"-13"},{"id":"443","terrestrial_date":"2014-01-11","sol":"509","ls":"74","season":"Month 3","min_temp":"-86","max_temp":"-30","pressure":"895","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:59","sunset":"17:42","local_uv_irradiance_index":"Moderate","min_gts_temp":"-88","max_gts_temp":"-13"},{"id":"442","terrestrial_date":"2014-01-10","sol":"508","ls":"73","season":"Month 3","min_temp":"-83","max_temp":"-29","pressure":"893","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:59","sunset":"17:42","local_uv_irradiance_index":"Moderate","min_gts_temp":"-91","max_gts_temp":"-11"},{"id":"440","terrestrial_date":"2014-01-09","sol":"507","ls":"73","season":"Month 3","min_temp":"-85","max_temp":"-25","pressure":"894","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:59","sunset":"17:42","local_uv_irradiance_index":"Moderate","min_gts_temp":"-90","max_gts_temp":"-10"},{"id":"439","terrestrial_date":"2014-01-08","sol":"506","ls":"73","season":"Month 3","min_temp":"-86","max_temp":"-27","pressure":"894","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:59","sunset":"17:43","local_uv_irradiance_index":"Moderate","min_gts_temp":"-90","max_gts_temp":"-9"},{"id":"438","terrestrial_date":"2014-01-06","sol":"505","ls":"72","season":"Month 3","min_temp":"-85","max_temp":"-29","pressure":"895","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:00","sunset":"17:43","local_uv_irradiance_index":"Moderate","min_gts_temp":"-91","max_gts_temp":"-8"},{"id":"436","terrestrial_date":"2014-01-05","sol":"504","ls":"72","season":"Month 3","min_temp":"-85","max_temp":"-29","pressure":"895","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:00","sunset":"17:43","local_uv_irradiance_index":"Moderate","min_gts_temp":"-90","max_gts_temp":"-9"},{"id":"483","terrestrial_date":"2014-01-04","sol":"503","ls":"71","season":"Month 3","min_temp":"-86","max_temp":"-28","pressure":"897","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:00","sunset":"17:43","local_uv_irradiance_index":"Moderate","min_gts_temp":"-87","max_gts_temp":"-10"},{"id":"437","terrestrial_date":"2014-01-03","sol":"502","ls":"71","season":"Month 3","min_temp":"-87","max_temp":"-30","pressure":"898","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:00","sunset":"17:44","local_uv_irradiance_index":"Moderate","min_gts_temp":"-88","max_gts_temp":"-8"},{"id":"435","terrestrial_date":"2014-01-02","sol":"501","ls":"70","season":"Month 3","min_temp":"-86","max_temp":"-28","pressure":"898","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:00","sunset":"17:44","local_uv_irradiance_index":"Moderate","min_gts_temp":"-88","max_gts_temp":"-10"},{"id":"430","terrestrial_date":"2014-01-01","sol":"500","ls":"70","season":"Month 3","min_temp":"-85","max_temp":"-23","pressure":"897","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:01","sunset":"17:44","local_uv_irradiance_index":"Moderate","min_gts_temp":"-88","max_gts_temp":"-11"},{"id":"432","terrestrial_date":"2013-12-31","sol":"499","ls":"69","season":"Month 3","min_temp":"-84","max_temp":"-30","pressure":"899","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:01","sunset":"17:44","local_uv_irradiance_index":"Moderate","min_gts_temp":"-90","max_gts_temp":"-9"},{"id":"424","terrestrial_date":"2013-12-30","sol":"498","ls":"69","season":"Month 3","min_temp":"-86","max_temp":"-28","pressure":"901","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:01","sunset":"17:45","local_uv_irradiance_index":"Moderate","min_gts_temp":"-90","max_gts_temp":"-10"},{"id":"425","terrestrial_date":"2013-12-29","sol":"497","ls":"69","season":"Month 3","min_temp":"-86","max_temp":"-30","pressure":"901","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:01","sunset":"17:45","local_uv_irradiance_index":"Moderate","min_gts_temp":"-87","max_gts_temp":"-9"},{"id":"428","terrestrial_date":"2013-12-28","sol":"496","ls":"68","season":"Month 3","min_temp":"-85","max_temp":"-26","pressure":"901","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:01","sunset":"17:45","local_uv_irradiance_index":"Moderate","min_gts_temp":"-88","max_gts_temp":"-10"},{"id":"431","terrestrial_date":"2013-12-27","sol":"495","ls":"68","season":"Month 3","min_temp":"-86","max_temp":"-26","pressure":"900","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:02","sunset":"17:45","local_uv_irradiance_index":"Moderate","min_gts_temp":"-88","max_gts_temp":"-10"},{"id":"429","terrestrial_date":"2013-12-26","sol":"494","ls":"67","season":"Month 3","min_temp":"-85","max_temp":"-24","pressure":"902","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:02","sunset":"17:46","local_uv_irradiance_index":"Moderate","min_gts_temp":"-87","max_gts_temp":"-9"},{"id":"426","terrestrial_date":"2013-12-25","sol":"493","ls":"67","season":"Month 3","min_temp":"-84","max_temp":"-32","pressure":"902","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:02","sunset":"17:46","local_uv_irradiance_index":"Moderate","min_gts_temp":"-89","max_gts_temp":"-14"},{"id":"427","terrestrial_date":"2013-12-24","sol":"492","ls":"66","season":"Month 3","min_temp":"-86","max_temp":"-30","pressure":"903","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:02","sunset":"17:46","local_uv_irradiance_index":"Moderate","min_gts_temp":"-92","max_gts_temp":"-13"},{"id":"433","terrestrial_date":"2013-12-23","sol":"491","ls":"66","season":"Month 3","min_temp":"-86","max_temp":"-30","pressure":"903","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:02","sunset":"17:46","local_uv_irradiance_index":"Moderate","min_gts_temp":"-87","max_gts_temp":"-13"},{"id":"434","terrestrial_date":"2013-12-22","sol":"490","ls":"65","season":"Month 3","min_temp":"-87","max_temp":"-31","pressure":"903","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:03","sunset":"17:47","local_uv_irradiance_index":"Moderate","min_gts_temp":"-88","max_gts_temp":"-10"},{"id":"423","terrestrial_date":"2013-12-21","sol":"489","ls":"65","season":"Month 3","min_temp":"-85","max_temp":"-29","pressure":"904","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:03","sunset":"17:47","local_uv_irradiance_index":"Moderate","min_gts_temp":"-88","max_gts_temp":"-11"},{"id":"480","terrestrial_date":"2013-12-20","sol":"488","ls":"64","season":"Month 3","min_temp":"-87","max_temp":"-23","pressure":"903","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:03","sunset":"17:47","local_uv_irradiance_index":"Moderate","min_gts_temp":"-89","max_gts_temp":"-11"},{"id":"473","terrestrial_date":"2013-12-19","sol":"487","ls":"64","season":"Month 3","min_temp":"-85","max_temp":"-30","pressure":"904","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:03","sunset":"17:47","local_uv_irradiance_index":"Moderate","min_gts_temp":"-91","max_gts_temp":"-8"},{"id":"422","terrestrial_date":"2013-12-18","sol":"486","ls":"64","season":"Month 3","min_temp":"-84","max_temp":"-31","pressure":"904","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:03","sunset":"17:48","local_uv_irradiance_index":"Moderate","min_gts_temp":"-91","max_gts_temp":"-12"},{"id":"484","terrestrial_date":"2013-12-17","sol":"485","ls":"63","season":"Month 3","min_temp":"--","max_temp":"--","pressure":"--","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:04","sunset":"17:48","local_uv_irradiance_index":"--","min_gts_temp":"--","max_gts_temp":"--"},{"id":"485","terrestrial_date":"2013-12-10","sol":"478","ls":"60","season":"Month 3","min_temp":"--","max_temp":"--","pressure":"--","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:05","sunset":"17:50","local_uv_irradiance_index":"--","min_gts_temp":"--","max_gts_temp":"--"},{"id":"421","terrestrial_date":"2013-12-09","sol":"477","ls":"60","season":"Month 3","min_temp":"-85","max_temp":"-26","pressure":"907","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:05","sunset":"17:50","local_uv_irradiance_index":"Moderate","min_gts_temp":"-93","max_gts_temp":"-9"},{"id":"418","terrestrial_date":"2013-12-08","sol":"476","ls":"59","season":"Month 2","min_temp":"-85","max_temp":"-25","pressure":"906","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:06","sunset":"17:51","local_uv_irradiance_index":"Moderate","min_gts_temp":"-90","max_gts_temp":"-7"},{"id":"419","terrestrial_date":"2013-12-07","sol":"475","ls":"59","season":"Month 2","min_temp":"-85","max_temp":"-28","pressure":"906","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:06","sunset":"17:51","local_uv_irradiance_index":"Moderate","min_gts_temp":"-93","max_gts_temp":"-9"},{"id":"420","terrestrial_date":"2013-12-06","sol":"474","ls":"58","season":"Month 2","min_temp":"-84","max_temp":"-26","pressure":"906","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:06","sunset":"17:51","local_uv_irradiance_index":"Moderate","min_gts_temp":"-95","max_gts_temp":"-9"},{"id":"417","terrestrial_date":"2013-12-05","sol":"473","ls":"58","season":"Month 2","min_temp":"-86","max_temp":"-29","pressure":"907","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:06","sunset":"17:52","local_uv_irradiance_index":"Moderate","min_gts_temp":"-90","max_gts_temp":"-8"},{"id":"416","terrestrial_date":"2013-12-04","sol":"472","ls":"57","season":"Month 2","min_temp":"-84","max_temp":"-31","pressure":"907","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:07","sunset":"17:52","local_uv_irradiance_index":"Moderate","min_gts_temp":"-86","max_gts_temp":"-10"},{"id":"479","terrestrial_date":"2013-12-03","sol":"471","ls":"57","season":"Month 2","min_temp":"-84","max_temp":"-26","pressure":"906","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:07","sunset":"17:52","local_uv_irradiance_index":"Moderate","min_gts_temp":"-87","max_gts_temp":"-12"},{"id":"475","terrestrial_date":"2013-12-02","sol":"470","ls":"56","season":"Month 2","min_temp":"-85","max_temp":"-26","pressure":"907","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:07","sunset":"17:53","local_uv_irradiance_index":"Moderate","min_gts_temp":"-86","max_gts_temp":"-9"},{"id":"413","terrestrial_date":"2013-11-30","sol":"469","ls":"56","season":"Month 2","min_temp":"-85","max_temp":"-29","pressure":"908","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:07","sunset":"17:53","local_uv_irradiance_index":"Moderate","min_gts_temp":"-86","max_gts_temp":"-12"},{"id":"414","terrestrial_date":"2013-11-29","sol":"468","ls":"55","season":"Month 2","min_temp":"-85","max_temp":"-29","pressure":"907","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:07","sunset":"17:53","local_uv_irradiance_index":"Moderate","min_gts_temp":"-86","max_gts_temp":"-14"},{"id":"411","terrestrial_date":"2013-11-28","sol":"467","ls":"55","season":"Month 2","min_temp":"-84","max_temp":"-28","pressure":"907","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:08","sunset":"17:54","local_uv_irradiance_index":"Moderate","min_gts_temp":"-85","max_gts_temp":"-11"},{"id":"415","terrestrial_date":"2013-11-27","sol":"466","ls":"55","season":"Month 2","min_temp":"-85","max_temp":"-26","pressure":"907","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:08","sunset":"17:54","local_uv_irradiance_index":"Moderate","min_gts_temp":"-84","max_gts_temp":"-12"},{"id":"412","terrestrial_date":"2013-11-26","sol":"465","ls":"54","season":"Month 2","min_temp":"-85","max_temp":"-23","pressure":"906","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:08","sunset":"17:54","local_uv_irradiance_index":"High","min_gts_temp":"-85","max_gts_temp":"-13"},{"id":"410","terrestrial_date":"2013-11-25","sol":"464","ls":"54","season":"Month 2","min_temp":"-84","max_temp":"-26","pressure":"906","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:08","sunset":"17:55","local_uv_irradiance_index":"High","min_gts_temp":"-87","max_gts_temp":"-13"},{"id":"409","terrestrial_date":"2013-11-24","sol":"463","ls":"53","season":"Month 2","min_temp":"-86","max_temp":"-26","pressure":"906","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:09","sunset":"17:55","local_uv_irradiance_index":"High","min_gts_temp":"-88","max_gts_temp":"-13"},{"id":"408","terrestrial_date":"2013-11-23","sol":"462","ls":"53","season":"Month 2","min_temp":"--","max_temp":"--","pressure":"--","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:09","sunset":"17:55","local_uv_irradiance_index":"--","min_gts_temp":"--","max_gts_temp":"--"},{"id":"474","terrestrial_date":"2013-11-18","sol":"457","ls":"51","season":"Month 2","min_temp":"--","max_temp":"--","pressure":"--","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:10","sunset":"17:57","local_uv_irradiance_index":"--","min_gts_temp":"--","max_gts_temp":"--"},{"id":"406","terrestrial_date":"2013-11-17","sol":"456","ls":"50","season":"Month 2","min_temp":"-83","max_temp":"-26","pressure":"905","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:11","sunset":"17:57","local_uv_irradiance_index":"High","min_gts_temp":"-89","max_gts_temp":"-12"},{"id":"407","terrestrial_date":"2013-11-16","sol":"455","ls":"50","season":"Month 2","min_temp":"-84","max_temp":"-28","pressure":"905","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:11","sunset":"17:58","local_uv_irradiance_index":"Moderate","min_gts_temp":"-82","max_gts_temp":"-12"},{"id":"478","terrestrial_date":"2013-11-15","sol":"454","ls":"49","season":"Month 2","min_temp":"--","max_temp":"--","pressure":"--","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:11","sunset":"17:58","local_uv_irradiance_index":"--","min_gts_temp":"--","max_gts_temp":"--"},{"id":"471","terrestrial_date":"2013-11-05","sol":"444","ls":"45","season":"Month 2","min_temp":"--","max_temp":"--","pressure":"--","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:14","sunset":"18:02","local_uv_irradiance_index":"--","min_gts_temp":"--","max_gts_temp":"--"},{"id":"403","terrestrial_date":"2013-11-04","sol":"443","ls":"44","season":"Month 2","min_temp":"-81","max_temp":"-24","pressure":"901","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:14","sunset":"18:02","local_uv_irradiance_index":"Moderate","min_gts_temp":"-79","max_gts_temp":"-11"},{"id":"405","terrestrial_date":"2013-11-03","sol":"442","ls":"44","season":"Month 2","min_temp":"-80","max_temp":"-22","pressure":"900","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:14","sunset":"18:03","local_uv_irradiance_index":"Moderate","min_gts_temp":"-79","max_gts_temp":"-9"},{"id":"402","terrestrial_date":"2013-11-02","sol":"441","ls":"43","season":"Month 2","min_temp":"-81","max_temp":"-23","pressure":"900","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:15","sunset":"18:03","local_uv_irradiance_index":"Moderate","min_gts_temp":"-79","max_gts_temp":"-10"},{"id":"404","terrestrial_date":"2013-11-01","sol":"440","ls":"43","season":"Month 2","min_temp":"-80","max_temp":"-25","pressure":"899","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:15","sunset":"18:03","local_uv_irradiance_index":"Moderate","min_gts_temp":"-79","max_gts_temp":"-11"},{"id":"401","terrestrial_date":"2013-10-31","sol":"439","ls":"42","season":"Month 2","min_temp":"-80","max_temp":"-21","pressure":"899","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:15","sunset":"18:04","local_uv_irradiance_index":"Moderate","min_gts_temp":"-79","max_gts_temp":"-15"},{"id":"400","terrestrial_date":"2013-10-30","sol":"438","ls":"42","season":"Month 2","min_temp":"-79","max_temp":"-26","pressure":"899","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:15","sunset":"18:04","local_uv_irradiance_index":"Moderate","min_gts_temp":"-84","max_gts_temp":"-14"},{"id":"399","terrestrial_date":"2013-10-29","sol":"437","ls":"41","season":"Month 2","min_temp":"-82","max_temp":"-27","pressure":"898","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:16","sunset":"18:05","local_uv_irradiance_index":"Moderate","min_gts_temp":"-89","max_gts_temp":"-10"},{"id":"398","terrestrial_date":"2013-10-28","sol":"436","ls":"41","season":"Month 2","min_temp":"-81","max_temp":"-24","pressure":"897","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:16","sunset":"18:05","local_uv_irradiance_index":"Moderate","min_gts_temp":"-81","max_gts_temp":"-7"},{"id":"396","terrestrial_date":"2013-10-27","sol":"435","ls":"40","season":"Month 2","min_temp":"-81","max_temp":"-24","pressure":"896","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:16","sunset":"18:05","local_uv_irradiance_index":"Moderate","min_gts_temp":"-82","max_gts_temp":"-9"},{"id":"397","terrestrial_date":"2013-10-26","sol":"434","ls":"40","season":"Month 2","min_temp":"-80","max_temp":"-25","pressure":"896","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:17","sunset":"18:06","local_uv_irradiance_index":"Moderate","min_gts_temp":"-82","max_gts_temp":"-11"},{"id":"394","terrestrial_date":"2013-10-24","sol":"433","ls":"40","season":"Month 2","min_temp":"-81","max_temp":"-26","pressure":"895","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:17","sunset":"18:06","local_uv_irradiance_index":"Moderate","min_gts_temp":"-86","max_gts_temp":"-9"},{"id":"395","terrestrial_date":"2013-10-23","sol":"432","ls":"39","season":"Month 2","min_temp":"-80","max_temp":"-26","pressure":"896","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:17","sunset":"18:06","local_uv_irradiance_index":"Moderate","min_gts_temp":"-88","max_gts_temp":"-9"},{"id":"389","terrestrial_date":"2013-10-22","sol":"431","ls":"39","season":"Month 2","min_temp":"-83","max_temp":"-24","pressure":"895","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:18","sunset":"18:07","local_uv_irradiance_index":"Moderate","min_gts_temp":"-87","max_gts_temp":"-9"},{"id":"390","terrestrial_date":"2013-10-21","sol":"430","ls":"38","season":"Month 2","min_temp":"-82","max_temp":"-24","pressure":"896","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:18","sunset":"18:07","local_uv_irradiance_index":"Moderate","min_gts_temp":"-88","max_gts_temp":"-5"},{"id":"387","terrestrial_date":"2013-10-20","sol":"429","ls":"38","season":"Month 2","min_temp":"-82","max_temp":"-21","pressure":"895","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:18","sunset":"18:08","local_uv_irradiance_index":"Moderate","min_gts_temp":"-86","max_gts_temp":"-5"},{"id":"391","terrestrial_date":"2013-10-19","sol":"428","ls":"37","season":"Month 2","min_temp":"-81","max_temp":"-23","pressure":"894","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:18","sunset":"18:08","local_uv_irradiance_index":"Moderate","min_gts_temp":"-87","max_gts_temp":"-6"},{"id":"392","terrestrial_date":"2013-10-18","sol":"427","ls":"37","season":"Month 2","min_temp":"-81","max_temp":"-21","pressure":"893","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:19","sunset":"18:09","local_uv_irradiance_index":"Moderate","min_gts_temp":"-87","max_gts_temp":"-4"},{"id":"388","terrestrial_date":"2013-10-17","sol":"426","ls":"36","season":"Month 2","min_temp":"-79","max_temp":"-22","pressure":"893","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:19","sunset":"18:09","local_uv_irradiance_index":"Moderate","min_gts_temp":"-89","max_gts_temp":"-5"},{"id":"393","terrestrial_date":"2013-10-16","sol":"425","ls":"36","season":"Month 2","min_temp":"-81","max_temp":"-21","pressure":"893","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:19","sunset":"18:09","local_uv_irradiance_index":"Moderate","min_gts_temp":"-87","max_gts_temp":"-5"},{"id":"386","terrestrial_date":"2013-10-15","sol":"424","ls":"35","season":"Month 2","min_temp":"-81","max_temp":"-22","pressure":"893","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:20","sunset":"18:10","local_uv_irradiance_index":"Moderate","min_gts_temp":"-82","max_gts_temp":"-4"},{"id":"383","terrestrial_date":"2013-10-14","sol":"423","ls":"35","season":"Month 2","min_temp":"-81","max_temp":"-24","pressure":"892","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:20","sunset":"18:10","local_uv_irradiance_index":"Moderate","min_gts_temp":"-81","max_gts_temp":"-11"},{"id":"384","terrestrial_date":"2013-10-13","sol":"422","ls":"34","season":"Month 2","min_temp":"-82","max_temp":"-20","pressure":"891","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:20","sunset":"18:11","local_uv_irradiance_index":"Moderate","min_gts_temp":"-85","max_gts_temp":"-7"},{"id":"382","terrestrial_date":"2013-10-12","sol":"421","ls":"34","season":"Month 2","min_temp":"-81","max_temp":"-24","pressure":"890","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:20","sunset":"18:11","local_uv_irradiance_index":"High","min_gts_temp":"-85","max_gts_temp":"-7"},{"id":"385","terrestrial_date":"2013-10-11","sol":"420","ls":"33","season":"Month 2","min_temp":"-80","max_temp":"-19","pressure":"892","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:21","sunset":"18:11","local_uv_irradiance_index":"High","min_gts_temp":"-86","max_gts_temp":"-7"},{"id":"381","terrestrial_date":"2013-10-10","sol":"419","ls":"33","season":"Month 2","min_temp":"-82","max_temp":"-24","pressure":"891","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:21","sunset":"18:12","local_uv_irradiance_index":"High","min_gts_temp":"-87","max_gts_temp":"-5"},{"id":"380","terrestrial_date":"2013-10-09","sol":"418","ls":"33","season":"Month 2","min_temp":"-79","max_temp":"-21","pressure":"891","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:21","sunset":"18:12","local_uv_irradiance_index":"High","min_gts_temp":"-87","max_gts_temp":"-3"},{"id":"379","terrestrial_date":"2013-10-08","sol":"417","ls":"32","season":"Month 2","min_temp":"-82","max_temp":"-21","pressure":"890","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:22","sunset":"18:13","local_uv_irradiance_index":"High","min_gts_temp":"-87","max_gts_temp":"-2"},{"id":"378","terrestrial_date":"2013-10-07","sol":"416","ls":"32","season":"Month 2","min_temp":"-79","max_temp":"-18","pressure":"889","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:22","sunset":"18:13","local_uv_irradiance_index":"High","min_gts_temp":"-84","max_gts_temp":"-6"},{"id":"377","terrestrial_date":"2013-10-06","sol":"415","ls":"31","season":"Month 2","min_temp":"-82","max_temp":"-19","pressure":"890","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:22","sunset":"18:14","local_uv_irradiance_index":"High","min_gts_temp":"-86","max_gts_temp":"-4"},{"id":"375","terrestrial_date":"2013-10-05","sol":"414","ls":"31","season":"Month 2","min_temp":"-79","max_temp":"-18","pressure":"889","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:23","sunset":"18:14","local_uv_irradiance_index":"High","min_gts_temp":"-87","max_gts_temp":"-4"},{"id":"376","terrestrial_date":"2013-10-04","sol":"413","ls":"30","season":"Month 2","min_temp":"-82","max_temp":"-22","pressure":"888","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:23","sunset":"18:14","local_uv_irradiance_index":"High","min_gts_temp":"-80","max_gts_temp":"-2"},{"id":"371","terrestrial_date":"2013-10-03","sol":"412","ls":"30","season":"Month 2","min_temp":"-80","max_temp":"-22","pressure":"888","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:23","sunset":"18:15","local_uv_irradiance_index":"Moderate","min_gts_temp":"-79","max_gts_temp":"-2"},{"id":"372","terrestrial_date":"2013-10-02","sol":"411","ls":"29","season":"Month 1","min_temp":"-82","max_temp":"-17","pressure":"887","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:24","sunset":"18:15","local_uv_irradiance_index":"Moderate","min_gts_temp":"-81","max_gts_temp":"-8"},{"id":"373","terrestrial_date":"2013-10-01","sol":"410","ls":"29","season":"Month 1","min_temp":"-81","max_temp":"-14","pressure":"888","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:24","sunset":"18:16","local_uv_irradiance_index":"High","min_gts_temp":"-84","max_gts_temp":"-7"},{"id":"368","terrestrial_date":"2013-09-30","sol":"409","ls":"28","season":"Month 1","min_temp":"-81","max_temp":"-17","pressure":"887","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:24","sunset":"18:16","local_uv_irradiance_index":"Moderate","min_gts_temp":"-95","max_gts_temp":"-2"},{"id":"369","terrestrial_date":"2013-09-29","sol":"408","ls":"28","season":"Month 1","min_temp":"-80","max_temp":"-16","pressure":"887","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:24","sunset":"18:17","local_uv_irradiance_index":"Moderate","min_gts_temp":"-95","max_gts_temp":"0"},{"id":"370","terrestrial_date":"2013-09-28","sol":"407","ls":"27","season":"Month 1","min_temp":"-81","max_temp":"-17","pressure":"887","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:25","sunset":"18:17","local_uv_irradiance_index":"Moderate","min_gts_temp":"-94","max_gts_temp":"0"},{"id":"374","terrestrial_date":"2013-09-27","sol":"406","ls":"27","season":"Month 1","min_temp":"-81","max_temp":"-15","pressure":"886","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:25","sunset":"18:17","local_uv_irradiance_index":"High","min_gts_temp":"-86","max_gts_temp":"1"},{"id":"366","terrestrial_date":"2013-09-26","sol":"405","ls":"26","season":"Month 1","min_temp":"-82","max_temp":"-25","pressure":"885","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:25","sunset":"18:18","local_uv_irradiance_index":"Moderate","min_gts_temp":"-87","max_gts_temp":"-6"},{"id":"367","terrestrial_date":"2013-09-25","sol":"404","ls":"26","season":"Month 1","min_temp":"-78","max_temp":"-20","pressure":"884","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:26","sunset":"18:18","local_uv_irradiance_index":"Moderate","min_gts_temp":"-85","max_gts_temp":"-6"},{"id":"364","terrestrial_date":"2013-09-24","sol":"403","ls":"25","season":"Month 1","min_temp":"-80","max_temp":"-18","pressure":"883","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:26","sunset":"18:19","local_uv_irradiance_index":"High","min_gts_temp":"-89","max_gts_temp":"-5"},{"id":"365","terrestrial_date":"2013-09-23","sol":"402","ls":"25","season":"Month 1","min_temp":"-78","max_temp":"-18","pressure":"883","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:26","sunset":"18:19","local_uv_irradiance_index":"High","min_gts_temp":"-80","max_gts_temp":"-3"},{"id":"362","terrestrial_date":"2013-09-22","sol":"401","ls":"25","season":"Month 1","min_temp":"-79","max_temp":"-15","pressure":"882","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:27","sunset":"18:20","local_uv_irradiance_index":"High","min_gts_temp":"-80","max_gts_temp":"-5"},{"id":"363","terrestrial_date":"2013-09-21","sol":"400","ls":"24","season":"Month 1","min_temp":"-79","max_temp":"-16","pressure":"881","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:27","sunset":"18:20","local_uv_irradiance_index":"High","min_gts_temp":"-80","max_gts_temp":"-6"},{"id":"361","terrestrial_date":"2013-09-20","sol":"399","ls":"24","season":"Month 1","min_temp":"-79","max_temp":"-16","pressure":"881","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:27","sunset":"18:20","local_uv_irradiance_index":"High","min_gts_temp":"-80","max_gts_temp":"-4"},{"id":"360","terrestrial_date":"2013-09-19","sol":"398","ls":"23","season":"Month 1","min_temp":"-79","max_temp":"-14","pressure":"880","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:28","sunset":"18:21","local_uv_irradiance_index":"High","min_gts_temp":"-79","max_gts_temp":"-6"},{"id":"359","terrestrial_date":"2013-09-18","sol":"397","ls":"23","season":"Month 1","min_temp":"-75","max_temp":"-15","pressure":"880","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:28","sunset":"18:21","local_uv_irradiance_index":"High","min_gts_temp":"-79","max_gts_temp":"-7"},{"id":"358","terrestrial_date":"2013-09-16","sol":"396","ls":"22","season":"Month 1","min_temp":"-78","max_temp":"-15","pressure":"879","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:28","sunset":"18:22","local_uv_irradiance_index":"High","min_gts_temp":"-80","max_gts_temp":"-5"},{"id":"357","terrestrial_date":"2013-09-15","sol":"395","ls":"22","season":"Month 1","min_temp":"-77","max_temp":"-20","pressure":"878","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:28","sunset":"18:22","local_uv_irradiance_index":"High","min_gts_temp":"-79","max_gts_temp":"-5"},{"id":"356","terrestrial_date":"2013-09-14","sol":"394","ls":"21","season":"Month 1","min_temp":"-79","max_temp":"-18","pressure":"878","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:29","sunset":"18:23","local_uv_irradiance_index":"High","min_gts_temp":"-79","max_gts_temp":"-4"},{"id":"355","terrestrial_date":"2013-09-13","sol":"393","ls":"21","season":"Month 1","min_temp":"-76","max_temp":"-18","pressure":"877","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:29","sunset":"18:23","local_uv_irradiance_index":"High","min_gts_temp":"-78","max_gts_temp":"-5"},{"id":"354","terrestrial_date":"2013-09-12","sol":"392","ls":"20","season":"Month 1","min_temp":"-77","max_temp":"-17","pressure":"876","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:29","sunset":"18:23","local_uv_irradiance_index":"High","min_gts_temp":"-74","max_gts_temp":"-4"},{"id":"353","terrestrial_date":"2013-09-11","sol":"391","ls":"20","season":"Month 1","min_temp":"-78","max_temp":"-17","pressure":"875","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:30","sunset":"18:24","local_uv_irradiance_index":"High","min_gts_temp":"-75","max_gts_temp":"-9"},{"id":"352","terrestrial_date":"2013-09-10","sol":"390","ls":"19","season":"Month 1","min_temp":"-78","max_temp":"-16","pressure":"875","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:30","sunset":"18:24","local_uv_irradiance_index":"High","min_gts_temp":"-79","max_gts_temp":"-1"},{"id":"348","terrestrial_date":"2013-09-09","sol":"389","ls":"19","season":"Month 1","min_temp":"-79","max_temp":"-14","pressure":"875","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:30","sunset":"18:25","local_uv_irradiance_index":"High","min_gts_temp":"-78","max_gts_temp":"-7"},{"id":"350","terrestrial_date":"2013-09-08","sol":"388","ls":"18","season":"Month 1","min_temp":"-79","max_temp":"-13","pressure":"875","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:31","sunset":"18:25","local_uv_irradiance_index":"High","min_gts_temp":"-79","max_gts_temp":"-6"},{"id":"349","terrestrial_date":"2013-09-07","sol":"387","ls":"18","season":"Month 1","min_temp":"-78","max_temp":"-13","pressure":"874","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:31","sunset":"18:26","local_uv_irradiance_index":"High","min_gts_temp":"-78","max_gts_temp":"-7"},{"id":"347","terrestrial_date":"2013-09-06","sol":"386","ls":"17","season":"Month 1","min_temp":"-77","max_temp":"-12","pressure":"873","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:31","sunset":"18:26","local_uv_irradiance_index":"High","min_gts_temp":"-78","max_gts_temp":"-7"},{"id":"351","terrestrial_date":"2013-09-05","sol":"385","ls":"17","season":"Month 1","min_temp":"-78","max_temp":"-12","pressure":"874","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:31","sunset":"18:26","local_uv_irradiance_index":"High","min_gts_temp":"-81","max_gts_temp":"-3"},{"id":"346","terrestrial_date":"2013-09-04","sol":"384","ls":"16","season":"Month 1","min_temp":"-77","max_temp":"-12","pressure":"875","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:32","sunset":"18:27","local_uv_irradiance_index":"High","min_gts_temp":"-84","max_gts_temp":"-4"},{"id":"342","terrestrial_date":"2013-09-03","sol":"383","ls":"16","season":"Month 1","min_temp":"-77","max_temp":"-12","pressure":"874","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:32","sunset":"18:27","local_uv_irradiance_index":"Moderate","min_gts_temp":"-81","max_gts_temp":"-6"},{"id":"344","terrestrial_date":"2013-09-02","sol":"382","ls":"15","season":"Month 1","min_temp":"-76","max_temp":"-14","pressure":"872","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:32","sunset":"18:28","local_uv_irradiance_index":"High","min_gts_temp":"-81","max_gts_temp":"-4"},{"id":"345","terrestrial_date":"2013-09-01","sol":"381","ls":"15","season":"Month 1","min_temp":"-79","max_temp":"-13","pressure":"871","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:33","sunset":"18:28","local_uv_irradiance_index":"High","min_gts_temp":"-81","max_gts_temp":"-4"},{"id":"343","terrestrial_date":"2013-08-31","sol":"380","ls":"14","season":"Month 1","min_temp":"-79","max_temp":"-13","pressure":"870","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:33","sunset":"18:29","local_uv_irradiance_index":"High","min_gts_temp":"-82","max_gts_temp":"-4"},{"id":"341","terrestrial_date":"2013-08-30","sol":"379","ls":"14","season":"Month 1","min_temp":"-79","max_temp":"-12","pressure":"870","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:33","sunset":"18:29","local_uv_irradiance_index":"High","min_gts_temp":"-84","max_gts_temp":"-2"},{"id":"339","terrestrial_date":"2013-08-29","sol":"378","ls":"13","season":"Month 1","min_temp":"-78","max_temp":"-18","pressure":"869","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:33","sunset":"18:29","local_uv_irradiance_index":"High","min_gts_temp":"-80","max_gts_temp":"-4"},{"id":"338","terrestrial_date":"2013-08-28","sol":"377","ls":"13","season":"Month 1","min_temp":"-78","max_temp":"-12","pressure":"870","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:34","sunset":"18:30","local_uv_irradiance_index":"High","min_gts_temp":"-86","max_gts_temp":"2"},{"id":"340","terrestrial_date":"2013-08-27","sol":"376","ls":"12","season":"Month 1","min_temp":"-78","max_temp":"-11","pressure":"870","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:34","sunset":"18:30","local_uv_irradiance_index":"High","min_gts_temp":"-82","max_gts_temp":"-2"},{"id":"336","terrestrial_date":"2013-08-26","sol":"375","ls":"12","season":"Month 1","min_temp":"-77","max_temp":"-9","pressure":"867","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:34","sunset":"18:31","local_uv_irradiance_index":"High","min_gts_temp":"-83","max_gts_temp":"-1"},{"id":"337","terrestrial_date":"2013-08-25","sol":"374","ls":"11","season":"Month 1","min_temp":"-79","max_temp":"-9","pressure":"866","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:35","sunset":"18:31","local_uv_irradiance_index":"High","min_gts_temp":"-84","max_gts_temp":"0"},{"id":"335","terrestrial_date":"2013-08-24","sol":"373","ls":"11","season":"Month 1","min_temp":"-79","max_temp":"-8","pressure":"866","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:35","sunset":"18:32","local_uv_irradiance_index":"High","min_gts_temp":"-85","max_gts_temp":"0"},{"id":"334","terrestrial_date":"2013-08-23","sol":"372","ls":"10","season":"Month 1","min_temp":"-76","max_temp":"-9","pressure":"866","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:35","sunset":"18:32","local_uv_irradiance_index":"High","min_gts_temp":"-82","max_gts_temp":"2"},{"id":"333","terrestrial_date":"2013-08-22","sol":"371","ls":"10","season":"Month 1","min_temp":"-77","max_temp":"-11","pressure":"865","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:35","sunset":"18:32","local_uv_irradiance_index":"High","min_gts_temp":"-76","max_gts_temp":"-3"},{"id":"332","terrestrial_date":"2013-08-21","sol":"370","ls":"9","season":"Month 1","min_temp":"-79","max_temp":"-9","pressure":"865","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:36","sunset":"18:33","local_uv_irradiance_index":"High","min_gts_temp":"-80","max_gts_temp":"-1"},{"id":"331","terrestrial_date":"2013-08-20","sol":"369","ls":"9","season":"Month 1","min_temp":"-75","max_temp":"-9","pressure":"865","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:36","sunset":"18:33","local_uv_irradiance_index":"High","min_gts_temp":"-80","max_gts_temp":"3"},{"id":"330","terrestrial_date":"2013-08-19","sol":"368","ls":"8","season":"Month 1","min_temp":"-77","max_temp":"-9","pressure":"863","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:36","sunset":"18:34","local_uv_irradiance_index":"High","min_gts_temp":"-81","max_gts_temp":"-3"},{"id":"328","terrestrial_date":"2013-08-18","sol":"367","ls":"8","season":"Month 1","min_temp":"-76","max_temp":"-9","pressure":"862","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:37","sunset":"18:34","local_uv_irradiance_index":"High","min_gts_temp":"-81","max_gts_temp":"-2"},{"id":"326","terrestrial_date":"2013-08-17","sol":"366","ls":"7","season":"Month 1","min_temp":"-76","max_temp":"-12","pressure":"861","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:37","sunset":"18:34","local_uv_irradiance_index":"High","min_gts_temp":"-82","max_gts_temp":"-3"},{"id":"329","terrestrial_date":"2013-08-16","sol":"365","ls":"7","season":"Month 1","min_temp":"--","max_temp":"--","pressure":"--","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:37","sunset":"18:35","local_uv_irradiance_index":"--","min_gts_temp":"--","max_gts_temp":"--"},{"id":"327","terrestrial_date":"2013-08-08","sol":"358","ls":"3","season":"Month 1","min_temp":"--","max_temp":"--","pressure":"--","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:39","sunset":"18:38","local_uv_irradiance_index":"--","min_gts_temp":"--","max_gts_temp":"--"},{"id":"325","terrestrial_date":"2013-08-07","sol":"357","ls":"3","season":"Month 1","min_temp":"-76","max_temp":"-7","pressure":"857","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:39","sunset":"18:38","local_uv_irradiance_index":"High","min_gts_temp":"-78","max_gts_temp":"-1"},{"id":"324","terrestrial_date":"2013-08-06","sol":"356","ls":"2","season":"Month 1","min_temp":"-77","max_temp":"-7","pressure":"856","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:39","sunset":"18:38","local_uv_irradiance_index":"High","min_gts_temp":"-79","max_gts_temp":"-1"},{"id":"321","terrestrial_date":"2013-08-05","sol":"355","ls":"2","season":"Month 1","min_temp":"-75","max_temp":"-7","pressure":"857","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:40","sunset":"18:39","local_uv_irradiance_index":"High","min_gts_temp":"-81","max_gts_temp":"0"},{"id":"322","terrestrial_date":"2013-08-04","sol":"354","ls":"1","season":"Month 1","min_temp":"-75","max_temp":"-13","pressure":"857","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:40","sunset":"18:39","local_uv_irradiance_index":"High","min_gts_temp":"-76","max_gts_temp":"-1"},{"id":"323","terrestrial_date":"2013-08-03","sol":"353","ls":"1","season":"Month 1","min_temp":"-75","max_temp":"-8","pressure":"855","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:40","sunset":"18:39","local_uv_irradiance_index":"High","min_gts_temp":"-76","max_gts_temp":"-4"},{"id":"319","terrestrial_date":"2013-08-02","sol":"352","ls":"0","season":"Month 1","min_temp":"-77","max_temp":"-9","pressure":"854","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:40","sunset":"18:40","local_uv_irradiance_index":"High","min_gts_temp":"-75","max_gts_temp":"-3"},{"id":"320","terrestrial_date":"2013-08-01","sol":"351","ls":"0","season":"Month 1","min_temp":"-75","max_temp":"-12","pressure":"853","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:41","sunset":"18:40","local_uv_irradiance_index":"High","min_gts_temp":"-78","max_gts_temp":"-4"},{"id":"318","terrestrial_date":"2013-07-31","sol":"350","ls":"359","season":"Month 12","min_temp":"-76","max_temp":"-6","pressure":"853","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:41","sunset":"18:41","local_uv_irradiance_index":"High","min_gts_temp":"-78","max_gts_temp":"0"},{"id":"316","terrestrial_date":"2013-07-30","sol":"349","ls":"359","season":"Month 12","min_temp":"-75","max_temp":"-12","pressure":"852","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:41","sunset":"18:41","local_uv_irradiance_index":"High","min_gts_temp":"-80","max_gts_temp":"-1"},{"id":"315","terrestrial_date":"2013-07-29","sol":"348","ls":"358","season":"Month 12","min_temp":"-74","max_temp":"-8","pressure":"850","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:41","sunset":"18:41","local_uv_irradiance_index":"High","min_gts_temp":"-80","max_gts_temp":"1"},{"id":"313","terrestrial_date":"2013-07-28","sol":"347","ls":"358","season":"Month 12","min_temp":"-77","max_temp":"-9","pressure":"851","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:41","sunset":"18:42","local_uv_irradiance_index":"High","min_gts_temp":"-82","max_gts_temp":"2"},{"id":"314","terrestrial_date":"2013-07-27","sol":"346","ls":"357","season":"Month 12","min_temp":"-76","max_temp":"-7","pressure":"853","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:42","sunset":"18:42","local_uv_irradiance_index":"High","min_gts_temp":"-82","max_gts_temp":"1"},{"id":"312","terrestrial_date":"2013-07-26","sol":"345","ls":"357","season":"Month 12","min_temp":"-77","max_temp":"-7","pressure":"853","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:42","sunset":"18:42","local_uv_irradiance_index":"High","min_gts_temp":"-82","max_gts_temp":"3"},{"id":"317","terrestrial_date":"2013-07-25","sol":"344","ls":"356","season":"Month 12","min_temp":"-77","max_temp":"-6","pressure":"851","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:42","sunset":"18:43","local_uv_irradiance_index":"High","min_gts_temp":"-81","max_gts_temp":"2"},{"id":"311","terrestrial_date":"2013-07-24","sol":"343","ls":"356","season":"Month 12","min_temp":"-77","max_temp":"-11","pressure":"850","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:42","sunset":"18:43","local_uv_irradiance_index":"High","min_gts_temp":"-81","max_gts_temp":"2"},{"id":"309","terrestrial_date":"2013-07-23","sol":"342","ls":"355","season":"Month 12","min_temp":"-76","max_temp":"-7","pressure":"851","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:42","sunset":"18:43","local_uv_irradiance_index":"High","min_gts_temp":"-84","max_gts_temp":"2"},{"id":"310","terrestrial_date":"2013-07-22","sol":"341","ls":"355","season":"Month 12","min_temp":"-77","max_temp":"-6","pressure":"852","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:43","sunset":"18:44","local_uv_irradiance_index":"High","min_gts_temp":"-83","max_gts_temp":"4"},{"id":"307","terrestrial_date":"2013-07-21","sol":"340","ls":"354","season":"Month 12","min_temp":"-76","max_temp":"-6","pressure":"851","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:43","sunset":"18:44","local_uv_irradiance_index":"High","min_gts_temp":"-79","max_gts_temp":"5"},{"id":"308","terrestrial_date":"2013-07-20","sol":"339","ls":"354","season":"Month 12","min_temp":"-76","max_temp":"-8","pressure":"849","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:43","sunset":"18:44","local_uv_irradiance_index":"High","min_gts_temp":"-81","max_gts_temp":"3"},{"id":"306","terrestrial_date":"2013-07-19","sol":"338","ls":"353","season":"Month 12","min_temp":"-78","max_temp":"-7","pressure":"848","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:43","sunset":"18:45","local_uv_irradiance_index":"High","min_gts_temp":"-81","max_gts_temp":"3"},{"id":"304","terrestrial_date":"2013-07-18","sol":"337","ls":"353","season":"Month 12","min_temp":"-75","max_temp":"-7","pressure":"853","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:43","sunset":"18:45","local_uv_irradiance_index":"High","min_gts_temp":"-83","max_gts_temp":"3"},{"id":"305","terrestrial_date":"2013-07-17","sol":"336","ls":"352","season":"Month 12","min_temp":"-76","max_temp":"-8","pressure":"852","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:44","sunset":"18:45","local_uv_irradiance_index":"High","min_gts_temp":"-83","max_gts_temp":"4"},{"id":"302","terrestrial_date":"2013-07-16","sol":"335","ls":"352","season":"Month 12","min_temp":"-76","max_temp":"-7","pressure":"852","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:44","sunset":"18:46","local_uv_irradiance_index":"High","min_gts_temp":"-81","max_gts_temp":"5"},{"id":"301","terrestrial_date":"2013-07-15","sol":"334","ls":"351","season":"Month 12","min_temp":"-74","max_temp":"-6","pressure":"852","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:44","sunset":"18:46","local_uv_irradiance_index":"High","min_gts_temp":"-78","max_gts_temp":"1"},{"id":"299","terrestrial_date":"2013-07-14","sol":"333","ls":"350","season":"Month 12","min_temp":"-75","max_temp":"-5","pressure":"849","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:44","sunset":"18:46","local_uv_irradiance_index":"High","min_gts_temp":"-83","max_gts_temp":"2"},{"id":"300","terrestrial_date":"2013-07-13","sol":"332","ls":"350","season":"Month 12","min_temp":"-76","max_temp":"-5","pressure":"849","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:44","sunset":"18:47","local_uv_irradiance_index":"High","min_gts_temp":"-83","max_gts_temp":"4"},{"id":"303","terrestrial_date":"2013-07-12","sol":"331","ls":"349","season":"Month 12","min_temp":"-74","max_temp":"-3","pressure":"848","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:44","sunset":"18:47","local_uv_irradiance_index":"High","min_gts_temp":"-85","max_gts_temp":"6"},{"id":"297","terrestrial_date":"2013-07-11","sol":"330","ls":"349","season":"Month 12","min_temp":"-75","max_temp":"-5","pressure":"848","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:45","sunset":"18:47","local_uv_irradiance_index":"High","min_gts_temp":"-85","max_gts_temp":"8"},{"id":"298","terrestrial_date":"2013-07-10","sol":"329","ls":"348","season":"Month 12","min_temp":"-74","max_temp":"-7","pressure":"847","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:45","sunset":"18:48","local_uv_irradiance_index":"High","min_gts_temp":"-87","max_gts_temp":"7"},{"id":"293","terrestrial_date":"2013-07-09","sol":"328","ls":"348","season":"Month 12","min_temp":"-75","max_temp":"-5","pressure":"847","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:45","sunset":"18:48","local_uv_irradiance_index":"High","min_gts_temp":"-86","max_gts_temp":"8"},{"id":"296","terrestrial_date":"2013-07-08","sol":"327","ls":"347","season":"Month 12","min_temp":"-74","max_temp":"-8","pressure":"848","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:45","sunset":"18:48","local_uv_irradiance_index":"Very_High","min_gts_temp":"-79","max_gts_temp":"7"},{"id":"295","terrestrial_date":"2013-07-07","sol":"326","ls":"347","season":"Month 12","min_temp":"-72","max_temp":"-5","pressure":"848","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:45","sunset":"18:48","local_uv_irradiance_index":"High","min_gts_temp":"-80","max_gts_temp":"5"},{"id":"294","terrestrial_date":"2013-07-06","sol":"325","ls":"346","season":"Month 12","min_temp":"-74","max_temp":"-8","pressure":"847","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:45","sunset":"18:49","local_uv_irradiance_index":"High","min_gts_temp":"-80","max_gts_temp":"8"},{"id":"292","terrestrial_date":"2013-07-04","sol":"324","ls":"346","season":"Month 12","min_temp":"-75","max_temp":"-8","pressure":"850","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:45","sunset":"18:49","local_uv_irradiance_index":"High","min_gts_temp":"-79","max_gts_temp":"8"},{"id":"290","terrestrial_date":"2013-07-03","sol":"323","ls":"345","season":"Month 12","min_temp":"-74","max_temp":"-6","pressure":"847","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:45","sunset":"18:49","local_uv_irradiance_index":"High","min_gts_temp":"-78","max_gts_temp":"4"},{"id":"291","terrestrial_date":"2013-07-02","sol":"322","ls":"345","season":"Month 12","min_temp":"-74","max_temp":"-5","pressure":"849","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:46","sunset":"18:50","local_uv_irradiance_index":"High","min_gts_temp":"-76","max_gts_temp":"3"},{"id":"289","terrestrial_date":"2013-07-01","sol":"321","ls":"344","season":"Month 12","min_temp":"-75","max_temp":"-5","pressure":"847","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:46","sunset":"18:50","local_uv_irradiance_index":"High","min_gts_temp":"-76","max_gts_temp":"3"},{"id":"269","terrestrial_date":"2013-06-30","sol":"320","ls":"344","season":"Month 12","min_temp":"-74","max_temp":"-4","pressure":"846","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:46","sunset":"18:50","local_uv_irradiance_index":"High","min_gts_temp":"-76","max_gts_temp":"3"},{"id":"271","terrestrial_date":"2013-06-29","sol":"319","ls":"343","season":"Month 12","min_temp":"-73","max_temp":"-3","pressure":"847","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:46","sunset":"18:50","local_uv_irradiance_index":"High","min_gts_temp":"-76","max_gts_temp":"4"},{"id":"270","terrestrial_date":"2013-06-28","sol":"318","ls":"342","season":"Month 12","min_temp":"-74","max_temp":"-4","pressure":"849","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:46","sunset":"18:51","local_uv_irradiance_index":"High","min_gts_temp":"-77","max_gts_temp":"3"},{"id":"268","terrestrial_date":"2013-06-27","sol":"317","ls":"342","season":"Month 12","min_temp":"-72","max_temp":"-4","pressure":"849","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:46","sunset":"18:51","local_uv_irradiance_index":"High","min_gts_temp":"-73","max_gts_temp":"2"},{"id":"288","terrestrial_date":"2013-06-26","sol":"316","ls":"341","season":"Month 12","min_temp":"-74","max_temp":"-16","pressure":"851","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:46","sunset":"18:51","local_uv_irradiance_index":"High","min_gts_temp":"-72","max_gts_temp":"2"},{"id":"273","terrestrial_date":"2013-06-25","sol":"315","ls":"341","season":"Month 12","min_temp":"-74","max_temp":"-10","pressure":"850","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:46","sunset":"18:51","local_uv_irradiance_index":"High","min_gts_temp":"-72","max_gts_temp":"3"},{"id":"272","terrestrial_date":"2013-06-24","sol":"314","ls":"340","season":"Month 12","min_temp":"-73","max_temp":"-11","pressure":"848","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:46","sunset":"18:51","local_uv_irradiance_index":"High","min_gts_temp":"-73","max_gts_temp":"2"},{"id":"274","terrestrial_date":"2013-06-23","sol":"313","ls":"340","season":"Month 12","min_temp":"-77","max_temp":"-14","pressure":"849","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:46","sunset":"18:52","local_uv_irradiance_index":"High","min_gts_temp":"-70","max_gts_temp":"2"},{"id":"285","terrestrial_date":"2013-06-22","sol":"312","ls":"339","season":"Month 12","min_temp":"-71","max_temp":"-11","pressure":"845","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:46","sunset":"18:52","local_uv_irradiance_index":"High","min_gts_temp":"-70","max_gts_temp":"-1"},{"id":"277","terrestrial_date":"2013-06-21","sol":"311","ls":"339","season":"Month 12","min_temp":"-74","max_temp":"-8","pressure":"846","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:46","sunset":"18:52","local_uv_irradiance_index":"High","min_gts_temp":"-70","max_gts_temp":"-2"},{"id":"278","terrestrial_date":"2013-06-20","sol":"310","ls":"338","season":"Month 12","min_temp":"-71","max_temp":"-9","pressure":"849","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:47","sunset":"18:52","local_uv_irradiance_index":"High","min_gts_temp":"-69","max_gts_temp":"-2"},{"id":"284","terrestrial_date":"2013-06-19","sol":"309","ls":"338","season":"Month 12","min_temp":"-72","max_temp":"-7","pressure":"848","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:47","sunset":"18:52","local_uv_irradiance_index":"High","min_gts_temp":"-71","max_gts_temp":"-2"},{"id":"282","terrestrial_date":"2013-06-18","sol":"308","ls":"337","season":"Month 12","min_temp":"-73","max_temp":"-5","pressure":"850","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:47","sunset":"18:53","local_uv_irradiance_index":"High","min_gts_temp":"-69","max_gts_temp":"-1"},{"id":"279","terrestrial_date":"2013-06-17","sol":"307","ls":"336","season":"Month 12","min_temp":"-72","max_temp":"-8","pressure":"851","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:47","sunset":"18:53","local_uv_irradiance_index":"High","min_gts_temp":"-75","max_gts_temp":"-3"},{"id":"280","terrestrial_date":"2013-06-16","sol":"306","ls":"336","season":"Month 12","min_temp":"-71","max_temp":"-8","pressure":"848","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:47","sunset":"18:53","local_uv_irradiance_index":"High","min_gts_temp":"-75","max_gts_temp":"2"},{"id":"275","terrestrial_date":"2013-06-15","sol":"305","ls":"335","season":"Month 12","min_temp":"-71","max_temp":"-5","pressure":"847","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:47","sunset":"18:53","local_uv_irradiance_index":"High","min_gts_temp":"-74","max_gts_temp":"1"},{"id":"281","terrestrial_date":"2013-06-14","sol":"304","ls":"335","season":"Month 12","min_temp":"-70","max_temp":"-6","pressure":"848","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:47","sunset":"18:53","local_uv_irradiance_index":"High","min_gts_temp":"-72","max_gts_temp":"1"},{"id":"283","terrestrial_date":"2013-06-13","sol":"303","ls":"334","season":"Month 12","min_temp":"-71","max_temp":"-8","pressure":"849","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:47","sunset":"18:53","local_uv_irradiance_index":"High","min_gts_temp":"-74","max_gts_temp":"1"},{"id":"287","terrestrial_date":"2013-06-12","sol":"302","ls":"334","season":"Month 12","min_temp":"-71","max_temp":"-3","pressure":"850","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:47","sunset":"18:54","local_uv_irradiance_index":"High","min_gts_temp":"-69","max_gts_temp":"1"},{"id":"276","terrestrial_date":"2013-06-11","sol":"301","ls":"333","season":"Month 12","min_temp":"-73","max_temp":"-6","pressure":"852","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:47","sunset":"18:54","local_uv_irradiance_index":"High","min_gts_temp":"-72","max_gts_temp":"-4"},{"id":"286","terrestrial_date":"2013-06-10","sol":"300","ls":"333","season":"Month 12","min_temp":"-72","max_temp":"-8","pressure":"852","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:47","sunset":"18:54","local_uv_irradiance_index":"High","min_gts_temp":"-73","max_gts_temp":"-2"},{"id":"267","terrestrial_date":"2013-06-09","sol":"299","ls":"332","season":"Month 12","min_temp":"-71","max_temp":"-8","pressure":"849","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:47","sunset":"18:54","local_uv_irradiance_index":"High","min_gts_temp":"-75","max_gts_temp":"-1"},{"id":"266","terrestrial_date":"2013-06-08","sol":"298","ls":"331","season":"Month 12","min_temp":"-72","max_temp":"-3","pressure":"850","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:47","sunset":"18:54","local_uv_irradiance_index":"High","min_gts_temp":"-76","max_gts_temp":"3"},{"id":"265","terrestrial_date":"2013-06-07","sol":"297","ls":"331","season":"Month 12","min_temp":"-72","max_temp":"-7","pressure":"849","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:47","sunset":"18:54","local_uv_irradiance_index":"High","min_gts_temp":"-73","max_gts_temp":"2"},{"id":"264","terrestrial_date":"2013-06-06","sol":"296","ls":"330","season":"Month 12","min_temp":"-71","max_temp":"-9","pressure":"850","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:46","sunset":"18:54","local_uv_irradiance_index":"High","min_gts_temp":"-73","max_gts_temp":"1"},{"id":"263","terrestrial_date":"2013-06-05","sol":"295","ls":"330","season":"Month 12","min_temp":"-71","max_temp":"-15","pressure":"853","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:46","sunset":"18:54","local_uv_irradiance_index":"High","min_gts_temp":"-71","max_gts_temp":"-1"},{"id":"262","terrestrial_date":"2013-06-04","sol":"294","ls":"329","season":"Month 11","min_temp":"-72","max_temp":"-4","pressure":"852","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:46","sunset":"18:54","local_uv_irradiance_index":"High","min_gts_temp":"-71","max_gts_temp":"-1"},{"id":"261","terrestrial_date":"2013-06-03","sol":"293","ls":"329","season":"Month 11","min_temp":"-72","max_temp":"-4","pressure":"850","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:46","sunset":"18:55","local_uv_irradiance_index":"High","min_gts_temp":"-72","max_gts_temp":"0"},{"id":"260","terrestrial_date":"2013-06-02","sol":"292","ls":"328","season":"Month 11","min_temp":"-71","max_temp":"-1","pressure":"853","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:46","sunset":"18:55","local_uv_irradiance_index":"High","min_gts_temp":"-71","max_gts_temp":"-1"},{"id":"259","terrestrial_date":"2013-06-01","sol":"291","ls":"327","season":"Month 11","min_temp":"-70","max_temp":"-4","pressure":"853","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:46","sunset":"18:55","local_uv_irradiance_index":"High","min_gts_temp":"-70","max_gts_temp":"0"},{"id":"258","terrestrial_date":"2013-05-31","sol":"290","ls":"327","season":"Month 11","min_temp":"-71","max_temp":"-3","pressure":"853","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:46","sunset":"18:55","local_uv_irradiance_index":"High","min_gts_temp":"-71","max_gts_temp":"0"},{"id":"257","terrestrial_date":"2013-05-30","sol":"289","ls":"326","season":"Month 11","min_temp":"-71","max_temp":"-3","pressure":"854","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:46","sunset":"18:55","local_uv_irradiance_index":"High","min_gts_temp":"-70","max_gts_temp":"0"},{"id":"256","terrestrial_date":"2013-05-29","sol":"288","ls":"326","season":"Month 11","min_temp":"-69","max_temp":"-4","pressure":"854","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:46","sunset":"18:55","local_uv_irradiance_index":"High","min_gts_temp":"-70","max_gts_temp":"0"},{"id":"255","terrestrial_date":"2013-05-27","sol":"287","ls":"325","season":"Month 11","min_temp":"-72","max_temp":"-3","pressure":"856","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:46","sunset":"18:55","local_uv_irradiance_index":"High","min_gts_temp":"-69","max_gts_temp":"-1"},{"id":"254","terrestrial_date":"2013-05-26","sol":"286","ls":"325","season":"Month 11","min_temp":"-71","max_temp":"-2","pressure":"854","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:46","sunset":"18:55","local_uv_irradiance_index":"High","min_gts_temp":"-70","max_gts_temp":"-1"},{"id":"253","terrestrial_date":"2013-05-25","sol":"285","ls":"324","season":"Month 11","min_temp":"-70","max_temp":"-5","pressure":"852","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:45","sunset":"18:55","local_uv_irradiance_index":"High","min_gts_temp":"-71","max_gts_temp":"0"},{"id":"252","terrestrial_date":"2013-05-24","sol":"284","ls":"323","season":"Month 11","min_temp":"-71","max_temp":"-3","pressure":"854","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:45","sunset":"18:55","local_uv_irradiance_index":"High","min_gts_temp":"-70","max_gts_temp":"-1"},{"id":"251","terrestrial_date":"2013-05-23","sol":"283","ls":"323","season":"Month 11","min_temp":"-68","max_temp":"-4","pressure":"854","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:45","sunset":"18:55","local_uv_irradiance_index":"High","min_gts_temp":"-70","max_gts_temp":"1"},{"id":"250","terrestrial_date":"2013-05-22","sol":"282","ls":"322","season":"Month 11","min_temp":"-72","max_temp":"-2","pressure":"856","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:45","sunset":"18:55","local_uv_irradiance_index":"High","min_gts_temp":"-70","max_gts_temp":"-1"},{"id":"249","terrestrial_date":"2013-05-21","sol":"281","ls":"322","season":"Month 11","min_temp":"-68","max_temp":"-2","pressure":"855","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:45","sunset":"18:55","local_uv_irradiance_index":"High","min_gts_temp":"-70","max_gts_temp":"0"},{"id":"248","terrestrial_date":"2013-05-20","sol":"280","ls":"321","season":"Month 11","min_temp":"-69","max_temp":"-4","pressure":"856","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:45","sunset":"18:55","local_uv_irradiance_index":"High","min_gts_temp":"-70","max_gts_temp":"0"},{"id":"247","terrestrial_date":"2013-05-19","sol":"279","ls":"321","season":"Month 11","min_temp":"-68","max_temp":"-5","pressure":"856","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:45","sunset":"18:55","local_uv_irradiance_index":"High","min_gts_temp":"-68","max_gts_temp":"-1"},{"id":"246","terrestrial_date":"2013-05-18","sol":"278","ls":"320","season":"Month 11","min_temp":"-67","max_temp":"-3","pressure":"855","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:44","sunset":"18:55","local_uv_irradiance_index":"High","min_gts_temp":"-67","max_gts_temp":"-1"},{"id":"245","terrestrial_date":"2013-05-17","sol":"277","ls":"319","season":"Month 11","min_temp":"-69","max_temp":"-4","pressure":"857","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:44","sunset":"18:55","local_uv_irradiance_index":"High","min_gts_temp":"-69","max_gts_temp":"-1"},{"id":"244","terrestrial_date":"2013-05-16","sol":"276","ls":"319","season":"Month 11","min_temp":"-69","max_temp":"-4","pressure":"858","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:44","sunset":"18:55","local_uv_irradiance_index":"Moderate","min_gts_temp":"-71","max_gts_temp":"0"},{"id":"169","terrestrial_date":"2013-05-15","sol":"275","ls":"318","season":"Month 11","min_temp":"-67","max_temp":"-6","pressure":"860","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:44","sunset":"18:55","local_uv_irradiance_index":"High","min_gts_temp":"-68","max_gts_temp":"-1"},{"id":"168","terrestrial_date":"2013-05-14","sol":"274","ls":"318","season":"Month 11","min_temp":"-70","max_temp":"-2","pressure":"861","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:44","sunset":"18:55","local_uv_irradiance_index":"High","min_gts_temp":"-68","max_gts_temp":"-1"},{"id":"167","terrestrial_date":"2013-05-13","sol":"273","ls":"317","season":"Month 11","min_temp":"-70","max_temp":"-4","pressure":"861","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:43","sunset":"18:55","local_uv_irradiance_index":"High","min_gts_temp":"-67","max_gts_temp":"-1"},{"id":"166","terrestrial_date":"2013-05-12","sol":"272","ls":"316","season":"Month 11","min_temp":"-71","max_temp":"-5","pressure":"864","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:43","sunset":"18:54","local_uv_irradiance_index":"High","min_gts_temp":"-72","max_gts_temp":"-1"},{"id":"165","terrestrial_date":"2013-05-11","sol":"271","ls":"316","season":"Month 11","min_temp":"-69","max_temp":"-6","pressure":"864","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:43","sunset":"18:54","local_uv_irradiance_index":"High","min_gts_temp":"-70","max_gts_temp":"2"},{"id":"164","terrestrial_date":"2013-05-10","sol":"270","ls":"315","season":"Month 11","min_temp":"-67","max_temp":"-6","pressure":"863","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:43","sunset":"18:54","local_uv_irradiance_index":"High","min_gts_temp":"-71","max_gts_temp":"2"},{"id":"162","terrestrial_date":"2013-05-09","sol":"269","ls":"315","season":"Month 11","min_temp":"-72","max_temp":"-3","pressure":"866","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:42","sunset":"18:54","local_uv_irradiance_index":"High","min_gts_temp":"-71","max_gts_temp":"2"},{"id":"161","terrestrial_date":"2013-05-08","sol":"268","ls":"314","season":"Month 11","min_temp":"-70","max_temp":"-5","pressure":"864","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:42","sunset":"18:54","local_uv_irradiance_index":"High","min_gts_temp":"-71","max_gts_temp":"2"},{"id":"160","terrestrial_date":"2013-05-07","sol":"267","ls":"313","season":"Month 11","min_temp":"--","max_temp":"--","pressure":"--","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:42","sunset":"18:54","local_uv_irradiance_index":"--","min_gts_temp":"--","max_gts_temp":"--"},{"id":"159","terrestrial_date":"2013-05-02","sol":"262","ls":"311","season":"Month 11","min_temp":"--","max_temp":"--","pressure":"--","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:40","sunset":"18:53","local_uv_irradiance_index":"--","min_gts_temp":"--","max_gts_temp":"--"},{"id":"158","terrestrial_date":"2013-05-01","sol":"261","ls":"310","season":"Month 11","min_temp":"-70","max_temp":"-4","pressure":"868","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:40","sunset":"18:53","local_uv_irradiance_index":"High","min_gts_temp":"-73","max_gts_temp":"4"},{"id":"157","terrestrial_date":"2013-04-30","sol":"260","ls":"309","season":"Month 11","min_temp":"-70","max_temp":"-11","pressure":"875","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:40","sunset":"18:53","local_uv_irradiance_index":"High","min_gts_temp":"-72","max_gts_temp":"2"},{"id":"155","terrestrial_date":"2013-04-29","sol":"259","ls":"309","season":"Month 11","min_temp":"-72","max_temp":"-3","pressure":"871","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:39","sunset":"18:52","local_uv_irradiance_index":"High","min_gts_temp":"-73","max_gts_temp":"6"},{"id":"154","terrestrial_date":"2013-04-28","sol":"258","ls":"308","season":"Month 11","min_temp":"-71","max_temp":"-9","pressure":"869","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:39","sunset":"18:52","local_uv_irradiance_index":"Very_High","min_gts_temp":"-73","max_gts_temp":"6"},{"id":"153","terrestrial_date":"2013-04-27","sol":"257","ls":"308","season":"Month 11","min_temp":"-70","max_temp":"-1","pressure":"871","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:39","sunset":"18:52","local_uv_irradiance_index":"Very_High","min_gts_temp":"-73","max_gts_temp":"5"},{"id":"152","terrestrial_date":"2013-04-26","sol":"256","ls":"307","season":"Month 11","min_temp":"-70","max_temp":"-2","pressure":"871","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:38","sunset":"18:52","local_uv_irradiance_index":"Very_High","min_gts_temp":"-73","max_gts_temp":"5"},{"id":"151","terrestrial_date":"2013-04-25","sol":"255","ls":"306","season":"Month 11","min_temp":"-69","max_temp":"-5","pressure":"877","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:38","sunset":"18:51","local_uv_irradiance_index":"Very_High","min_gts_temp":"-72","max_gts_temp":"6"},{"id":"150","terrestrial_date":"2013-04-24","sol":"254","ls":"306","season":"Month 11","min_temp":"-70","max_temp":"-3","pressure":"871","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:37","sunset":"18:51","local_uv_irradiance_index":"Very_High","min_gts_temp":"-73","max_gts_temp":"4"},{"id":"149","terrestrial_date":"2013-04-23","sol":"253","ls":"305","season":"Month 11","min_temp":"-71","max_temp":"-6","pressure":"877","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:37","sunset":"18:51","local_uv_irradiance_index":"Very_High","min_gts_temp":"-73","max_gts_temp":"5"},{"id":"148","terrestrial_date":"2013-04-22","sol":"252","ls":"304","season":"Month 11","min_temp":"-71","max_temp":"-8","pressure":"874","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:37","sunset":"18:51","local_uv_irradiance_index":"Very_High","min_gts_temp":"-72","max_gts_temp":"5"},{"id":"147","terrestrial_date":"2013-04-20","sol":"251","ls":"304","season":"Month 11","min_temp":"-71","max_temp":"-2","pressure":"876","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:36","sunset":"18:50","local_uv_irradiance_index":"Very_High","min_gts_temp":"-73","max_gts_temp":"7"},{"id":"146","terrestrial_date":"2013-04-19","sol":"250","ls":"303","season":"Month 11","min_temp":"-70","max_temp":"-2","pressure":"877","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:36","sunset":"18:50","local_uv_irradiance_index":"Very_High","min_gts_temp":"-72","max_gts_temp":"5"},{"id":"144","terrestrial_date":"2013-04-18","sol":"249","ls":"303","season":"Month 11","min_temp":"-69","max_temp":"-3","pressure":"873","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:35","sunset":"18:50","local_uv_irradiance_index":"High","min_gts_temp":"-72","max_gts_temp":"5"},{"id":"143","terrestrial_date":"2013-04-17","sol":"248","ls":"302","season":"Month 11","min_temp":"-70","max_temp":"-4","pressure":"877","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:35","sunset":"18:49","local_uv_irradiance_index":"High","min_gts_temp":"-71","max_gts_temp":"3"},{"id":"142","terrestrial_date":"2013-04-16","sol":"247","ls":"301","season":"Month 11","min_temp":"-71","max_temp":"-4","pressure":"873","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:35","sunset":"18:49","local_uv_irradiance_index":"Very_High","min_gts_temp":"-73","max_gts_temp":"6"},{"id":"141","terrestrial_date":"2013-04-15","sol":"246","ls":"301","season":"Month 11","min_temp":"-73","max_temp":"-1","pressure":"878","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:34","sunset":"18:49","local_uv_irradiance_index":"Very_High","min_gts_temp":"-73","max_gts_temp":"6"},{"id":"140","terrestrial_date":"2013-04-14","sol":"245","ls":"300","season":"Month 11","min_temp":"-70","max_temp":"-6","pressure":"879","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:34","sunset":"18:48","local_uv_irradiance_index":"Very_High","min_gts_temp":"-72","max_gts_temp":"6"},{"id":"139","terrestrial_date":"2013-04-13","sol":"244","ls":"300","season":"Month 11","min_temp":"-69","max_temp":"-6","pressure":"878","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:33","sunset":"18:48","local_uv_irradiance_index":"Very_High","min_gts_temp":"-72","max_gts_temp":"7"},{"id":"138","terrestrial_date":"2013-04-12","sol":"243","ls":"299","season":"Month 10","min_temp":"-71","max_temp":"-7","pressure":"884","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:33","sunset":"18:48","local_uv_irradiance_index":"Very_High","min_gts_temp":"-73","max_gts_temp":"6"},{"id":"137","terrestrial_date":"2013-04-11","sol":"242","ls":"298","season":"Month 10","min_temp":"-70","max_temp":"-7","pressure":"881","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:32","sunset":"18:47","local_uv_irradiance_index":"Very_High","min_gts_temp":"-72","max_gts_temp":"6"},{"id":"136","terrestrial_date":"2013-04-10","sol":"241","ls":"298","season":"Month 10","min_temp":"-72","max_temp":"0","pressure":"884","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:32","sunset":"18:47","local_uv_irradiance_index":"Very_High","min_gts_temp":"-73","max_gts_temp":"5"},{"id":"135","terrestrial_date":"2013-04-09","sol":"240","ls":"297","season":"Month 10","min_temp":"-70","max_temp":"-6","pressure":"884","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:31","sunset":"18:47","local_uv_irradiance_index":"Very_High","min_gts_temp":"-72","max_gts_temp":"5"},{"id":"133","terrestrial_date":"2013-04-08","sol":"239","ls":"297","season":"Month 10","min_temp":"-73","max_temp":"-1","pressure":"885","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:31","sunset":"18:46","local_uv_irradiance_index":"Very_High","min_gts_temp":"-73","max_gts_temp":"5"},{"id":"132","terrestrial_date":"2013-04-07","sol":"238","ls":"296","season":"Month 10","min_temp":"-70","max_temp":"-2","pressure":"885","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:30","sunset":"18:46","local_uv_irradiance_index":"Very_High","min_gts_temp":"-72","max_gts_temp":"7"},{"id":"131","terrestrial_date":"2013-04-06","sol":"237","ls":"295","season":"Month 10","min_temp":"-71","max_temp":"-5","pressure":"883","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:30","sunset":"18:45","local_uv_irradiance_index":"Very_High","min_gts_temp":"-73","max_gts_temp":"6"},{"id":"130","terrestrial_date":"2013-04-05","sol":"236","ls":"295","season":"Month 10","min_temp":"-72","max_temp":"-3","pressure":"886","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:29","sunset":"18:45","local_uv_irradiance_index":"Very_High","min_gts_temp":"-72","max_gts_temp":"6"},{"id":"129","terrestrial_date":"2013-04-04","sol":"235","ls":"294","season":"Month 10","min_temp":"-70","max_temp":"0","pressure":"886","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:29","sunset":"18:44","local_uv_irradiance_index":"Very_High","min_gts_temp":"-73","max_gts_temp":"7"},{"id":"128","terrestrial_date":"2013-04-03","sol":"234","ls":"293","season":"Month 10","min_temp":"-69","max_temp":"-4","pressure":"890","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:28","sunset":"18:44","local_uv_irradiance_index":"Very_High","min_gts_temp":"-73","max_gts_temp":"5"},{"id":"127","terrestrial_date":"2013-04-02","sol":"233","ls":"293","season":"Month 10","min_temp":"-69","max_temp":"-3","pressure":"889","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:28","sunset":"18:44","local_uv_irradiance_index":"Very_High","min_gts_temp":"-73","max_gts_temp":"6"},{"id":"126","terrestrial_date":"2013-04-01","sol":"232","ls":"292","season":"Month 10","min_temp":"-69","max_temp":"-5","pressure":"888","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:27","sunset":"18:43","local_uv_irradiance_index":"Very_High","min_gts_temp":"-72","max_gts_temp":"6"},{"id":"125","terrestrial_date":"2013-03-31","sol":"231","ls":"292","season":"Month 10","min_temp":"-71","max_temp":"-6","pressure":"890","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:26","sunset":"18:43","local_uv_irradiance_index":"Very_High","min_gts_temp":"-73","max_gts_temp":"7"},{"id":"124","terrestrial_date":"2013-03-30","sol":"230","ls":"291","season":"Month 10","min_temp":"-69","max_temp":"0","pressure":"888","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:26","sunset":"18:42","local_uv_irradiance_index":"Very_High","min_gts_temp":"-72","max_gts_temp":"7"},{"id":"122","terrestrial_date":"2013-03-29","sol":"229","ls":"290","season":"Month 10","min_temp":"-69","max_temp":"-4","pressure":"894","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:25","sunset":"18:42","local_uv_irradiance_index":"Very_High","min_gts_temp":"-70","max_gts_temp":"7"},{"id":"121","terrestrial_date":"2013-03-28","sol":"228","ls":"290","season":"Month 10","min_temp":"-71","max_temp":"-3","pressure":"894","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:25","sunset":"18:41","local_uv_irradiance_index":"Very_High","min_gts_temp":"-71","max_gts_temp":"7"},{"id":"120","terrestrial_date":"2013-03-27","sol":"227","ls":"289","season":"Month 10","min_temp":"-70","max_temp":"4","pressure":"892","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:24","sunset":"18:41","local_uv_irradiance_index":"Very_High","min_gts_temp":"-72","max_gts_temp":"7"},{"id":"119","terrestrial_date":"2013-03-26","sol":"226","ls":"288","season":"Month 10","min_temp":"-70","max_temp":"1","pressure":"894","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:24","sunset":"18:40","local_uv_irradiance_index":"Very_High","min_gts_temp":"-71","max_gts_temp":"6"},{"id":"118","terrestrial_date":"2013-03-25","sol":"225","ls":"288","season":"Month 10","min_temp":"-71","max_temp":"-8","pressure":"894","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:23","sunset":"18:40","local_uv_irradiance_index":"Very_High","min_gts_temp":"-72","max_gts_temp":"7"},{"id":"117","terrestrial_date":"2013-03-24","sol":"224","ls":"287","season":"Month 10","min_temp":"-69","max_temp":"-1","pressure":"894","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:22","sunset":"18:39","local_uv_irradiance_index":"Very_High","min_gts_temp":"-71","max_gts_temp":"6"},{"id":"116","terrestrial_date":"2013-03-23","sol":"223","ls":"287","season":"Month 10","min_temp":"-71","max_temp":"-5","pressure":"895","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:22","sunset":"18:39","local_uv_irradiance_index":"Very_High","min_gts_temp":"-72","max_gts_temp":"6"},{"id":"115","terrestrial_date":"2013-03-22","sol":"222","ls":"286","season":"Month 10","min_temp":"--","max_temp":"--","pressure":"--","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:21","sunset":"18:38","local_uv_irradiance_index":"--","min_gts_temp":"--","max_gts_temp":"--"},{"id":"113","terrestrial_date":"2013-03-15","sol":"215","ls":"281","season":"Month 10","min_temp":"--","max_temp":"--","pressure":"--","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:17","sunset":"18:34","local_uv_irradiance_index":"--","min_gts_temp":"--","max_gts_temp":"--"},{"id":"111","terrestrial_date":"2013-02-27","sol":"200","ls":"272","season":"Month 10","min_temp":"--","max_temp":"--","pressure":"--","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:06","sunset":"18:24","local_uv_irradiance_index":"--","min_gts_temp":"--","max_gts_temp":"--"},{"id":"109","terrestrial_date":"2013-02-26","sol":"199","ls":"271","season":"Month 10","min_temp":"-66","max_temp":"0","pressure":"917","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:06","sunset":"18:24","local_uv_irradiance_index":"High","min_gts_temp":"-71","max_gts_temp":"4"},{"id":"108","terrestrial_date":"2013-02-25","sol":"198","ls":"271","season":"Month 10","min_temp":"-67","max_temp":"-2","pressure":"914","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:05","sunset":"18:23","local_uv_irradiance_index":"High","min_gts_temp":"-70","max_gts_temp":"6"},{"id":"107","terrestrial_date":"2013-02-24","sol":"197","ls":"270","season":"Month 10","min_temp":"-68","max_temp":"-3","pressure":"915","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:04","sunset":"18:22","local_uv_irradiance_index":"High","min_gts_temp":"-72","max_gts_temp":"5"},{"id":"106","terrestrial_date":"2013-02-23","sol":"196","ls":"269","season":"Month 9","min_temp":"-66","max_temp":"-3","pressure":"916","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:04","sunset":"18:21","local_uv_irradiance_index":"High","min_gts_temp":"-70","max_gts_temp":"7"},{"id":"105","terrestrial_date":"2013-02-22","sol":"195","ls":"269","season":"Month 9","min_temp":"--","max_temp":"--","pressure":"--","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:03","sunset":"18:21","local_uv_irradiance_index":"--","min_gts_temp":"--","max_gts_temp":"--"},{"id":"104","terrestrial_date":"2013-02-19","sol":"192","ls":"267","season":"Month 9","min_temp":"--","max_temp":"--","pressure":"--","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:01","sunset":"18:19","local_uv_irradiance_index":"--","min_gts_temp":"--","max_gts_temp":"--"},{"id":"103","terrestrial_date":"2013-02-18","sol":"191","ls":"266","season":"Month 9","min_temp":"-67","max_temp":"-3","pressure":"921","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"06:00","sunset":"18:18","local_uv_irradiance_index":"Very_High","min_gts_temp":"-71","max_gts_temp":"7"},{"id":"102","terrestrial_date":"2013-02-17","sol":"190","ls":"265","season":"Month 9","min_temp":"-66","max_temp":"-3","pressure":"917","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:59","sunset":"18:17","local_uv_irradiance_index":"High","min_gts_temp":"-72","max_gts_temp":"7"},{"id":"100","terrestrial_date":"2013-02-16","sol":"189","ls":"265","season":"Month 9","min_temp":"-68","max_temp":"-2","pressure":"920","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:59","sunset":"18:16","local_uv_irradiance_index":"High","min_gts_temp":"-71","max_gts_temp":"6"},{"id":"99","terrestrial_date":"2013-02-15","sol":"188","ls":"264","season":"Month 9","min_temp":"-68","max_temp":"-3","pressure":"920","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:58","sunset":"18:16","local_uv_irradiance_index":"Very_High","min_gts_temp":"-72","max_gts_temp":"7"},{"id":"98","terrestrial_date":"2013-02-14","sol":"187","ls":"263","season":"Month 9","min_temp":"-67","max_temp":"-1","pressure":"921","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:57","sunset":"18:15","local_uv_irradiance_index":"High","min_gts_temp":"-73","max_gts_temp":"7"},{"id":"97","terrestrial_date":"2013-02-13","sol":"186","ls":"263","season":"Month 9","min_temp":"-66","max_temp":"-2","pressure":"922","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:57","sunset":"18:14","local_uv_irradiance_index":"High","min_gts_temp":"-71","max_gts_temp":"5"},{"id":"96","terrestrial_date":"2013-02-12","sol":"185","ls":"262","season":"Month 9","min_temp":"-67","max_temp":"-5","pressure":"923","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:56","sunset":"18:14","local_uv_irradiance_index":"High","min_gts_temp":"-72","max_gts_temp":"7"},{"id":"95","terrestrial_date":"2013-02-11","sol":"184","ls":"261","season":"Month 9","min_temp":"-67","max_temp":"0","pressure":"923","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:55","sunset":"18:13","local_uv_irradiance_index":"High","min_gts_temp":"-71","max_gts_temp":"6"},{"id":"94","terrestrial_date":"2013-02-10","sol":"183","ls":"261","season":"Month 9","min_temp":"-67","max_temp":"-1","pressure":"921","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:55","sunset":"18:12","local_uv_irradiance_index":"High","min_gts_temp":"-72","max_gts_temp":"6"},{"id":"93","terrestrial_date":"2013-02-09","sol":"182","ls":"260","season":"Month 9","min_temp":"-67","max_temp":"0","pressure":"921","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:54","sunset":"18:11","local_uv_irradiance_index":"High","min_gts_temp":"-70","max_gts_temp":"7"},{"id":"92","terrestrial_date":"2013-02-08","sol":"181","ls":"260","season":"Month 9","min_temp":"-67","max_temp":"1","pressure":"918","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:53","sunset":"18:11","local_uv_irradiance_index":"High","min_gts_temp":"-70","max_gts_temp":"6"},{"id":"91","terrestrial_date":"2013-02-07","sol":"180","ls":"259","season":"Month 9","min_temp":"-67","max_temp":"-1","pressure":"919","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:52","sunset":"18:10","local_uv_irradiance_index":"High","min_gts_temp":"-70","max_gts_temp":"6"},{"id":"89","terrestrial_date":"2013-02-06","sol":"179","ls":"258","season":"Month 9","min_temp":"-66","max_temp":"-7","pressure":"920","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:52","sunset":"18:09","local_uv_irradiance_index":"High","min_gts_temp":"-72","max_gts_temp":"5"},{"id":"88","terrestrial_date":"2013-02-04","sol":"178","ls":"258","season":"Month 9","min_temp":"-66","max_temp":"-2","pressure":"920","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:51","sunset":"18:09","local_uv_irradiance_index":"High","min_gts_temp":"-71","max_gts_temp":"6"},{"id":"87","terrestrial_date":"2013-02-03","sol":"177","ls":"257","season":"Month 9","min_temp":"-66","max_temp":"-1","pressure":"921","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:50","sunset":"18:08","local_uv_irradiance_index":"High","min_gts_temp":"-69","max_gts_temp":"5"},{"id":"86","terrestrial_date":"2013-02-02","sol":"176","ls":"256","season":"Month 9","min_temp":"-67","max_temp":"0","pressure":"920","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:50","sunset":"18:07","local_uv_irradiance_index":"High","min_gts_temp":"-69","max_gts_temp":"6"},{"id":"85","terrestrial_date":"2013-02-01","sol":"175","ls":"256","season":"Month 9","min_temp":"-68","max_temp":"-4","pressure":"921","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:49","sunset":"18:06","local_uv_irradiance_index":"High","min_gts_temp":"-70","max_gts_temp":"6"},{"id":"84","terrestrial_date":"2013-01-31","sol":"174","ls":"255","season":"Month 9","min_temp":"-67","max_temp":"0","pressure":"921","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:48","sunset":"18:06","local_uv_irradiance_index":"High","min_gts_temp":"-71","max_gts_temp":"7"},{"id":"83","terrestrial_date":"2013-01-30","sol":"173","ls":"254","season":"Month 9","min_temp":"-67","max_temp":"0","pressure":"920","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:48","sunset":"18:05","local_uv_irradiance_index":"High","min_gts_temp":"-71","max_gts_temp":"6"},{"id":"82","terrestrial_date":"2013-01-29","sol":"172","ls":"254","season":"Month 9","min_temp":"-67","max_temp":"0","pressure":"923","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:47","sunset":"18:04","local_uv_irradiance_index":"High","min_gts_temp":"-71","max_gts_temp":"6"},{"id":"81","terrestrial_date":"2013-01-28","sol":"171","ls":"253","season":"Month 9","min_temp":"-67","max_temp":"1","pressure":"925","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:46","sunset":"18:03","local_uv_irradiance_index":"High","min_gts_temp":"-71","max_gts_temp":"6"},{"id":"80","terrestrial_date":"2013-01-27","sol":"170","ls":"252","season":"Month 9","min_temp":"-66","max_temp":"0","pressure":"925","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:46","sunset":"18:03","local_uv_irradiance_index":"High","min_gts_temp":"-69","max_gts_temp":"5"},{"id":"78","terrestrial_date":"2013-01-26","sol":"169","ls":"252","season":"Month 9","min_temp":"-66","max_temp":"0","pressure":"922","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:45","sunset":"18:02","local_uv_irradiance_index":"High","min_gts_temp":"-70","max_gts_temp":"6"},{"id":"77","terrestrial_date":"2013-01-25","sol":"168","ls":"251","season":"Month 9","min_temp":"-66","max_temp":"1","pressure":"923","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:45","sunset":"18:01","local_uv_irradiance_index":"High","min_gts_temp":"-69","max_gts_temp":"7"},{"id":"76","terrestrial_date":"2013-01-24","sol":"167","ls":"250","season":"Month 9","min_temp":"-65","max_temp":"-3","pressure":"920","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:44","sunset":"18:01","local_uv_irradiance_index":"High","min_gts_temp":"-69","max_gts_temp":"6"},{"id":"75","terrestrial_date":"2013-01-23","sol":"166","ls":"250","season":"Month 9","min_temp":"-65","max_temp":"-5","pressure":"922","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:43","sunset":"18:00","local_uv_irradiance_index":"High","min_gts_temp":"-67","max_gts_temp":"4"},{"id":"74","terrestrial_date":"2013-01-22","sol":"165","ls":"249","season":"Month 9","min_temp":"-65","max_temp":"-3","pressure":"922","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:43","sunset":"17:59","local_uv_irradiance_index":"High","min_gts_temp":"-69","max_gts_temp":"4"},{"id":"73","terrestrial_date":"2013-01-21","sol":"164","ls":"248","season":"Month 9","min_temp":"-64","max_temp":"-1","pressure":"919","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:42","sunset":"17:59","local_uv_irradiance_index":"High","min_gts_temp":"-68","max_gts_temp":"7"},{"id":"72","terrestrial_date":"2013-01-20","sol":"163","ls":"248","season":"Month 9","min_temp":"-65","max_temp":"-1","pressure":"919","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:41","sunset":"17:58","local_uv_irradiance_index":"Very_High","min_gts_temp":"-71","max_gts_temp":"6"},{"id":"71","terrestrial_date":"2013-01-19","sol":"162","ls":"247","season":"Month 9","min_temp":"-65","max_temp":"-1","pressure":"919","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:41","sunset":"17:57","local_uv_irradiance_index":"Very_High","min_gts_temp":"-72","max_gts_temp":"7"},{"id":"70","terrestrial_date":"2013-01-18","sol":"161","ls":"246","season":"Month 9","min_temp":"-65","max_temp":"-2","pressure":"919","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:40","sunset":"17:56","local_uv_irradiance_index":"Very_High","min_gts_temp":"-71","max_gts_temp":"5"},{"id":"69","terrestrial_date":"2013-01-17","sol":"160","ls":"246","season":"Month 9","min_temp":"-67","max_temp":"-3","pressure":"919","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:40","sunset":"17:56","local_uv_irradiance_index":"Very_High","min_gts_temp":"-72","max_gts_temp":"6"},{"id":"67","terrestrial_date":"2013-01-16","sol":"159","ls":"245","season":"Month 9","min_temp":"-65","max_temp":"-4","pressure":"918","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:39","sunset":"17:55","local_uv_irradiance_index":"Very_High","min_gts_temp":"-72","max_gts_temp":"5"},{"id":"66","terrestrial_date":"2013-01-15","sol":"158","ls":"245","season":"Month 9","min_temp":"-66","max_temp":"-3","pressure":"922","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:39","sunset":"17:54","local_uv_irradiance_index":"Very_High","min_gts_temp":"-70","max_gts_temp":"4"},{"id":"65","terrestrial_date":"2013-01-14","sol":"157","ls":"244","season":"Month 9","min_temp":"-64","max_temp":"-2","pressure":"920","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:38","sunset":"17:54","local_uv_irradiance_index":"Very_High","min_gts_temp":"-71","max_gts_temp":"5"},{"id":"64","terrestrial_date":"2013-01-13","sol":"156","ls":"243","season":"Month 9","min_temp":"-65","max_temp":"0","pressure":"922","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:37","sunset":"17:53","local_uv_irradiance_index":"Very_High","min_gts_temp":"-73","max_gts_temp":"5"},{"id":"63","terrestrial_date":"2013-01-12","sol":"155","ls":"243","season":"Month 9","min_temp":"-64","max_temp":"0","pressure":"917","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:37","sunset":"17:52","local_uv_irradiance_index":"Very_High","min_gts_temp":"-70","max_gts_temp":"4"},{"id":"62","terrestrial_date":"2013-01-11","sol":"154","ls":"242","season":"Month 9","min_temp":"-65","max_temp":"-2","pressure":"917","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:36","sunset":"17:52","local_uv_irradiance_index":"Very_High","min_gts_temp":"-72","max_gts_temp":"4"},{"id":"61","terrestrial_date":"2013-01-10","sol":"153","ls":"241","season":"Month 9","min_temp":"-65","max_temp":"1","pressure":"915","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:36","sunset":"17:51","local_uv_irradiance_index":"Very_High","min_gts_temp":"-70","max_gts_temp":"4"},{"id":"60","terrestrial_date":"2013-01-09","sol":"152","ls":"241","season":"Month 9","min_temp":"-63","max_temp":"1","pressure":"914","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:35","sunset":"17:51","local_uv_irradiance_index":"Very_High","min_gts_temp":"-69","max_gts_temp":"6"},{"id":"59","terrestrial_date":"2013-01-08","sol":"151","ls":"240","season":"Month 9","min_temp":"-65","max_temp":"-3","pressure":"915","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:35","sunset":"17:50","local_uv_irradiance_index":"Very_High","min_gts_temp":"-69","max_gts_temp":"5"},{"id":"58","terrestrial_date":"2013-01-07","sol":"150","ls":"239","season":"Month 8","min_temp":"-64","max_temp":"-3","pressure":"913","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:34","sunset":"17:49","local_uv_irradiance_index":"Very_High","min_gts_temp":"-73","max_gts_temp":"6"},{"id":"56","terrestrial_date":"2013-01-06","sol":"149","ls":"239","season":"Month 8","min_temp":"-65","max_temp":"0","pressure":"914","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:34","sunset":"17:49","local_uv_irradiance_index":"Very_High","min_gts_temp":"-71","max_gts_temp":"4"},{"id":"55","terrestrial_date":"2013-01-05","sol":"148","ls":"238","season":"Month 8","min_temp":"-65","max_temp":"-3","pressure":"912","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:33","sunset":"17:48","local_uv_irradiance_index":"Very_High","min_gts_temp":"-70","max_gts_temp":"5"},{"id":"54","terrestrial_date":"2013-01-04","sol":"147","ls":"237","season":"Month 8","min_temp":"-65","max_temp":"0","pressure":"914","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:33","sunset":"17:47","local_uv_irradiance_index":"Very_High","min_gts_temp":"-72","max_gts_temp":"6"},{"id":"53","terrestrial_date":"2013-01-03","sol":"146","ls":"237","season":"Month 8","min_temp":"-65","max_temp":"-1","pressure":"908","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:32","sunset":"17:47","local_uv_irradiance_index":"Very_High","min_gts_temp":"-72","max_gts_temp":"8"},{"id":"52","terrestrial_date":"2013-01-02","sol":"145","ls":"236","season":"Month 8","min_temp":"-64","max_temp":"-1","pressure":"909","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:32","sunset":"17:46","local_uv_irradiance_index":"Very_High","min_gts_temp":"-70","max_gts_temp":"7"},{"id":"51","terrestrial_date":"2013-01-01","sol":"144","ls":"235","season":"Month 8","min_temp":"-64","max_temp":"2","pressure":"907","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:31","sunset":"17:46","local_uv_irradiance_index":"Very_High","min_gts_temp":"-71","max_gts_temp":"7"},{"id":"50","terrestrial_date":"2012-12-31","sol":"143","ls":"235","season":"Month 8","min_temp":"-63","max_temp":"-2","pressure":"908","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:31","sunset":"17:45","local_uv_irradiance_index":"Very_High","min_gts_temp":"-72","max_gts_temp":"8"},{"id":"49","terrestrial_date":"2012-12-29","sol":"142","ls":"234","season":"Month 8","min_temp":"-65","max_temp":"0","pressure":"906","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:30","sunset":"17:44","local_uv_irradiance_index":"Very_High","min_gts_temp":"-73","max_gts_temp":"8"},{"id":"48","terrestrial_date":"2012-12-28","sol":"141","ls":"233","season":"Month 8","min_temp":"-64","max_temp":"-2","pressure":"904","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:30","sunset":"17:44","local_uv_irradiance_index":"Very_High","min_gts_temp":"-72","max_gts_temp":"8"},{"id":"47","terrestrial_date":"2012-12-27","sol":"140","ls":"233","season":"Month 8","min_temp":"-66","max_temp":"-2","pressure":"903","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:29","sunset":"17:43","local_uv_irradiance_index":"Very_High","min_gts_temp":"-71","max_gts_temp":"8"},{"id":"45","terrestrial_date":"2012-12-26","sol":"139","ls":"232","season":"Month 8","min_temp":"-66","max_temp":"0","pressure":"899","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:29","sunset":"17:43","local_uv_irradiance_index":"Very_High","min_gts_temp":"-71","max_gts_temp":"9"},{"id":"44","terrestrial_date":"2012-12-25","sol":"138","ls":"232","season":"Month 8","min_temp":"-65","max_temp":"-1","pressure":"899","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:29","sunset":"17:42","local_uv_irradiance_index":"Very_High","min_gts_temp":"-71","max_gts_temp":"8"},{"id":"43","terrestrial_date":"2012-12-24","sol":"137","ls":"231","season":"Month 8","min_temp":"-64","max_temp":"-1","pressure":"896","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:28","sunset":"17:42","local_uv_irradiance_index":"Very_High","min_gts_temp":"-71","max_gts_temp":"8"},{"id":"42","terrestrial_date":"2012-12-23","sol":"136","ls":"230","season":"Month 8","min_temp":"-65","max_temp":"-1","pressure":"897","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:28","sunset":"17:41","local_uv_irradiance_index":"Very_High","min_gts_temp":"-74","max_gts_temp":"7"},{"id":"41","terrestrial_date":"2012-12-22","sol":"135","ls":"230","season":"Month 8","min_temp":"-65","max_temp":"-5","pressure":"894","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:27","sunset":"17:41","local_uv_irradiance_index":"Very_High","min_gts_temp":"-72","max_gts_temp":"8"},{"id":"40","terrestrial_date":"2012-12-21","sol":"134","ls":"229","season":"Month 8","min_temp":"-67","max_temp":"-1","pressure":"893","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:27","sunset":"17:40","local_uv_irradiance_index":"Very_High","min_gts_temp":"-74","max_gts_temp":"7"},{"id":"39","terrestrial_date":"2012-12-20","sol":"133","ls":"228","season":"Month 8","min_temp":"-65","max_temp":"1","pressure":"891","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:27","sunset":"17:39","local_uv_irradiance_index":"Very_High","min_gts_temp":"-69","max_gts_temp":"6"},{"id":"38","terrestrial_date":"2012-12-19","sol":"132","ls":"228","season":"Month 8","min_temp":"-65","max_temp":"-6","pressure":"890","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:26","sunset":"17:39","local_uv_irradiance_index":"Very_High","min_gts_temp":"-69","max_gts_temp":"5"},{"id":"37","terrestrial_date":"2012-12-18","sol":"131","ls":"227","season":"Month 8","min_temp":"-65","max_temp":"-8","pressure":"889","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:26","sunset":"17:38","local_uv_irradiance_index":"Very_High","min_gts_temp":"-70","max_gts_temp":"6"},{"id":"36","terrestrial_date":"2012-12-17","sol":"130","ls":"226","season":"Month 8","min_temp":"-65","max_temp":"-9","pressure":"888","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:26","sunset":"17:38","local_uv_irradiance_index":"High","min_gts_temp":"-70","max_gts_temp":"5"},{"id":"34","terrestrial_date":"2012-12-16","sol":"129","ls":"226","season":"Month 8","min_temp":"-65","max_temp":"-2","pressure":"886","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:25","sunset":"17:37","local_uv_irradiance_index":"High","min_gts_temp":"-69","max_gts_temp":"6"},{"id":"33","terrestrial_date":"2012-12-15","sol":"128","ls":"225","season":"Month 8","min_temp":"-67","max_temp":"-3","pressure":"883","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:25","sunset":"17:37","local_uv_irradiance_index":"High","min_gts_temp":"-69","max_gts_temp":"6"},{"id":"32","terrestrial_date":"2012-12-14","sol":"127","ls":"224","season":"Month 8","min_temp":"-67","max_temp":"-1","pressure":"884","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:25","sunset":"17:37","local_uv_irradiance_index":"Very_High","min_gts_temp":"-71","max_gts_temp":"7"},{"id":"31","terrestrial_date":"2012-12-13","sol":"126","ls":"224","season":"Month 8","min_temp":"-66","max_temp":"-4","pressure":"880","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:24","sunset":"17:36","local_uv_irradiance_index":"Very_High","min_gts_temp":"-71","max_gts_temp":"7"},{"id":"30","terrestrial_date":"2012-12-12","sol":"125","ls":"223","season":"Month 8","min_temp":"-68","max_temp":"-5","pressure":"880","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:24","sunset":"17:36","local_uv_irradiance_index":"Very_High","min_gts_temp":"-66","max_gts_temp":"7"},{"id":"29","terrestrial_date":"2012-12-11","sol":"124","ls":"223","season":"Month 8","min_temp":"-66","max_temp":"-5","pressure":"876","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:24","sunset":"17:35","local_uv_irradiance_index":"Very_High","min_gts_temp":"-63","max_gts_temp":"6"},{"id":"28","terrestrial_date":"2012-12-10","sol":"123","ls":"222","season":"Month 8","min_temp":"-66","max_temp":"-10","pressure":"875","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:23","sunset":"17:35","local_uv_irradiance_index":"High","min_gts_temp":"-62","max_gts_temp":"2"},{"id":"27","terrestrial_date":"2012-12-09","sol":"122","ls":"221","season":"Month 8","min_temp":"-65","max_temp":"-3","pressure":"869","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:23","sunset":"17:34","local_uv_irradiance_index":"Very_High","min_gts_temp":"-66","max_gts_temp":"3"},{"id":"26","terrestrial_date":"2012-12-08","sol":"121","ls":"221","season":"Month 8","min_temp":"-66","max_temp":"0","pressure":"869","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:23","sunset":"17:34","local_uv_irradiance_index":"High","min_gts_temp":"-68","max_gts_temp":"9"},{"id":"25","terrestrial_date":"2012-12-07","sol":"120","ls":"220","season":"Month 8","min_temp":"-67","max_temp":"-3","pressure":"867","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:22","sunset":"17:33","local_uv_irradiance_index":"Very_High","min_gts_temp":"-79","max_gts_temp":"16"},{"id":"23","terrestrial_date":"2012-12-06","sol":"119","ls":"219","season":"Month 8","min_temp":"-66","max_temp":"-6","pressure":"866","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:22","sunset":"17:33","local_uv_irradiance_index":"Very_High","min_gts_temp":"-78","max_gts_temp":"19"},{"id":"22","terrestrial_date":"2012-12-05","sol":"118","ls":"219","season":"Month 8","min_temp":"-65","max_temp":"-6","pressure":"864","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:22","sunset":"17:33","local_uv_irradiance_index":"Very_High","min_gts_temp":"-77","max_gts_temp":"18"},{"id":"21","terrestrial_date":"2012-12-04","sol":"117","ls":"218","season":"Month 8","min_temp":"-66","max_temp":"-5","pressure":"861","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:22","sunset":"17:32","local_uv_irradiance_index":"Very_High","min_gts_temp":"-76","max_gts_temp":"18"},{"id":"20","terrestrial_date":"2012-12-03","sol":"116","ls":"217","season":"Month 8","min_temp":"-67","max_temp":"-6","pressure":"859","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:21","sunset":"17:32","local_uv_irradiance_index":"Very_High","min_gts_temp":"-78","max_gts_temp":"18"},{"id":"19","terrestrial_date":"2012-12-02","sol":"115","ls":"217","season":"Month 8","min_temp":"-66","max_temp":"-8","pressure":"857","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:21","sunset":"17:31","local_uv_irradiance_index":"Very_High","min_gts_temp":"-78","max_gts_temp":"17"},{"id":"18","terrestrial_date":"2012-12-01","sol":"114","ls":"216","season":"Month 8","min_temp":"-69","max_temp":"-6","pressure":"857","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:21","sunset":"17:31","local_uv_irradiance_index":"Very_High","min_gts_temp":"-78","max_gts_temp":"18"},{"id":"17","terrestrial_date":"2012-11-30","sol":"113","ls":"216","season":"Month 8","min_temp":"-66","max_temp":"-6","pressure":"857","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:21","sunset":"17:31","local_uv_irradiance_index":"Very_High","min_gts_temp":"-77","max_gts_temp":"16"},{"id":"16","terrestrial_date":"2012-11-29","sol":"112","ls":"215","season":"Month 8","min_temp":"-65","max_temp":"-8","pressure":"852","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:21","sunset":"17:30","local_uv_irradiance_index":"Very_High","min_gts_temp":"-79","max_gts_temp":"18"},{"id":"15","terrestrial_date":"2012-11-28","sol":"111","ls":"214","season":"Month 8","min_temp":"-66","max_temp":"-4","pressure":"849","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:20","sunset":"17:30","local_uv_irradiance_index":"Very_High","min_gts_temp":"-77","max_gts_temp":"18"},{"id":"14","terrestrial_date":"2012-11-27","sol":"110","ls":"214","season":"Month 8","min_temp":"-65","max_temp":"-5","pressure":"848","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:20","sunset":"17:30","local_uv_irradiance_index":"High","min_gts_temp":"-78","max_gts_temp":"17"},{"id":"12","terrestrial_date":"2012-11-26","sol":"109","ls":"213","season":"Month 8","min_temp":"-64","max_temp":"-3","pressure":"844","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:20","sunset":"17:29","local_uv_irradiance_index":"High","min_gts_temp":"-78","max_gts_temp":"16"},{"id":"11","terrestrial_date":"2012-11-25","sol":"108","ls":"212","season":"Month 8","min_temp":"-65","max_temp":"-4","pressure":"845","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:20","sunset":"17:29","local_uv_irradiance_index":"High","min_gts_temp":"-78","max_gts_temp":"17"},{"id":"10","terrestrial_date":"2012-11-24","sol":"107","ls":"212","season":"Month 8","min_temp":"-66","max_temp":"-5","pressure":"844","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:20","sunset":"17:29","local_uv_irradiance_index":"High","min_gts_temp":"-80","max_gts_temp":"17"},{"id":"9","terrestrial_date":"2012-11-23","sol":"106","ls":"211","season":"Month 8","min_temp":"-66","max_temp":"-8","pressure":"841","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:19","sunset":"17:28","local_uv_irradiance_index":"High","min_gts_temp":"-78","max_gts_temp":"17"},{"id":"8","terrestrial_date":"2012-11-21","sol":"105","ls":"211","season":"Month 8","min_temp":"-66","max_temp":"-6","pressure":"839","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:19","sunset":"17:28","local_uv_irradiance_index":"High","min_gts_temp":"-77","max_gts_temp":"17"},{"id":"7","terrestrial_date":"2012-11-20","sol":"104","ls":"210","season":"Month 8","min_temp":"-67","max_temp":"-5","pressure":"838","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:19","sunset":"17:28","local_uv_irradiance_index":"Very_High","min_gts_temp":"-78","max_gts_temp":"18"},{"id":"6","terrestrial_date":"2012-11-19","sol":"103","ls":"209","season":"Month 7","min_temp":"-66","max_temp":"-3","pressure":"836","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:19","sunset":"17:27","local_uv_irradiance_index":"Very_High","min_gts_temp":"-80","max_gts_temp":"19"},{"id":"5","terrestrial_date":"2012-11-18","sol":"102","ls":"209","season":"Month 7","min_temp":"-67","max_temp":"-3","pressure":"833","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:19","sunset":"17:27","local_uv_irradiance_index":"Very_High","min_gts_temp":"-75","max_gts_temp":"17"},{"id":"4","terrestrial_date":"2012-11-17","sol":"101","ls":"208","season":"Month 7","min_temp":"-65","max_temp":"-2","pressure":"830","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:19","sunset":"17:27","local_uv_irradiance_index":"Very_High","min_gts_temp":"-77","max_gts_temp":"16"},{"id":"3","terrestrial_date":"2012-11-16","sol":"100","ls":"207","season":"Month 7","min_temp":"-66","max_temp":"-1","pressure":"829","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:19","sunset":"17:26","local_uv_irradiance_index":"Very_High","min_gts_temp":"-77","max_gts_temp":"13"},{"id":"242","terrestrial_date":"2012-11-15","sol":"99","ls":"207","season":"Month 7","min_temp":"-68","max_temp":"-1","pressure":"829","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:19","sunset":"17:26","local_uv_irradiance_index":"Very_High","min_gts_temp":"-76","max_gts_temp":"14"},{"id":"241","terrestrial_date":"2012-11-14","sol":"98","ls":"206","season":"Month 7","min_temp":"-68","max_temp":"-1","pressure":"828","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:26","local_uv_irradiance_index":"Very_High","min_gts_temp":"-79","max_gts_temp":"15"},{"id":"240","terrestrial_date":"2012-11-13","sol":"97","ls":"206","season":"Month 7","min_temp":"-68","max_temp":"-3","pressure":"828","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:26","local_uv_irradiance_index":"Very_High","min_gts_temp":"-79","max_gts_temp":"15"},{"id":"239","terrestrial_date":"2012-11-12","sol":"96","ls":"205","season":"Month 7","min_temp":"-71","max_temp":"2","pressure":"826","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:25","local_uv_irradiance_index":"Very_High","min_gts_temp":"-79","max_gts_temp":"15"},{"id":"238","terrestrial_date":"2012-11-11","sol":"95","ls":"204","season":"Month 7","min_temp":"-68","max_temp":"0","pressure":"822","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:25","local_uv_irradiance_index":"Very_High","min_gts_temp":"-79","max_gts_temp":"15"},{"id":"237","terrestrial_date":"2012-11-10","sol":"94","ls":"204","season":"Month 7","min_temp":"-70","max_temp":"0","pressure":"822","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:25","local_uv_irradiance_index":"Very_High","min_gts_temp":"-81","max_gts_temp":"16"},{"id":"236","terrestrial_date":"2012-11-09","sol":"93","ls":"203","season":"Month 7","min_temp":"-72","max_temp":"-1","pressure":"819","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:25","local_uv_irradiance_index":"Very_High","min_gts_temp":"-81","max_gts_temp":"16"},{"id":"235","terrestrial_date":"2012-11-08","sol":"92","ls":"202","season":"Month 7","min_temp":"-74","max_temp":"-1","pressure":"820","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:24","local_uv_irradiance_index":"Very_High","min_gts_temp":"-82","max_gts_temp":"15"},{"id":"234","terrestrial_date":"2012-11-07","sol":"91","ls":"202","season":"Month 7","min_temp":"-74","max_temp":"-1","pressure":"817","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:24","local_uv_irradiance_index":"Very_High","min_gts_temp":"-81","max_gts_temp":"16"},{"id":"233","terrestrial_date":"2012-11-06","sol":"90","ls":"201","season":"Month 7","min_temp":"-71","max_temp":"0","pressure":"813","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:24","local_uv_irradiance_index":"Very_High","min_gts_temp":"-80","max_gts_temp":"16"},{"id":"231","terrestrial_date":"2012-11-05","sol":"89","ls":"201","season":"Month 7","min_temp":"-73","max_temp":"-1","pressure":"813","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:24","local_uv_irradiance_index":"Very_High","min_gts_temp":"-82","max_gts_temp":"16"},{"id":"230","terrestrial_date":"2012-11-04","sol":"88","ls":"200","season":"Month 7","min_temp":"-70","max_temp":"-2","pressure":"811","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:24","local_uv_irradiance_index":"Very_High","min_gts_temp":"-82","max_gts_temp":"15"},{"id":"229","terrestrial_date":"2012-11-03","sol":"87","ls":"199","season":"Month 7","min_temp":"-70","max_temp":"-2","pressure":"808","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:23","local_uv_irradiance_index":"Very_High","min_gts_temp":"-81","max_gts_temp":"15"},{"id":"228","terrestrial_date":"2012-11-02","sol":"86","ls":"199","season":"Month 7","min_temp":"-71","max_temp":"-4","pressure":"808","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:23","local_uv_irradiance_index":"Very_High","min_gts_temp":"-81","max_gts_temp":"15"},{"id":"227","terrestrial_date":"2012-11-01","sol":"85","ls":"198","season":"Month 7","min_temp":"-71","max_temp":"-1","pressure":"805","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:23","local_uv_irradiance_index":"Very_High","min_gts_temp":"-81","max_gts_temp":"15"},{"id":"226","terrestrial_date":"2012-10-31","sol":"84","ls":"198","season":"Month 7","min_temp":"-70","max_temp":"0","pressure":"801","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:23","local_uv_irradiance_index":"Very_High","min_gts_temp":"-82","max_gts_temp":"15"},{"id":"225","terrestrial_date":"2012-10-30","sol":"83","ls":"197","season":"Month 7","min_temp":"-72","max_temp":"0","pressure":"801","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:23","local_uv_irradiance_index":"Very_High","min_gts_temp":"-83","max_gts_temp":"15"},{"id":"224","terrestrial_date":"2012-10-29","sol":"82","ls":"196","season":"Month 7","min_temp":"-72","max_temp":"0","pressure":"799","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:22","local_uv_irradiance_index":"Very_High","min_gts_temp":"-83","max_gts_temp":"14"},{"id":"223","terrestrial_date":"2012-10-28","sol":"81","ls":"196","season":"Month 7","min_temp":"-72","max_temp":"-2","pressure":"798","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:22","local_uv_irradiance_index":"Very_High","min_gts_temp":"-82","max_gts_temp":"13"},{"id":"222","terrestrial_date":"2012-10-27","sol":"80","ls":"195","season":"Month 7","min_temp":"-70","max_temp":"-3","pressure":"796","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:22","local_uv_irradiance_index":"Very_High","min_gts_temp":"-82","max_gts_temp":"14"},{"id":"221","terrestrial_date":"2012-10-26","sol":"79","ls":"195","season":"Month 7","min_temp":"-73","max_temp":"-1","pressure":"795","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:22","local_uv_irradiance_index":"Very_High","min_gts_temp":"-84","max_gts_temp":"14"},{"id":"220","terrestrial_date":"2012-10-25","sol":"78","ls":"194","season":"Month 7","min_temp":"-71","max_temp":"0","pressure":"793","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:22","local_uv_irradiance_index":"Very_High","min_gts_temp":"-82","max_gts_temp":"14"},{"id":"219","terrestrial_date":"2012-10-24","sol":"77","ls":"193","season":"Month 7","min_temp":"-71","max_temp":"0","pressure":"792","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:22","local_uv_irradiance_index":"Very_High","min_gts_temp":"-83","max_gts_temp":"15"},{"id":"218","terrestrial_date":"2012-10-23","sol":"76","ls":"193","season":"Month 7","min_temp":"-73","max_temp":"-1","pressure":"792","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:22","local_uv_irradiance_index":"Very_High","min_gts_temp":"-83","max_gts_temp":"15"},{"id":"217","terrestrial_date":"2012-10-22","sol":"75","ls":"192","season":"Month 7","min_temp":"-73","max_temp":"-1","pressure":"791","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:21","local_uv_irradiance_index":"Very_High","min_gts_temp":"-84","max_gts_temp":"13"},{"id":"216","terrestrial_date":"2012-10-21","sol":"74","ls":"192","season":"Month 7","min_temp":"-72","max_temp":"-5","pressure":"790","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:21","local_uv_irradiance_index":"Very_High","min_gts_temp":"-83","max_gts_temp":"13"},{"id":"215","terrestrial_date":"2012-10-20","sol":"73","ls":"191","season":"Month 7","min_temp":"-70","max_temp":"0","pressure":"788","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:21","local_uv_irradiance_index":"Very_High","min_gts_temp":"-83","max_gts_temp":"13"},{"id":"214","terrestrial_date":"2012-10-19","sol":"72","ls":"190","season":"Month 7","min_temp":"-73","max_temp":"-2","pressure":"785","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:21","local_uv_irradiance_index":"Very_High","min_gts_temp":"-82","max_gts_temp":"12"},{"id":"213","terrestrial_date":"2012-10-18","sol":"71","ls":"190","season":"Month 7","min_temp":"-71","max_temp":"-2","pressure":"784","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:21","local_uv_irradiance_index":"Very_High","min_gts_temp":"-81","max_gts_temp":"12"},{"id":"212","terrestrial_date":"2012-10-17","sol":"70","ls":"189","season":"Month 7","min_temp":"-72","max_temp":"-1","pressure":"783","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:21","local_uv_irradiance_index":"Very_High","min_gts_temp":"-82","max_gts_temp":"13"},{"id":"211","terrestrial_date":"2012-10-15","sol":"69","ls":"189","season":"Month 7","min_temp":"-73","max_temp":"0","pressure":"778","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:21","local_uv_irradiance_index":"High","min_gts_temp":"-82","max_gts_temp":"12"},{"id":"210","terrestrial_date":"2012-10-14","sol":"68","ls":"188","season":"Month 7","min_temp":"-71","max_temp":"-2","pressure":"781","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:21","local_uv_irradiance_index":"Very_High","min_gts_temp":"-80","max_gts_temp":"12"},{"id":"209","terrestrial_date":"2012-10-13","sol":"67","ls":"187","season":"Month 7","min_temp":"-73","max_temp":"-6","pressure":"780","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:21","local_uv_irradiance_index":"Very_High","min_gts_temp":"-81","max_gts_temp":"13"},{"id":"208","terrestrial_date":"2012-10-12","sol":"66","ls":"187","season":"Month 7","min_temp":"-73","max_temp":"-2","pressure":"778","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:18","sunset":"17:21","local_uv_irradiance_index":"Very_High","min_gts_temp":"-83","max_gts_temp":"12"},{"id":"207","terrestrial_date":"2012-10-11","sol":"65","ls":"186","season":"Month 7","min_temp":"-72","max_temp":"-2","pressure":"777","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:19","sunset":"17:20","local_uv_irradiance_index":"Very_High","min_gts_temp":"-82","max_gts_temp":"12"},{"id":"206","terrestrial_date":"2012-10-10","sol":"64","ls":"186","season":"Month 7","min_temp":"-74","max_temp":"0","pressure":"776","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:19","sunset":"17:20","local_uv_irradiance_index":"Very_High","min_gts_temp":"-83","max_gts_temp":"12"},{"id":"205","terrestrial_date":"2012-10-09","sol":"63","ls":"185","season":"Month 7","min_temp":"-73","max_temp":"0","pressure":"775","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:19","sunset":"17:20","local_uv_irradiance_index":"Very_High","min_gts_temp":"-81","max_gts_temp":"12"},{"id":"204","terrestrial_date":"2012-10-08","sol":"62","ls":"184","season":"Month 7","min_temp":"-72","max_temp":"-1","pressure":"774","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:19","sunset":"17:20","local_uv_irradiance_index":"Very_High","min_gts_temp":"-82","max_gts_temp":"11"},{"id":"203","terrestrial_date":"2012-10-07","sol":"61","ls":"184","season":"Month 7","min_temp":"-72","max_temp":"-2","pressure":"772","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:19","sunset":"17:20","local_uv_irradiance_index":"Very_High","min_gts_temp":"-83","max_gts_temp":"13"},{"id":"202","terrestrial_date":"2012-10-06","sol":"60","ls":"183","season":"Month 7","min_temp":"-75","max_temp":"-2","pressure":"772","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:19","sunset":"17:20","local_uv_irradiance_index":"Very_High","min_gts_temp":"-84","max_gts_temp":"12"},{"id":"201","terrestrial_date":"2012-10-05","sol":"59","ls":"183","season":"Month 7","min_temp":"-76","max_temp":"-1","pressure":"771","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:19","sunset":"17:20","local_uv_irradiance_index":"Very_High","min_gts_temp":"-82","max_gts_temp":"11"},{"id":"200","terrestrial_date":"2012-10-04","sol":"58","ls":"182","season":"Month 7","min_temp":"-74","max_temp":"-3","pressure":"769","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:19","sunset":"17:20","local_uv_irradiance_index":"Very_High","min_gts_temp":"-82","max_gts_temp":"11"},{"id":"199","terrestrial_date":"2012-10-03","sol":"57","ls":"181","season":"Month 7","min_temp":"-73","max_temp":"-3","pressure":"769","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:19","sunset":"17:20","local_uv_irradiance_index":"Very_High","min_gts_temp":"-82","max_gts_temp":"12"},{"id":"198","terrestrial_date":"2012-10-02","sol":"56","ls":"181","season":"Month 7","min_temp":"-73","max_temp":"-4","pressure":"768","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:20","sunset":"17:20","local_uv_irradiance_index":"Very_High","min_gts_temp":"-80","max_gts_temp":"10"},{"id":"197","terrestrial_date":"2012-10-01","sol":"55","ls":"180","season":"Month 7","min_temp":"-74","max_temp":"-2","pressure":"766","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:20","sunset":"17:20","local_uv_irradiance_index":"Very_High","min_gts_temp":"-78","max_gts_temp":"11"},{"id":"196","terrestrial_date":"2012-09-30","sol":"54","ls":"180","season":"Month 7","min_temp":"-72","max_temp":"-9","pressure":"766","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:20","sunset":"17:20","local_uv_irradiance_index":"Very_High","min_gts_temp":"-77","max_gts_temp":"10"},{"id":"195","terrestrial_date":"2012-09-29","sol":"53","ls":"179","season":"Month 6","min_temp":"-71","max_temp":"-5","pressure":"764","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:20","sunset":"17:20","local_uv_irradiance_index":"Very_High","min_gts_temp":"-81","max_gts_temp":"11"},{"id":"194","terrestrial_date":"2012-09-28","sol":"52","ls":"179","season":"Month 6","min_temp":"-74","max_temp":"-7","pressure":"762","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:20","sunset":"17:20","local_uv_irradiance_index":"Very_High","min_gts_temp":"-84","max_gts_temp":"12"},{"id":"193","terrestrial_date":"2012-09-27","sol":"51","ls":"178","season":"Month 6","min_temp":"-76","max_temp":"-7","pressure":"762","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:20","sunset":"17:20","local_uv_irradiance_index":"Very_High","min_gts_temp":"-83","max_gts_temp":"13"},{"id":"192","terrestrial_date":"2012-09-26","sol":"50","ls":"177","season":"Month 6","min_temp":"-72","max_temp":"-10","pressure":"761","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:20","sunset":"17:20","local_uv_irradiance_index":"Very_High","min_gts_temp":"-76","max_gts_temp":"11"},{"id":"191","terrestrial_date":"2012-09-25","sol":"49","ls":"177","season":"Month 6","min_temp":"-74","max_temp":"-10","pressure":"761","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:21","sunset":"17:20","local_uv_irradiance_index":"Very_High","min_gts_temp":"-81","max_gts_temp":"10"},{"id":"190","terrestrial_date":"2012-09-24","sol":"48","ls":"176","season":"Month 6","min_temp":"-75","max_temp":"0","pressure":"759","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:21","sunset":"17:20","local_uv_irradiance_index":"Very_High","min_gts_temp":"-84","max_gts_temp":"12"},{"id":"189","terrestrial_date":"2012-09-23","sol":"47","ls":"176","season":"Month 6","min_temp":"-75","max_temp":"-9","pressure":"758","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:21","sunset":"17:20","local_uv_irradiance_index":"Very_High","min_gts_temp":"-83","max_gts_temp":"14"},{"id":"188","terrestrial_date":"2012-09-22","sol":"46","ls":"175","season":"Month 6","min_temp":"-74","max_temp":"-12","pressure":"758","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:21","sunset":"17:20","local_uv_irradiance_index":"Very_High","min_gts_temp":"-83","max_gts_temp":"13"},{"id":"187","terrestrial_date":"2012-09-21","sol":"45","ls":"175","season":"Month 6","min_temp":"-74","max_temp":"-9","pressure":"758","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:21","sunset":"17:20","local_uv_irradiance_index":"Very_High","min_gts_temp":"-87","max_gts_temp":"11"},{"id":"186","terrestrial_date":"2012-09-20","sol":"44","ls":"174","season":"Month 6","min_temp":"-75","max_temp":"-10","pressure":"757","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:21","sunset":"17:20","local_uv_irradiance_index":"Very_High","min_gts_temp":"-86","max_gts_temp":"13"},{"id":"185","terrestrial_date":"2012-09-19","sol":"43","ls":"173","season":"Month 6","min_temp":"-74","max_temp":"-12","pressure":"756","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:22","sunset":"17:20","local_uv_irradiance_index":"Very_High","min_gts_temp":"-80","max_gts_temp":"9"},{"id":"184","terrestrial_date":"2012-09-18","sol":"42","ls":"173","season":"Month 6","min_temp":"-75","max_temp":"-7","pressure":"754","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:22","sunset":"17:20","local_uv_irradiance_index":"Very_High","min_gts_temp":"-80","max_gts_temp":"7"},{"id":"183","terrestrial_date":"2012-09-17","sol":"41","ls":"172","season":"Month 6","min_temp":"-75","max_temp":"-12","pressure":"753","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:22","sunset":"17:20","local_uv_irradiance_index":"Very_High","min_gts_temp":"-79","max_gts_temp":"8"},{"id":"182","terrestrial_date":"2012-09-16","sol":"40","ls":"172","season":"Month 6","min_temp":"-75","max_temp":"-12","pressure":"753","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:22","sunset":"17:20","local_uv_irradiance_index":"Very_High","min_gts_temp":"-79","max_gts_temp":"8"},{"id":"181","terrestrial_date":"2012-09-15","sol":"39","ls":"171","season":"Month 6","min_temp":"-75","max_temp":"-8","pressure":"751","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:22","sunset":"17:20","local_uv_irradiance_index":"Very_High","min_gts_temp":"-79","max_gts_temp":"7"},{"id":"180","terrestrial_date":"2012-09-14","sol":"38","ls":"171","season":"Month 6","min_temp":"-73","max_temp":"-13","pressure":"750","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:22","sunset":"17:20","local_uv_irradiance_index":"Very_High","min_gts_temp":"-78","max_gts_temp":"8"},{"id":"179","terrestrial_date":"2012-09-13","sol":"37","ls":"170","season":"Month 6","min_temp":"-73","max_temp":"0","pressure":"750","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:23","sunset":"17:20","local_uv_irradiance_index":"Very_High","min_gts_temp":"-78","max_gts_temp":"7"},{"id":"178","terrestrial_date":"2012-09-12","sol":"36","ls":"169","season":"Month 6","min_temp":"-73","max_temp":"-1","pressure":"750","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:23","sunset":"17:20","local_uv_irradiance_index":"Very_High","min_gts_temp":"-78","max_gts_temp":"7"},{"id":"177","terrestrial_date":"2012-09-11","sol":"35","ls":"169","season":"Month 6","min_temp":"-73","max_temp":"-1","pressure":"749","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:23","sunset":"17:20","local_uv_irradiance_index":"Very_High","min_gts_temp":"-78","max_gts_temp":"6"},{"id":"176","terrestrial_date":"2012-09-10","sol":"34","ls":"168","season":"Month 6","min_temp":"-73","max_temp":"1","pressure":"748","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:23","sunset":"17:20","local_uv_irradiance_index":"Very_High","min_gts_temp":"-78","max_gts_temp":"6"},{"id":"175","terrestrial_date":"2012-09-08","sol":"33","ls":"168","season":"Month 6","min_temp":"-73","max_temp":"-2","pressure":"748","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:23","sunset":"17:20","local_uv_irradiance_index":"Very_High","min_gts_temp":"-80","max_gts_temp":"5"},{"id":"174","terrestrial_date":"2012-09-07","sol":"32","ls":"167","season":"Month 6","min_temp":"--","max_temp":"--","pressure":"--","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:24","sunset":"17:20","local_uv_irradiance_index":"--","min_gts_temp":"--","max_gts_temp":"--"},{"id":"173","terrestrial_date":"2012-09-06","sol":"31","ls":"167","season":"Month 6","min_temp":"-74","max_temp":"-23","pressure":"745","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:24","sunset":"17:20","local_uv_irradiance_index":"Very_High","min_gts_temp":"-80","max_gts_temp":"2"},{"id":"172","terrestrial_date":"2012-09-05","sol":"30","ls":"166","season":"Month 6","min_temp":"-74","max_temp":"-3","pressure":"747","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:24","sunset":"17:20","local_uv_irradiance_index":"Very_High","min_gts_temp":"-80","max_gts_temp":"6"},{"id":"171","terrestrial_date":"2012-09-04","sol":"29","ls":"166","season":"Month 6","min_temp":"-75","max_temp":"-2","pressure":"747","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:24","sunset":"17:20","local_uv_irradiance_index":"Very_High","min_gts_temp":"-78","max_gts_temp":"7"},{"id":"170","terrestrial_date":"2012-09-03","sol":"28","ls":"165","season":"Month 6","min_temp":"-75","max_temp":"-15","pressure":"745","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:24","sunset":"17:20","local_uv_irradiance_index":"Very_High","min_gts_temp":"-79","max_gts_temp":"5"},{"id":"163","terrestrial_date":"2012-09-02","sol":"27","ls":"164","season":"Month 6","min_temp":"-75","max_temp":"-15","pressure":"743","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:25","sunset":"17:20","local_uv_irradiance_index":"Very_High","min_gts_temp":"-79","max_gts_temp":"5"},{"id":"156","terrestrial_date":"2012-09-01","sol":"26","ls":"164","season":"Month 6","min_temp":"-76","max_temp":"-14","pressure":"745","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:25","sunset":"17:20","local_uv_irradiance_index":"Very_High","min_gts_temp":"-83","max_gts_temp":"6"},{"id":"145","terrestrial_date":"2012-08-31","sol":"25","ls":"163","season":"Month 6","min_temp":"-75","max_temp":"-11","pressure":"743","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:25","sunset":"17:20","local_uv_irradiance_index":"Very_High","min_gts_temp":"-79","max_gts_temp":"6"},{"id":"134","terrestrial_date":"2012-08-30","sol":"24","ls":"163","season":"Month 6","min_temp":"-75","max_temp":"-7","pressure":"742","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:25","sunset":"17:21","local_uv_irradiance_index":"Very_High","min_gts_temp":"-83","max_gts_temp":"6"},{"id":"123","terrestrial_date":"2012-08-29","sol":"23","ls":"162","season":"Month 6","min_temp":"-75","max_temp":"-16","pressure":"741","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:25","sunset":"17:21","local_uv_irradiance_index":"Very_High","min_gts_temp":"-82","max_gts_temp":"9"},{"id":"114","terrestrial_date":"2012-08-28","sol":"22","ls":"162","season":"Month 6","min_temp":"-74","max_temp":"-6","pressure":"742","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:26","sunset":"17:21","local_uv_irradiance_index":"Very_High","min_gts_temp":"-81","max_gts_temp":"8"},{"id":"112","terrestrial_date":"2012-08-27","sol":"21","ls":"161","season":"Month 6","min_temp":"-74","max_temp":"-3","pressure":"741","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:26","sunset":"17:21","local_uv_irradiance_index":"Very_High","min_gts_temp":"-81","max_gts_temp":"7"},{"id":"110","terrestrial_date":"2012-08-26","sol":"20","ls":"161","season":"Month 6","min_temp":"--","max_temp":"--","pressure":"--","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:26","sunset":"17:21","local_uv_irradiance_index":"--","min_gts_temp":"--","max_gts_temp":"--"},{"id":"101","terrestrial_date":"2012-08-25","sol":"19","ls":"160","season":"Month 6","min_temp":"--","max_temp":"--","pressure":"--","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:26","sunset":"17:21","local_uv_irradiance_index":"--","min_gts_temp":"--","max_gts_temp":"--"},{"id":"90","terrestrial_date":"2012-08-24","sol":"18","ls":"160","season":"Month 6","min_temp":"--","max_temp":"--","pressure":"--","pressure_string":"Higher","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:26","sunset":"17:21","local_uv_irradiance_index":"--","min_gts_temp":"--","max_gts_temp":"--"},{"id":"79","terrestrial_date":"2012-08-23","sol":"17","ls":"159","season":"Month 6","min_temp":"-76","max_temp":"-4","pressure":"742","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:27","sunset":"17:21","local_uv_irradiance_index":"Very_High","min_gts_temp":"-81","max_gts_temp":"7"},{"id":"68","terrestrial_date":"2012-08-22","sol":"16","ls":"158","season":"Month 6","min_temp":"-77","max_temp":"0","pressure":"740","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:27","sunset":"17:21","local_uv_irradiance_index":"Very_High","min_gts_temp":"-81","max_gts_temp":"9"},{"id":"57","terrestrial_date":"2012-08-21","sol":"15","ls":"158","season":"Month 6","min_temp":"-78","max_temp":"-15","pressure":"740","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:27","sunset":"17:21","local_uv_irradiance_index":"Very_High","min_gts_temp":"-82","max_gts_temp":"8"},{"id":"46","terrestrial_date":"2012-08-20","sol":"14","ls":"157","season":"Month 6","min_temp":"-74","max_temp":"-16","pressure":"740","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:27","sunset":"17:21","local_uv_irradiance_index":"Very_High","min_gts_temp":"-82","max_gts_temp":"9"},{"id":"35","terrestrial_date":"2012-08-19","sol":"13","ls":"157","season":"Month 6","min_temp":"-74","max_temp":"-15","pressure":"732","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:28","sunset":"17:21","local_uv_irradiance_index":"Very_High","min_gts_temp":"-80","max_gts_temp":"8"},{"id":"24","terrestrial_date":"2012-08-18","sol":"12","ls":"156","season":"Month 6","min_temp":"-76","max_temp":"-18","pressure":"741","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:28","sunset":"17:21","local_uv_irradiance_index":"Very_High","min_gts_temp":"-82","max_gts_temp":"8"},{"id":"13","terrestrial_date":"2012-08-17","sol":"11","ls":"156","season":"Month 6","min_temp":"-76","max_temp":"-11","pressure":"740","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:28","sunset":"17:21","local_uv_irradiance_index":"Very_High","min_gts_temp":"-83","max_gts_temp":"9"},{"id":"2","terrestrial_date":"2012-08-16","sol":"10","ls":"155","season":"Month 6","min_temp":"-75","max_temp":"-16","pressure":"739","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:28","sunset":"17:22","local_uv_irradiance_index":"Very_High","min_gts_temp":"-83","max_gts_temp":"8"},{"id":"232","terrestrial_date":"2012-08-15","sol":"9","ls":"155","season":"Month 6","min_temp":"--","max_temp":"--","pressure":"--","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:28","sunset":"17:22","local_uv_irradiance_index":"--","min_gts_temp":"--","max_gts_temp":"--"},{"id":"1","terrestrial_date":"2012-08-07","sol":"1","ls":"150","season":"Month 6","min_temp":"--","max_temp":"--","pressure":"--","pressure_string":"Lower","abs_humidity":"--","wind_speed":"--","wind_direction":"--","atmo_opacity":"Sunny","sunrise":"05:30","sunset":"17:22","local_uv_irradiance_index":"--","min_gts_temp":"--","max_gts_temp":"--"}]} \ No newline at end of file
diff --git a/example/msl/src/MSLDataDictionary.js b/example/msl/src/MSLDataDictionary.js
deleted file mode 100644
index d5df9e657..000000000
--- a/example/msl/src/MSLDataDictionary.js
+++ /dev/null
@@ -1,78 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- /**
- * A data dictionary describes the telemetry available from a data
- * source and its data types. The data dictionary will be parsed by a custom
- * server provider for this data source (in this case
- * {@link RemsTelemetryServerAdapter}).
- *
- * Typically a data dictionary would be made available alongside the
- * telemetry data source itself.
- */
- function () {
- return {
- "name": "Mars Science Laboratory",
- "identifier": "msl",
- "instruments": [
- {
- "name": "rems",
- "identifier": "rems",
- "measurements": [
- {
- "name": "Min. Air Temperature",
- "identifier": "min_temp",
- "units": "Degrees (C)",
- "type": "float"
- },
- {
- "name": "Max. Air Temperature",
- "identifier": "max_temp",
- "units": "Degrees (C)",
- "type": "float"
- },
- {
- "name": "Atmospheric Pressure",
- "identifier": "pressure",
- "units": "Millibars",
- "type": "float"
- },
- {
- "name": "Min. Ground Temperature",
- "identifier": "min_gts_temp",
- "units": "Degrees (C)",
- "type": "float"
- },
- {
- "name": "Max. Ground Temperature",
- "identifier": "max_gts_temp",
- "units": "Degrees (C)",
- "type": "float"
- }
- ]
- }
- ]
- };
- }
-);
diff --git a/example/msl/src/RemsTelemetryModelProvider.js b/example/msl/src/RemsTelemetryModelProvider.js
deleted file mode 100644
index 1542d5c84..000000000
--- a/example/msl/src/RemsTelemetryModelProvider.js
+++ /dev/null
@@ -1,96 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- function () {
- "use strict";
-
- var PREFIX = "msl_tlm:",
- FORMAT_MAPPINGS = {
- float: "number",
- integer: "number",
- string: "string"
- };
-
- function RemsTelemetryModelProvider(adapter) {
-
- function isRelevant(id) {
- return id.indexOf(PREFIX) === 0;
- }
-
- function makeId(element) {
- return PREFIX + element.identifier;
- }
-
- function buildTaxonomy(dictionary) {
- var models = {};
-
- function addMeasurement(measurement, parent) {
- var format = FORMAT_MAPPINGS[measurement.type];
- models[makeId(measurement)] = {
- type: "msl.measurement",
- name: measurement.name,
- location: parent,
- telemetry: {
- key: measurement.identifier,
- ranges: [{
- key: "value",
- name: measurement.units,
- units: measurement.units,
- format: format
- }]
- }
- };
- }
-
- function addInstrument(subsystem, spacecraftId) {
- var measurements = (subsystem.measurements || []),
- instrumentId = makeId(subsystem);
-
- models[instrumentId] = {
- type: "msl.instrument",
- name: subsystem.name,
- location: spacecraftId,
- composition: measurements.map(makeId)
- };
- measurements.forEach(function (measurement) {
- addMeasurement(measurement, instrumentId);
- });
- }
-
- (dictionary.instruments || []).forEach(function (instrument) {
- addInstrument(instrument, "msl:curiosity");
- });
-
- return models;
- }
-
- return {
- getModels: function (ids) {
- return ids.some(isRelevant) ? buildTaxonomy(adapter.dictionary) : {};
- }
- };
- }
-
- return RemsTelemetryModelProvider;
- }
-);
diff --git a/example/msl/src/RemsTelemetryProvider.js b/example/msl/src/RemsTelemetryProvider.js
deleted file mode 100644
index b4a64111a..000000000
--- a/example/msl/src/RemsTelemetryProvider.js
+++ /dev/null
@@ -1,83 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-define (
- ['./RemsTelemetrySeries'],
- function (RemsTelemetrySeries) {
- "use strict";
-
- var SOURCE = "rems.source";
-
- function RemsTelemetryProvider(adapter, $q) {
- this.adapter = adapter;
- this.$q = $q;
- }
-
- /**
- * Retrieve telemetry from this telemetry source.
- * @memberOf example/msl
- * @param {Array<TelemetryRequest>} requests An array of all request
- * objects (which needs to be filtered to only those relevant to this
- * source)
- * @returns {Promise} A {@link Promise} resolved with a {@link RemsTelemetrySeries}
- * object that wraps the telemetry returned from the telemetry source.
- */
- RemsTelemetryProvider.prototype.requestTelemetry = function (requests) {
- var packaged = {},
- relevantReqs,
- adapter = this.adapter;
-
- function matchesSource(request) {
- return (request.source === SOURCE);
- }
-
- function addToPackage(history) {
- packaged[SOURCE][history.id] =
- new RemsTelemetrySeries(history.values);
- }
-
- function handleRequest(request) {
- return adapter.history(request).then(addToPackage);
- }
-
- relevantReqs = requests.filter(matchesSource);
- packaged[SOURCE] = {};
-
- return this.$q.all(relevantReqs.map(handleRequest))
- .then(function () {
- return packaged;
- });
- };
-
- /**
- * This data source does not support real-time subscriptions
- */
- RemsTelemetryProvider.prototype.subscribe = function (callback, requests) {
- return function () {};
- };
-
- RemsTelemetryProvider.prototype.unsubscribe = function (callback, requests) {
- return function () {};
- };
-
- return RemsTelemetryProvider;
- }
-);
diff --git a/example/msl/src/RemsTelemetrySeries.js b/example/msl/src/RemsTelemetrySeries.js
deleted file mode 100644
index 6c01e56d1..000000000
--- a/example/msl/src/RemsTelemetrySeries.js
+++ /dev/null
@@ -1,84 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-define(
- function () {
- "use strict";
-
- /**
- * @typedef {Object} RemsTelemetryValue
- * @memberOf example/msl
- * @property {number} date The date/time of the telemetry value. Constitutes the domain value of this value pair
- * @property {number} value The value of this telemetry datum.
- * A floating point value representing some observable quantity (eg.
- * temperature, air pressure, etc.)
- */
-
- /**
- * A representation of a collection of telemetry data. The REMS
- * telemetry data is time ordered, with the 'domain' value
- * constituting the time stamp of each data value and the
- * 'range' being the value itself.
- *
- * TelemetrySeries will typically wrap an array of telemetry data,
- * and provide an interface for retrieving individual an telemetry
- * value.
- * @memberOf example/msl
- * @param {Array<RemsTelemetryValue>} data An array of telemetry values
- * @constructor
- */
- function RemsTelemetrySeries(data) {
- this.data = data;
- }
-
- /**
- * @returns {number} A count of the number of data values available in
- * this series
- */
- RemsTelemetrySeries.prototype.getPointCount = function () {
- return this.data.length;
- };
-
- /**
- * The domain value at the given index. The Rems telemetry data is
- * time ordered, so the domain value is the time stamp of each data
- * value.
- * @param index
- * @returns {number} the time value in ms since 1 January 1970
- */
- RemsTelemetrySeries.prototype.getDomainValue = function (index) {
- return this.data[index].date;
- };
-
- /**
- * The range value of the REMS data set is the value of the thing
- * being measured, be it temperature, air pressure, etc.
- * @param index The datum in the data series to return the range
- * value of.
- * @returns {number} A floating point number
- */
- RemsTelemetrySeries.prototype.getRangeValue = function (index) {
- return this.data[index].value;
- };
-
- return RemsTelemetrySeries;
- }
-);
diff --git a/example/msl/src/RemsTelemetryServerAdapter.js b/example/msl/src/RemsTelemetryServerAdapter.js
deleted file mode 100644
index 509661c9d..000000000
--- a/example/msl/src/RemsTelemetryServerAdapter.js
+++ /dev/null
@@ -1,145 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-/*jslint es5: true */
-
-define(
- [
- "./MSLDataDictionary",
- "module"
- ],
- function (MSLDataDictionary, module) {
- "use strict";
-
- var TERRESTRIAL_DATE = "terrestrial_date",
- LOCAL_DATA = "../data/rems.json";
-
- /**
- * Fetches historical data from the REMS instrument on the Curiosity
- * Rover.
- * @memberOf example/msl
- * @param $q
- * @param $http
- * @param REMS_WS_URL The location of the REMS telemetry data.
- * @constructor
- */
- function RemsTelemetryServerAdapter($http, $log, REMS_WS_URL) {
- this.localDataURI = module.uri.substring(0, module.uri.lastIndexOf('/') + 1) + LOCAL_DATA;
- this.REMS_WS_URL = REMS_WS_URL;
- this.$http = $http;
- this.$log = $log;
- this.promise = undefined;
-
- this.dataTransforms = {
- //Convert from pascals to millibars
- 'pressure': function pascalsToMillibars(pascals) {
- return pascals / 100;
- }
- };
- }
-
- /**
- * The data dictionary for this data source.
- * @type {MSLDataDictionary}
- */
- RemsTelemetryServerAdapter.prototype.dictionary = MSLDataDictionary;
-
- /**
- * Fetches historical data from source, and associates it with the
- * given request ID.
- * @private
- */
- RemsTelemetryServerAdapter.prototype.requestHistory = function (request) {
- var self = this,
- id = request.key;
-
- var dataTransforms = this.dataTransforms;
-
- function processResponse(response) {
- var data = [];
- /*
- * History data is organised by Sol. Iterate over sols...
- */
- response.data.soles.forEach(function (solData) {
- /*
- * Check that valid data exists
- */
- if (!isNaN(solData[id])) {
- var dataTransform = dataTransforms[id];
- /*
- * Append each data point to the array of values
- * for this data point property (min. temp, etc).
- */
- data.unshift({
- date: Date.parse(solData[TERRESTRIAL_DATE]),
- value: dataTransform ? dataTransform(solData[id]) : solData[id]
- });
- }
- });
-
- return data;
- }
-
- function fallbackToLocal() {
- self.$log.warn("Loading REMS data failed, probably due to"
- + " cross origin policy. Falling back to local data");
-
- return self.$http.get(self.localDataURI);
- }
-
- //Filter results to match request parameters
- function filterResults(results) {
- return results.filter(function (result) {
- return result.date >= (request.start || Number.MIN_VALUE)
- && result.date <= (request.end || Number.MAX_VALUE);
- });
- }
-
- function packageAndResolve(results) {
- return {
- id: id,
- values: results
- };
- }
-
- return (this.promise = this.promise || this.$http.get(this.REMS_WS_URL))
- .catch(fallbackToLocal)
- .then(processResponse)
- .then(filterResults)
- .then(packageAndResolve);
- };
-
- /**
- * Requests historical telemetry for the named data attribute. In
- * the case of REMS, this data source exposes multiple different
- * data variables from the REMS instrument, including temperature
- * and others
- * @param id The telemetry data point key to be queried.
- * @returns {Promise | Array<RemsTelemetryValue>} that resolves with an Array of {@link RemsTelemetryValue} objects for the request data key.
- */
- RemsTelemetryServerAdapter.prototype.history = function (request) {
- return this.requestHistory(request);
- };
-
- return RemsTelemetryServerAdapter;
- }
-);
-
diff --git a/example/notifications/bundle.js b/example/notifications/bundle.js
deleted file mode 100644
index 941c20fc1..000000000
--- a/example/notifications/bundle.js
+++ /dev/null
@@ -1,90 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
- "./src/DialogLaunchController",
- "./src/NotificationLaunchController",
- "./src/DialogLaunchIndicator",
- "./src/NotificationLaunchIndicator",
- "./res/dialog-launch.html",
- "./res/notification-launch.html"
-], function (
- DialogLaunchController,
- NotificationLaunchController,
- DialogLaunchIndicator,
- NotificationLaunchIndicator,
- DialogLaunch,
- NotificationLaunch
-) {
- "use strict";
-
- return {
- name: "example/notifications",
- definition: {
- "extensions": {
- "templates": [
- {
- "key": "dialogLaunchTemplate",
- "template": DialogLaunch
- },
- {
- "key": "notificationLaunchTemplate",
- "template": NotificationLaunch
- }
- ],
- "controllers": [
- {
- "key": "DialogLaunchController",
- "implementation": DialogLaunchController,
- "depends": [
- "$scope",
- "$timeout",
- "$log",
- "dialogService",
- "notificationService"
- ]
- },
- {
- "key": "NotificationLaunchController",
- "implementation": NotificationLaunchController,
- "depends": [
- "$scope",
- "$timeout",
- "$log",
- "notificationService"
- ]
- }
- ],
- "indicators": [
- {
- "implementation": DialogLaunchIndicator,
- "priority": "fallback"
- },
- {
- "implementation": NotificationLaunchIndicator,
- "priority": "fallback"
- }
- ]
- }
- }
- };
-});
diff --git a/example/notifications/res/dialog-launch.html b/example/notifications/res/dialog-launch.html
deleted file mode 100644
index f6b33d7b7..000000000
--- a/example/notifications/res/dialog-launch.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<span class="h-indicator" ng-controller="DialogLaunchController">
- <!-- DO NOT ADD SPACES BETWEEN THE SPANS - IT ADDS WHITE SPACE!! -->
- <div class="c-indicator c-indicator--clickable icon-box-with-arrow s-status-available"><span class="label c-indicator__label">
- <button ng-click="launchProgress(true)">Known</button>
- <button ng-click="launchProgress(false)">Unknown</button>
- <button ng-click="launchError()">Error</button>
- <button ng-click="launchInfo()">Info</button>
- </span></div>
-</span>
diff --git a/example/notifications/res/notification-launch.html b/example/notifications/res/notification-launch.html
deleted file mode 100644
index 11c66566e..000000000
--- a/example/notifications/res/notification-launch.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<span class="h-indicator" ng-controller="NotificationLaunchController">
- <!-- DO NOT ADD SPACES BETWEEN THE SPANS - IT ADDS WHITE SPACE!! -->
- <div class="c-indicator c-indicator--clickable icon-bell s-status-available"><span class="label c-indicator__label">
- <button ng-click="newInfo()">Success</button>
- <button ng-click="newError()">Error</button>
- <button ng-click="newAlert()">Alert</button>
- <button ng-click="newProgress()">Progress</button>
- </span></div>
-</span>
diff --git a/example/notifications/src/DialogLaunchController.js b/example/notifications/src/DialogLaunchController.js
deleted file mode 100644
index 4880f79aa..000000000
--- a/example/notifications/src/DialogLaunchController.js
+++ /dev/null
@@ -1,157 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
- "use strict";
-
- /**
- * A controller for the dialog launch view. This view allows manual
- * launching of dialogs for demonstration and testing purposes. It
- * also demonstrates the use of the DialogService.
- * @param $scope
- * @param $timeout
- * @param $log
- * @param dialogService
- * @param notificationService
- * @constructor
- */
- function DialogLaunchController($scope, $timeout, $log, dialogService, notificationService) {
-
- /*
- Demonstrates launching a progress dialog and updating it
- periodically with the progress of an ongoing process.
- */
- $scope.launchProgress = function (knownProgress) {
- var dialog,
- model = {
- title: "Progress Dialog Example",
- progress: 0,
- hint: "Do not navigate away from this page or close this browser tab while this operation is in progress.",
- actionText: "Calculating...",
- unknownProgress: !knownProgress,
- unknownDuration: false,
- severity: "info",
- options: [
- {
- label: "Cancel Operation",
- callback: function () {
- $log.debug("Operation cancelled");
- dialog.dismiss();
- }
- },
- {
- label: "Do something else...",
- callback: function () {
- $log.debug("Something else pressed");
- }
- }
- ]
- };
-
- function incrementProgress() {
- model.progress = Math.min(100, Math.floor(model.progress + Math.random() * 30));
- model.progressText = ["Estimated time remaining: about ", 60 - Math.floor((model.progress / 100) * 60), " seconds"].join(" ");
- if (model.progress < 100) {
- $timeout(incrementProgress, 1000);
- }
- }
-
- dialog = dialogService.showBlockingMessage(model);
-
- if (dialog) {
- //Do processing here
- model.actionText = "Processing 100 objects...";
- if (knownProgress) {
- $timeout(incrementProgress, 1000);
- }
- } else {
- $log.error("Could not display modal dialog");
- }
- };
-
- /*
- Demonstrates launching an error dialog
- */
- $scope.launchError = function () {
- var dialog,
- model = {
- title: "Error Dialog Example",
- actionText: "Something happened, and it was not good.",
- severity: "error",
- options: [
- {
- label: "Try Again",
- callback: function () {
- $log.debug("Try Again Pressed");
- dialog.dismiss();
- }
- },
- {
- label: "Cancel",
- callback: function () {
- $log.debug("Cancel Pressed");
- dialog.dismiss();
- }
- }
- ]
- };
- dialog = dialogService.showBlockingMessage(model);
-
- if (!dialog) {
- $log.error("Could not display modal dialog");
- }
- };
-
- /*
- Demonstrates launching an error dialog
- */
- $scope.launchInfo = function () {
- var dialog,
- model = {
- title: "Info Dialog Example",
- actionText: "This is an example of a blocking info"
- + " dialog. This dialog can be used to draw the user's"
- + " attention to an event.",
- severity: "info",
- primaryOption: {
- label: "OK",
- callback: function () {
- $log.debug("OK Pressed");
- dialog.dismiss();
- }
- }
- };
-
- dialog = dialogService.showBlockingMessage(model);
-
- if (!dialog) {
- $log.error("Could not display modal dialog");
- }
- };
-
- }
-
- return DialogLaunchController;
- }
-);
diff --git a/example/notifications/src/NotificationLaunchController.js b/example/notifications/src/NotificationLaunchController.js
deleted file mode 100644
index 7cf41f402..000000000
--- a/example/notifications/src/NotificationLaunchController.js
+++ /dev/null
@@ -1,126 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
- "use strict";
-
- /**
- * Allows launching of notification messages for the purposes of
- * demonstration and testing. Also demonstrates use of
- * the NotificationService. Notifications are non-blocking messages that
- * appear at the bottom of the screen to inform the user of events
- * in a non-intrusive way. For more information see the
- * {@link NotificationService}
- * @param $scope
- * @param $timeout
- * @param $log
- * @param notificationService
- * @constructor
- */
- function NotificationLaunchController($scope, $timeout, $log, notificationService) {
- var messageCounter = 1;
-
- function getExampleActionText() {
- var actionTexts = [
- "Adipiscing turpis mauris in enim elementu hac, enim aliquam etiam.",
- "Eros turpis, pulvinar turpis eros eu",
- "Lundium nascetur a, lectus montes ac, parturient in natoque, duis risus risus pulvinar pid rhoncus, habitasse auctor natoque!"
- ];
-
- return actionTexts[Math.floor(Math.random() * 3)];
- }
-
- /**
- * Launch a new notification with a severity level of 'Error'.
- */
- $scope.newError = function () {
- notificationService.notify({
- title: "Example error notification " + messageCounter++,
- hint: "An error has occurred",
- severity: "error"
- });
- };
-
- /**
- * Launch a new notification with a severity of 'Alert'.
- */
- $scope.newAlert = function () {
- notificationService.notify({
- title: "Alert notification " + (messageCounter++),
- hint: "This is an alert message",
- severity: "alert",
- autoDismiss: true
- });
- };
-
- /**
- * Launch a new notification with a progress bar that is updated
- * periodically, tracking an ongoing process.
- */
- $scope.newProgress = function () {
- let progress = 0;
- var notificationModel = {
- title: "Progress notification example",
- severity: "info",
- progress: progress,
- actionText: getExampleActionText()
- };
- let notification;
-
- /**
- * Simulate an ongoing process and update the progress bar.
- * @param notification
- */
- function incrementProgress() {
- progress = Math.min(100, Math.floor(progress + Math.random() * 30));
- let progressText = ["Estimated time"
- + " remaining:"
- + " about ", 60 - Math.floor((progress / 100) * 60), " seconds"].join(" ");
- notification.progress(progress, progressText);
-
- if (progress < 100) {
- $timeout(function () {
- incrementProgress(notificationModel);
- }, 1000);
- }
- }
-
- notification = notificationService.notify(notificationModel);
- incrementProgress();
- };
-
- /**
- * Launch a new notification with severity level of INFO.
- */
- $scope.newInfo = function () {
- notificationService.info({
- title: "Example Info notification " + messageCounter++
- });
- };
-
- }
-
- return NotificationLaunchController;
- }
-);
diff --git a/example/notifications/src/NotificationLaunchIndicator.js b/example/notifications/src/NotificationLaunchIndicator.js
deleted file mode 100644
index 3132edf42..000000000
--- a/example/notifications/src/NotificationLaunchIndicator.js
+++ /dev/null
@@ -1,55 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
- "use strict";
-
- /**
- * A tool for manually invoking notifications. When included this
- * indicator will allow for notifications of different types to be
- * launched for demonstration and testing purposes.
- * @constructor
- */
-
- function NotificationLaunchIndicator() {
-
- }
-
- NotificationLaunchIndicator.template = 'notificationLaunchTemplate';
-
- NotificationLaunchIndicator.prototype.getGlyphClass = function () {
- return 'ok';
- };
-
- NotificationLaunchIndicator.prototype.getText = function () {
- return "Launch notification";
- };
-
- NotificationLaunchIndicator.prototype.getDescription = function () {
- return "Launch notification";
- };
-
- return NotificationLaunchIndicator;
- }
-);
diff --git a/example/persistence/bundle.js b/example/persistence/bundle.js
deleted file mode 100644
index 7dac4ab22..000000000
--- a/example/persistence/bundle.js
+++ /dev/null
@@ -1,54 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
- "./src/BrowserPersistenceProvider"
-], function (
- BrowserPersistenceProvider
-) {
- "use strict";
-
- return {
- name: "example/persistence",
- definition: {
- "extensions": {
- "components": [
- {
- "provides": "persistenceService",
- "type": "provider",
- "implementation": BrowserPersistenceProvider,
- "depends": [
- "$q",
- "PERSISTENCE_SPACE"
- ]
- }
- ],
- "constants": [
- {
- "key": "PERSISTENCE_SPACE",
- "value": "mct"
- }
- ]
- }
- }
- };
-});
diff --git a/example/persistence/src/BrowserPersistenceProvider.js b/example/persistence/src/BrowserPersistenceProvider.js
deleted file mode 100644
index 12e88adc5..000000000
--- a/example/persistence/src/BrowserPersistenceProvider.js
+++ /dev/null
@@ -1,102 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Stubbed implementation of a persistence provider,
- * to permit objects to be created, saved, etc.
- */
-define(
- [],
- function () {
- 'use strict';
-
- function BrowserPersistenceProvider($q, SPACE) {
- var spaces = SPACE ? [SPACE] : [],
- caches = {},
- promises = {
- as: function (value) {
- return $q.when(value);
- }
- };
-
- spaces.forEach(function (space) {
- caches[space] = {};
- });
-
- return {
- listSpaces: function () {
- return promises.as(spaces);
- },
- listObjects: function (space) {
- var cache = caches[space];
-
- return promises.as(
- cache ? Object.keys(cache) : null
- );
- },
- createObject: function (space, key, value) {
- var cache = caches[space];
-
- if (!cache || cache[key]) {
- return promises.as(null);
- }
-
- cache[key] = value;
-
- return promises.as(true);
- },
- readObject: function (space, key) {
- var cache = caches[space];
-
- return promises.as(
- cache ? cache[key] : null
- );
- },
- updateObject: function (space, key, value) {
- var cache = caches[space];
-
- if (!cache || !cache[key]) {
- return promises.as(null);
- }
-
- cache[key] = value;
-
- return promises.as(true);
- },
- deleteObject: function (space, key, value) {
- var cache = caches[space];
-
- if (!cache || !cache[key]) {
- return promises.as(null);
- }
-
- delete cache[key];
-
- return promises.as(true);
- }
- };
-
- }
-
- return BrowserPersistenceProvider;
- }
-);
diff --git a/example/policy/bundle.js b/example/policy/bundle.js
deleted file mode 100644
index acceddf89..000000000
--- a/example/policy/bundle.js
+++ /dev/null
@@ -1,45 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
- "./src/ExamplePolicy"
-], function (
- ExamplePolicy
-) {
- "use strict";
-
- return {
- name: "example/policy",
- definition: {
- "name": "Example Policy",
- "description": "Provides an example of using policies to prohibit actions.",
- "extensions": {
- "policies": [
- {
- "implementation": ExamplePolicy,
- "category": "action"
- }
- ]
- }
- }
- };
-});
diff --git a/example/policy/src/ExamplePolicy.js b/example/policy/src/ExamplePolicy.js
deleted file mode 100644
index c17cb21ac..000000000
--- a/example/policy/src/ExamplePolicy.js
+++ /dev/null
@@ -1,47 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
- "use strict";
-
- function ExamplePolicy() {
- return {
- /**
- * Disallow the Remove action on objects whose name contains
- * "foo."
- */
- allow: function (action, context) {
- var domainObject = (context || {}).domainObject,
- model = (domainObject && domainObject.getModel()) || {},
- name = model.name || "",
- metadata = action.getMetadata() || {};
-
- return metadata.key !== 'remove' || name.indexOf('foo') < 0;
- }
- };
- }
-
- return ExamplePolicy;
- }
-);
diff --git a/example/profiling/src/DigestIndicator.js b/example/profiling/src/DigestIndicator.js
deleted file mode 100644
index 80c6c9f9c..000000000
--- a/example/profiling/src/DigestIndicator.js
+++ /dev/null
@@ -1,82 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
- "use strict";
-
- /**
- * Displays the number of digests that have occurred since the
- * indicator was first instantiated.
- * @constructor
- * @param $interval Angular's $interval
- * @implements {Indicator}
- */
- function DigestIndicator($interval, $rootScope) {
- var digests = 0,
- displayed = 0,
- start = Date.now();
-
- function update() {
- var now = Date.now(),
- secs = (now - start) / 1000;
- displayed = Math.round(digests / secs);
- start = now;
- digests = 0;
- }
-
- function increment() {
- digests += 1;
- }
-
- $rootScope.$watch(increment);
-
- // Update state every second
- $interval(update, 1000);
-
- // Provide initial state, too
- update();
-
- return {
- /**
- * Get the CSS class that defines the icon
- * to display in this indicator. This will appear
- * as a dataflow icon.
- * @returns {string} the cssClass of the dataflow icon
- */
- getCssClass: function () {
- return "icon-connectivity";
- },
- getText: function () {
- return displayed + " digests/sec";
- },
- getDescription: function () {
- return "";
- }
- };
- }
-
- return DigestIndicator;
-
- }
-);
diff --git a/example/profiling/src/WatchIndicator.js b/example/profiling/src/WatchIndicator.js
deleted file mode 100644
index 21579b5b7..000000000
--- a/example/profiling/src/WatchIndicator.js
+++ /dev/null
@@ -1,86 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
- "use strict";
-
- /**
- * Updates a count of currently-active Angular watches.
- * @constructor
- * @param $interval Angular's $interval
- */
- function WatchIndicator($interval, $rootScope) {
- var watches = 0;
-
- function count(scope) {
- if (scope) {
- watches += (scope.$$watchers || []).length;
- count(scope.$$childHead);
- count(scope.$$nextSibling);
- }
- }
-
- function update() {
- watches = 0;
- count($rootScope);
- }
-
- // Update state every second
- $interval(update, 1000);
-
- // Provide initial state, too
- update();
-
- return {
- /**
- * Get the CSS class (single character used as an icon)
- * to display in this indicator. This will return ".",
- * which should appear as a database icon.
- * @returns {string} the character of the database icon
- */
- getCssClass: function () {
- return "icon-database";
- },
- /**
- * Get the text that should appear in the indicator.
- * @returns {string} brief summary of connection status
- */
- getText: function () {
- return watches + " watches";
- },
- /**
- * Get a longer-form description of the current connection
- * space, suitable for display in a tooltip
- * @returns {string} longer summary of connection status
- */
- getDescription: function () {
- return "";
- }
- };
- }
-
- return WatchIndicator;
-
- }
-);
diff --git a/example/scratchpad/README.md b/example/scratchpad/README.md
deleted file mode 100644
index 624a4786b..000000000
--- a/example/scratchpad/README.md
+++ /dev/null
@@ -1,2 +0,0 @@
-Example of using multiple persistence stores by exposing a root
-object with a different space prefix.
diff --git a/example/scratchpad/bundle.js b/example/scratchpad/bundle.js
deleted file mode 100644
index 1735b3d45..000000000
--- a/example/scratchpad/bundle.js
+++ /dev/null
@@ -1,63 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
- "./src/ScratchPersistenceProvider"
-], function (
- ScratchPersistenceProvider
-) {
- "use strict";
-
- return {
- name: "example/scratchpad",
- definition: {
- "extensions": {
- "roots": [
- {
- "id": "scratch:root"
- }
- ],
- "models": [
- {
- "id": "scratch:root",
- "model": {
- "type": "folder",
- "composition": [],
- "name": "Scratchpad"
- },
- "priority": "preferred"
- }
- ],
- "components": [
- {
- "provides": "persistenceService",
- "type": "provider",
- "implementation": ScratchPersistenceProvider,
- "depends": [
- "$q"
- ]
- }
- ]
- }
- }
- };
-});
diff --git a/example/scratchpad/src/ScratchPersistenceProvider.js b/example/scratchpad/src/ScratchPersistenceProvider.js
deleted file mode 100644
index 241fb310d..000000000
--- a/example/scratchpad/src/ScratchPersistenceProvider.js
+++ /dev/null
@@ -1,79 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
- 'use strict';
-
- /**
- * The ScratchPersistenceProvider keeps JSON documents in memory
- * and provides a persistence interface, but changes are lost on reload.
- * @memberof example/scratchpad
- * @constructor
- * @implements {PersistenceService}
- * @param q Angular's $q, for promises
- */
- function ScratchPersistenceProvider($q) {
- this.$q = $q;
- this.table = {};
- }
-
- ScratchPersistenceProvider.prototype.listSpaces = function () {
- return this.$q.when(['scratch']);
- };
-
- ScratchPersistenceProvider.prototype.listObjects = function (space) {
- return this.$q.when(
- space === 'scratch' ? Object.keys(this.table) : []
- );
- };
-
- ScratchPersistenceProvider.prototype.createObject = function (space, key, value) {
- if (space === 'scratch') {
- this.table[key] = JSON.stringify(value);
- }
-
- return this.$q.when(space === 'scratch');
- };
-
- ScratchPersistenceProvider.prototype.readObject = function (space, key) {
- return this.$q.when(
- (space === 'scratch' && this.table[key])
- ? JSON.parse(this.table[key]) : undefined
- );
- };
-
- ScratchPersistenceProvider.prototype.deleteObject = function (space, key, value) {
- if (space === 'scratch') {
- delete this.table[key];
- }
-
- return this.$q.when(space === 'scratch');
- };
-
- ScratchPersistenceProvider.prototype.updateObject =
- ScratchPersistenceProvider.prototype.createObject;
-
- return ScratchPersistenceProvider;
- }
-);
diff --git a/example/styleguide/bundle.js b/example/styleguide/bundle.js
deleted file mode 100644
index e1af53082..000000000
--- a/example/styleguide/bundle.js
+++ /dev/null
@@ -1,188 +0,0 @@
-define([
- "./src/ExampleStyleGuideModelProvider",
- "./src/MCTExample",
- "./res/templates/intro.html",
- "./res/templates/standards.html",
- "./res/templates/colors.html",
- "./res/templates/status.html",
- "./res/templates/glyphs.html",
- "./res/templates/controls.html",
- "./res/templates/input.html",
- "./res/templates/menus.html"
-], function (
- ExampleStyleGuideModelProvider,
- MCTExample,
- introTemplate,
- standardsTemplate,
- colorsTemplate,
- statusTemplate,
- glyphsTemplate,
- controlsTemplate,
- inputTemplate,
- menusTemplate
-) {
- return {
- name: "example/styleguide",
- definition: {
- "name": "Open MCT Style Guide",
- "description": "Examples and documentation illustrating UI styles in use in Open MCT.",
- "extensions":
- {
- "types": [
- {
- "key": "styleguide.intro",
- "name": "Introduction",
- "cssClass": "icon-page",
- "description": "Introduction and overview to the style guide"
- },
- {
- "key": "styleguide.standards",
- "name": "Standards",
- "cssClass": "icon-page",
- "description": ""
- },
- {
- "key": "styleguide.colors",
- "name": "Colors",
- "cssClass": "icon-page",
- "description": ""
- },
- {
- "key": "styleguide.status",
- "name": "status",
- "cssClass": "icon-page",
- "description": "Limits, telemetry paused, etc."
- },
- {
- "key": "styleguide.glyphs",
- "name": "Glyphs",
- "cssClass": "icon-page",
- "description": "Glyphs overview"
- },
- {
- "key": "styleguide.controls",
- "name": "Controls",
- "cssClass": "icon-page",
- "description": "Buttons, selects, HTML controls"
- },
- {
- "key": "styleguide.input",
- "name": "Text Inputs",
- "cssClass": "icon-page",
- "description": "Various text inputs"
- },
- {
- "key": "styleguide.menus",
- "name": "Menus",
- "cssClass": "icon-page",
- "description": "Context menus, dropdowns"
- }
- ],
- "views": [
- {
- "key": "styleguide.intro",
- "type": "styleguide.intro",
- "template": introTemplate,
- "editable": false
- },
- {
- "key": "styleguide.standards",
- "type": "styleguide.standards",
- "template": standardsTemplate,
- "editable": false
- },
- {
- "key": "styleguide.colors",
- "type": "styleguide.colors",
- "template": colorsTemplate,
- "editable": false
- },
- {
- "key": "styleguide.status",
- "type": "styleguide.status",
- "template": statusTemplate,
- "editable": false
- },
- {
- "key": "styleguide.glyphs",
- "type": "styleguide.glyphs",
- "template": glyphsTemplate,
- "editable": false
- },
- {
- "key": "styleguide.controls",
- "type": "styleguide.controls",
- "template": controlsTemplate,
- "editable": false
- },
- {
- "key": "styleguide.input",
- "type": "styleguide.input",
- "template": inputTemplate,
- "editable": false
- },
- {
- "key": "styleguide.menus",
- "type": "styleguide.menus",
- "template": menusTemplate,
- "editable": false
- }
- ],
- "roots": [
- {
- "id": "styleguide:home"
- }
- ],
- "models": [
- {
- "id": "styleguide:home",
- "priority": "preferred",
- "model": {
- "type": "noneditable.folder",
- "name": "Style Guide Home",
- "location": "ROOT",
- "composition": [
- "intro",
- "standards",
- "colors",
- "status",
- "glyphs",
- "styleguide:ui-elements"
- ]
- }
- },
- {
- "id": "styleguide:ui-elements",
- "priority": "preferred",
- "model": {
- "type": "noneditable.folder",
- "name": "UI Elements",
- "location": "styleguide:home",
- "composition": [
- "controls",
- "input",
- "menus"
- ]
- }
- }
- ],
- "directives": [
- {
- "key": "mctExample",
- "implementation": MCTExample
- }
- ],
- "components": [
- {
- "provides": "modelService",
- "type": "provider",
- "implementation": ExampleStyleGuideModelProvider,
- "depends": [
- "$q"
- ]
- }
- ]
- }
- }
- };
-});
diff --git a/example/styleguide/res/images/diagram-containment.svg b/example/styleguide/res/images/diagram-containment.svg
deleted file mode 100644
index a718ae33a..000000000
--- a/example/styleguide/res/images/diagram-containment.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 574"><defs><style>.a{fill:#231f20;opacity:0.1;}.b{fill:#80d8f0;}.Line\.singleton,.c,.line\.contain-own{fill:none;stroke-miterlimit:10;}.c{stroke:#fff;}.d{fill:#f8b6c2;}.e,.f,.l{fill:#00b1e2;}.f,.h{font-size:21px;}.f,.h,.m{font-family:Helvetica Neue;}.g,.h,.i{fill:#f16e86;}.g,.l{fill-opacity:0.7;}.g,.i{opacity:0.99;}.Line\.singleton{stroke:#f16e86;}.Line\.singleton,.line\.contain-own{stroke-opacity:0.7;stroke-width:2px;stroke-dasharray:5;}.j{letter-spacing:-2px;}.k{letter-spacing:-1px;}.line\.contain-own{stroke:#00b1e2;}.m{font-size:20px;fill:#737373;}</style></defs><title>d</title><ellipse class="a" cx="510.28" cy="396.61" rx="505.71" ry="146.79"/><ellipse class="b" cx="511.28" cy="386.61" rx="505.71" ry="146.79"/><ellipse class="c" cx="511.28" cy="386.61" rx="505.71" ry="146.79"/><ellipse class="a" cx="430.53" cy="289.9" rx="425.96" ry="123.64"/><ellipse class="b" cx="431.53" cy="279.9" rx="425.96" ry="123.64"/><ellipse class="c" cx="431.53" cy="279.9" rx="425.96" ry="123.64"/><ellipse class="a" cx="420.25" cy="295.43" rx="134.04" ry="38.91"/><ellipse class="d" cx="421.25" cy="285.43" rx="134.04" ry="38.91"/><ellipse class="c" cx="421.25" cy="285.43" rx="134.04" ry="38.91"/><ellipse class="a" cx="138.61" cy="228.16" rx="134.04" ry="38.91"/><ellipse class="b" cx="139.61" cy="218.16" rx="134.04" ry="38.91"/><ellipse class="c" cx="139.61" cy="218.16" rx="134.04" ry="38.91"/><ellipse class="a" cx="420.25" cy="228.16" rx="134.04" ry="38.91"/><ellipse class="d" cx="421.25" cy="218.16" rx="134.04" ry="38.91"/><ellipse class="c" cx="421.25" cy="218.16" rx="134.04" ry="38.91"/><ellipse class="a" cx="419.61" cy="160.88" rx="134.04" ry="38.91"/><ellipse class="d" cx="420.61" cy="150.88" rx="134.04" ry="38.91"/><ellipse class="c" cx="420.61" cy="150.88" rx="134.04" ry="38.91"/><ellipse class="a" cx="390.89" cy="99.79" rx="105.32" ry="30.57"/><ellipse class="d" cx="391.89" cy="89.79" rx="105.32" ry="30.57"/><ellipse class="c" cx="391.89" cy="89.79" rx="105.32" ry="30.57"/><ellipse class="a" cx="109.88" cy="168.88" rx="105.32" ry="30.57"/><ellipse class="b" cx="110.88" cy="158.88" rx="105.32" ry="30.57"/><ellipse class="c" cx="110.88" cy="158.88" rx="105.32" ry="30.57"/><path class="e" d="M924.48,406.86H913.71v-1.8a3.6,3.6,0,0,0-3.59-3.59h-7.18a3.6,3.6,0,0,0-3.59,3.59v9a3.6,3.6,0,0,1,3.59-3.59h21.54a3.6,3.6,0,0,1,3.59,3.59v-3.59A3.6,3.6,0,0,0,924.48,406.86Z"/><rect class="e" x="899.35" y="414.04" width="28.72" height="16.16" rx="3.59" ry="3.59"/><text class="f" transform="translate(933.82 423.41)">Folder</text><ellipse class="g" cx="50.61" cy="412.43" rx="16.88" ry="5.94"/><ellipse class="g" cx="50.61" cy="161.02" rx="16.88" ry="5.94"/><line class="Line.singleton" x1="50.61" y1="37.92" x2="50.61" y2="411.88"/><text class="h" transform="translate(73.71 23.46)">Activity Mode</text><path class="i" d="M50.85,5.48A14.91,14.91,0,0,0,37,14.79h2.64a5.57,5.57,0,0,1,4.17,1.86h7l-5.59-5.59h7.45L62,20.38,52.71,29.7H45.26l5.59-5.59h-7A5.57,5.57,0,0,1,39.67,26H37A14.9,14.9,0,1,0,50.85,5.48Z"/><ellipse class="g" cx="320.17" cy="492.43" rx="16.88" ry="5.94"/><ellipse class="g" cx="320.17" cy="288.63" rx="16.88" ry="5.94"/><ellipse class="g" cx="320.17" cy="368.62" rx="16.88" ry="5.94"/><ellipse class="g" cx="320.17" cy="220.08" rx="16.88" ry="5.94"/><ellipse class="g" cx="320.17" cy="151.53" rx="16.88" ry="5.94"/><ellipse class="g" cx="320.17" cy="89.41" rx="16.88" ry="5.94"/><line class="Line.singleton" x1="320.39" y1="36.23" x2="320.39" y2="492.69"/><text class="h" transform="translate(343.27 22.68)"><tspan class="j">T</tspan><tspan x="9.72" y="0">elemetry Element</tspan></text><path class="i" d="M316,12.44a5.25,5.25,0,0,1,4.09,2.43,17.94,17.94,0,0,1,2.28,4.24c1.08,2.76,2.21,3.94,2.61,4.1,0.4-.16,1.53-1.33,2.61-4.1s2.78-5.81,5.3-6.52a14.36,14.36,0,0,0-26.48,10.09,2,2,0,0,0,.62.52c0.4-.16,1.53-1.33,2.61-4.1C310.8,16.06,312.87,12.44,316,12.44Z"/><path class="i" d="M334.54,17a2,2,0,0,0-.62-0.52c-0.4.16-1.53,1.33-2.61,4.1-1.19,3-3.26,6.67-6.37,6.67a5.25,5.25,0,0,1-4.09-2.43,18,18,0,0,1-2.28-4.24c-1.08-2.76-2.21-3.94-2.61-4.1-0.4.16-1.53,1.33-2.61,4.1s-2.78,5.81-5.3,6.52A14.36,14.36,0,0,0,334.54,17Z"/><ellipse class="g" cx="594.57" cy="501.41" rx="16.88" ry="5.94"/><ellipse class="g" cx="594.57" cy="362.9" rx="16.88" ry="5.94"/><line class="Line.singleton" x1="594.57" y1="85.54" x2="594.57" y2="501.41"/><line class="Line.singleton" x1="594.57" y1="36.09" x2="594.57" y2="48.55"/><text class="h" transform="translate(617.68 22.64)">Clock</text><path class="i" d="M594.51,5.48a14.36,14.36,0,1,0,14.36,14.36A14.36,14.36,0,0,0,594.51,5.48Zm7.57,19.35a2,2,0,0,1-2.76.74L593.11,22h0l-0.07,0,0,0-0.05,0,0,0,0,0,0,0,0,0-0.06-.05h0a2,2,0,0,1-.37-0.44l0,0,0,0a2,2,0,0,1-.2-0.54h0l0-.08V20.25h0V10a2,2,0,0,1,4,0v9l5.21,3A2,2,0,0,1,602.09,24.83Z"/><text class="h" transform="translate(617.68 70.51)">Timer</text><path class="i" d="M598.67,55.54V52.9a2.05,2.05,0,0,0-2.05-2.05h-4.1a2.05,2.05,0,0,0-2.05,2.05v2.65A14.36,14.36,0,1,0,598.67,55.54ZM595.2,69.95l-8.46,6.76a10.77,10.77,0,0,1,7.81-18.18l0.64,0V69.95Z"/><ellipse class="g" cx="712.33" cy="491.37" rx="16.88" ry="5.94"/><ellipse class="g" cx="712.33" cy="342.33" rx="16.88" ry="5.94"/><line class="Line.singleton" x1="712.33" y1="35.63" x2="712.33" y2="491.37"/><text class="h" transform="translate(732.44 25.18)"><tspan class="k">W</tspan><tspan x="18.25" y="0">eb Page</tspan></text><path class="i" d="M717.92,19.84a5.4,5.4,0,0,1-5.39-5.39v-9h-9a5.4,5.4,0,0,0-5.39,5.39v18a5.4,5.4,0,0,0,5.39,5.39h18a5.4,5.4,0,0,0,5.39-5.39v-9h-9Z"/><path class="i" d="M719.72,16.25h7.18L716.13,5.48v7.18A3.6,3.6,0,0,0,719.72,16.25Z"/><text class="f" transform="translate(789.85 322.46)">Display Layout</text><ellipse class="l" cx="770.72" cy="484.1" rx="16.88" ry="5.94"/><line class="line.contain-own" x1="770.72" y1="331.49" x2="770.72" y2="484.1"/><path class="e" d="M769,300.92h-7.18a5.4,5.4,0,0,0-5.39,5.39v18a5.4,5.4,0,0,0,5.39,5.39H769V300.92Z"/><path class="e" d="M779.73,300.92h-7.18v16.2h12.57V306.3A5.4,5.4,0,0,0,779.73,300.92Z"/><path class="e" d="M772.55,329.64h7.18a5.4,5.4,0,0,0,5.39-5.39v-3.64H772.55v9Z"/><ellipse class="l" cx="138.48" cy="455.07" rx="16.88" ry="5.94"/><ellipse class="l" cx="138.48" cy="340.75" rx="16.88" ry="5.94"/><line class="line.contain-own" x1="138.48" y1="245.87" x2="138.48" y2="455.07"/><text class="f" transform="translate(156.49 237.91)">Timeline</text><rect class="e" x="131.31" y="224.18" width="10.77" height="3.59"/><rect class="e" x="134.9" y="229.57" width="10.77" height="3.59"/><rect class="e" x="133.11" y="234.95" width="10.77" height="3.59"/><path class="e" d="M147.47,217h-3.59v5.39h3.59v17.93h-3.59v5.39h3.59a5.4,5.4,0,0,0,5.39-5.39v-18A5.4,5.4,0,0,0,147.47,217Z"/><path class="e" d="M129.52,240.32V222.4h3.59V217h-3.59a5.4,5.4,0,0,0-5.39,5.39v18a5.4,5.4,0,0,0,5.39,5.39h3.59v-5.39h-3.59Z"/><ellipse class="g" cx="421.87" cy="502.71" rx="16.88" ry="5.94"/><ellipse class="g" cx="421.87" cy="374.73" rx="16.88" ry="5.94"/><line class="Line.singleton" x1="421.87" y1="180.01" x2="421.87" y2="215.19"/><line class="Line.singleton" x1="421.87" y1="246.55" x2="421.87" y2="285.56"/><line class="Line.singleton" x1="421.87" y1="313.57" x2="421.87" y2="502.71"/><text class="h" transform="translate(441.26 170.44)">Fixed</text><rect class="i" x="407.51" y="159.86" width="3.59" height="7.18"/><path class="i" d="M411.1,152.69h5.39v-3.59H411.1a3.6,3.6,0,0,0-3.59,3.59v5.39h3.59v-5.38Z"/><path class="i" d="M411.1,174.22v-5.38h-3.59v5.39a3.6,3.6,0,0,0,3.59,3.59h5.39v-3.59H411.1Z"/><rect class="i" x="418.28" y="149.09" width="7.18" height="3.59"/><path class="i" d="M432.65,174.22h-5.39v3.59h5.39a3.6,3.6,0,0,0,3.59-3.59v-5.39h-3.59v5.38Z"/><path class="i" d="M432.65,149.09h-5.39v3.59h5.39v5.38h3.59v-5.39A3.6,3.6,0,0,0,432.65,149.09Z"/><rect class="i" x="432.65" y="159.86" width="3.59" height="7.18"/><rect class="i" x="418.28" y="174.23" width="7.18" height="3.59"/><rect class="i" x="414.69" y="156.27" width="14.36" height="14.36"/><ellipse class="g" cx="368.89" cy="498.33" rx="16.88" ry="5.94"/><line class="Line.singleton" x1="368.89" y1="101.13" x2="368.89" y2="498.33"/><path class="i" d="M369.26,77.09l-14.36,9v14.36a5.4,5.4,0,0,0,5.39,5.39h18a5.4,5.4,0,0,0,5.39-5.39V86.07Zm0,5.39,10.05,6.28L369.26,95l-10.05-6.28Z"/><text class="h" transform="translate(388.19 101.09)">Packet</text><ellipse class="l" cx="92.91" cy="438.7" rx="16.88" ry="5.94"/><ellipse class="l" cx="92.91" cy="322.78" rx="16.88" ry="5.94"/><ellipse class="l" cx="92.91" cy="230.39" rx="16.88" ry="5.94"/><line class="line.contain-own" x1="92.91" y1="181.86" x2="92.91" y2="438.7"/><text class="f" transform="translate(107.32 173.2)">Activity</text><path class="e" d="M94.19,153.3H87l9,9H87.84a7.18,7.18,0,0,0-6.22-3.59H78V173h3.59a7.18,7.18,0,0,0,6.22-3.59H96l-9,9h7.18l12.57-12.57Z"/><text class="h" transform="translate(441.26 303.84)"><tspan class="j">T</tspan><tspan x="9.72" y="0">able</tspan></text><path class="i" d="M433.17,283.37H411.62A3.6,3.6,0,0,0,408,287V308.5a3.6,3.6,0,0,0,3.59,3.59h21.54a3.6,3.6,0,0,0,3.59-3.59V287A3.6,3.6,0,0,0,433.17,283.37ZM417,310.3h-5.39a1.8,1.8,0,0,1-1.8-1.8v-3.59H417v5.39Zm0-7.18h-7.18v-5.39H417v5.39Zm0-7.18h-7.18v-5.39H417v5.39Zm9,14.36H418.8v-5.39H426v5.39Zm0-7.18H418.8v-5.39H426v5.39Zm0-7.18H418.8v-5.39H426v5.39Zm9,12.57a1.8,1.8,0,0,1-1.8,1.8h-5.39v-5.39H435v3.59Zm0-5.39h-7.18v-5.39H435v5.39Zm0-7.18h-7.18v-5.39H435v5.39Z"/><text class="h" transform="translate(441.26 238.09)">Plot</text><path class="i" d="M431.19,217.07H413.36a5.46,5.46,0,0,0-5.44,5.44v11.41a2.78,2.78,0,0,0,.9.87c0.4-.16,1.53-1.33,2.61-4.1,1.19-3,3.26-6.67,6.37-6.67a5.25,5.25,0,0,1,4.09,2.43,17.94,17.94,0,0,1,2.28,4.24c1.08,2.76,2.21,3.94,2.61,4.1,0.4-.16,1.53-1.33,2.61-4.1,1.19-3,3.26-6.67,6.37-6.67a3.9,3.9,0,0,1,.9.1v-1.62A5.46,5.46,0,0,0,431.19,217.07Z"/><path class="i" d="M435.74,228.08c-0.4.16-1.53,1.33-2.61,4.1-1.19,3-3.26,6.67-6.37,6.67a5.25,5.25,0,0,1-4.09-2.43,17.94,17.94,0,0,1-2.28-4.24c-1.08-2.76-2.21-3.94-2.61-4.1-0.4.16-1.53,1.33-2.61,4.1-1.19,3-3.26,6.67-6.37,6.67a3.9,3.9,0,0,1-.9-0.1v1.62a5.46,5.46,0,0,0,5.44,5.44h17.84a5.46,5.46,0,0,0,5.44-5.44V228.94A2.78,2.78,0,0,0,435.74,228.08Z"/><rect class="e" x="742.49" y="532.43" width="22.98" height="22.98" rx="2" ry="2"/><text class="m" transform="translate(769.86 551.1)">Can contain their own type</text></svg> \ No newline at end of file
diff --git a/example/styleguide/res/images/diagram-objects.svg b/example/styleguide/res/images/diagram-objects.svg
deleted file mode 100644
index c457666dc..000000000
--- a/example/styleguide/res/images/diagram-objects.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 719 622.47"><defs><style>.a{fill:#fff;}.b,.h{fill:none;stroke-miterlimit:10;}.b{stroke:#737373;}.c,.d{opacity:0.99;}.d,.e,.j,.k,.n{fill:#00b1e2;}.e{font-size:14px;}.e,.j,.n{font-family:HelveticaNeue, Helvetica Neue;}.f{letter-spacing:-0.11em;}.g{fill:#d6d6d6;}.h{stroke:#ababab;stroke-dasharray:5;}.i{opacity:0.5;fill:url(#a);}.j{font-size:14.33px;}.l{fill:#f0f0f0;}.m{opacity:0.8;}.n{font-size:15.44px;}.o{letter-spacing:-0.11em;}</style><linearGradient id="a" x1="547.24" y1="-217.84" x2="537.62" y2="-254.83" gradientTransform="translate(127.72 377.65) rotate(45)" gradientUnits="userSpaceOnUse"><stop offset="0"/><stop offset="1" stop-opacity="0"/></linearGradient><symbol id="b" viewBox="0 0 331 81"><rect class="a" x="0.5" y="0.5" width="330" height="80" rx="5" ry="5"/><rect class="b" x="0.5" y="0.5" width="330" height="80" rx="5" ry="5"/><g class="c"><path class="d" d="M12.91,10.08a3.66,3.66,0,0,1,2.85,1.69,12.49,12.49,0,0,1,1.59,3c.75,1.92,1.54,2.74,1.82,2.85.28-.11,1.06-.93,1.82-2.85s1.94-4,3.69-4.54a10,10,0,0,0-18.44,7,1.38,1.38,0,0,0,.43.36c.28-.11,1.06-.93,1.82-2.85C9.3,12.6,10.74,10.08,12.91,10.08Z"/><path class="d" d="M25.84,13.26a1.38,1.38,0,0,0-.43-.36c-.28.11-1.06.93-1.82,2.85-.83,2.12-2.27,4.64-4.43,4.64a3.66,3.66,0,0,1-2.85-1.69,12.5,12.5,0,0,1-1.59-3C14,13.83,13.18,13,12.91,12.9c-.28.11-1.06.93-1.82,2.85s-1.94,4-3.69,4.54a10,10,0,0,0,18.44-7Z"/></g><text class="e" transform="translate(30.83 20.56)"><tspan class="f">T</tspan><tspan x="6.48" y="0">elemetry Element</tspan></text><rect class="g" x="31.05" y="30.75" width="289.31" height="38.35"/></symbol></defs><title>diagram-objects</title><line class="h" x1="6.31" y1="161.24" x2="77.48" y2="120.15"/><polygon class="i" points="666.72 614.57 666.72 562.32 712.05 588.44 666.72 614.57"/><path class="a" d="M669.11,167.18a6.89,6.89,0,0,0-4.83-6.29L76.15,3.29A3.65,3.65,0,0,0,71.32,7V450.34a6.89,6.89,0,0,0,4.83,6.29L664.28,614.23a3.65,3.65,0,0,0,4.83-3.71Z"/><path class="b" d="M669.11,167.18a6.89,6.89,0,0,0-4.83-6.29L76.15,3.29A3.65,3.65,0,0,0,71.32,7V450.34a6.89,6.89,0,0,0,4.83,6.29L664.28,614.23a3.65,3.65,0,0,0,4.83-3.71Z"/><text class="j" transform="matrix(1, 0.27, 0, 1, 103.5, 35.75)">Display Layout</text><path class="k" d="M87.84,15.66,82.72,14.3a2.86,2.86,0,0,0-3.84,2.82V29.91a5.37,5.37,0,0,0,3.84,4.86l5.12,1.36Z"/><path class="k" d="M95.52,17.71,90.4,16.34V27.89l9,2.38V22.57A5.37,5.37,0,0,0,95.52,17.71Z"/><path class="k" d="M90.4,36.81l5.12,1.36a2.86,2.86,0,0,0,3.84-2.82V32.77l-9-2.38Z"/><path class="l" d="M277,169a3.66,3.66,0,0,1-4.83,3.71L83.32,122.46a6.88,6.88,0,0,1-4.83-6.29V49a3.66,3.66,0,0,1,4.83-3.71l188.9,50.29a6.88,6.88,0,0,1,4.83,6.29Z"/><path class="l" d="M277,462.27a3.66,3.66,0,0,1-4.83,3.71L83.32,415.7a6.88,6.88,0,0,1-4.83-6.29V134.92a3.66,3.66,0,0,1,4.83-3.71l188.9,50.29a6.88,6.88,0,0,1,4.83,6.29Z"/><path class="l" d="M658.73,565a3.66,3.66,0,0,1-4.83,3.71L288.5,471.4a6.88,6.88,0,0,1-4.83-6.29V103.58a3.66,3.66,0,0,1,4.83-3.71l365.4,97.27a6.88,6.88,0,0,1,4.83,6.29Z"/><g class="m"><path class="a" d="M585,605.89a3.66,3.66,0,0,1-4.83,3.71l-365.4-97.27A6.88,6.88,0,0,1,210,506V144.51a3.66,3.66,0,0,1,4.83-3.71l365.4,97.27a6.88,6.88,0,0,1,4.83,6.29Z"/><path class="b" d="M585,605.89a3.66,3.66,0,0,1-4.83,3.71l-365.4-97.27A6.88,6.88,0,0,1,210,506V144.51a3.66,3.66,0,0,1,4.83-3.71l365.4,97.27a6.88,6.88,0,0,1,4.83,6.29Z"/><text class="n" transform="matrix(1, 0.27, 0, 1, 243.44, 171.65)">Plot</text><g class="c"><path class="d" d="M234.06,152.09l-13.7-3.65a3.11,3.11,0,0,0-4.18,3.07v8.77a3.17,3.17,0,0,0,.69.85c.31,0,1.17-.71,2-2.61.91-2.09,2.51-4.45,4.89-3.82a5.74,5.74,0,0,1,3.14,2.7,18.93,18.93,0,0,1,1.75,3.72,9,9,0,0,0,2,3.68c.31,0,1.17-.71,2-2.61.91-2.09,2.51-4.46,4.89-3.82a3.6,3.6,0,0,1,.69.26v-1.24A5.85,5.85,0,0,0,234.06,152.09Z"/><path class="d" d="M237.55,161.47c-.31,0-1.17.71-2,2.61-.91,2.09-2.51,4.45-4.89,3.82a5.74,5.74,0,0,1-3.14-2.7,18.93,18.93,0,0,1-1.75-3.72,9,9,0,0,0-2-3.68c-.31,0-1.17.71-2,2.61-.91,2.09-2.51,4.46-4.89,3.82a3.6,3.6,0,0,1-.69-.26v1.24a5.85,5.85,0,0,0,4.18,5.29l13.7,3.65a3.11,3.11,0,0,0,4.18-3.07v-8.77A3.17,3.17,0,0,0,237.55,161.47Z"/></g></g><path class="l" d="M580.08,356a3.66,3.66,0,0,1-4.83,3.71L220.89,265.41a6.88,6.88,0,0,1-4.83-6.29V180.88a3.66,3.66,0,0,1,4.83-3.71L575.25,271.5a6.88,6.88,0,0,1,4.83,6.29Z"/><path class="l" d="M580.08,451a3.66,3.66,0,0,1-4.83,3.71L220.89,360.39a6.88,6.88,0,0,1-4.83-6.29V275.86a3.66,3.66,0,0,1,4.83-3.71l354.37,94.34a6.88,6.88,0,0,1,4.83,6.29Z"/><path class="l" d="M580.08,545.88a3.66,3.66,0,0,1-4.83,3.71L220.89,455.26a6.88,6.88,0,0,1-4.83-6.29V370.73a3.66,3.66,0,0,1,4.83-3.71l354.37,94.34a6.88,6.88,0,0,1,4.83,6.29Z"/><line class="h" x1="6.33" y1="86.4" x2="77.26" y2="45.45"/><line class="h" x1="6.11" y1="172.56" x2="78.79" y2="130.6"/><line class="h" x1="6.19" y1="454.54" x2="77.48" y2="413.39"/><line class="h" x1="209.99" y1="511.05" x2="282.66" y2="469.09"/><line class="h" x1="203.37" y1="508.2" x2="276.04" y2="466.25"/><line class="h" x1="584.06" y1="609.92" x2="656.73" y2="567.96"/><line class="h" x1="483" y1="604.78" x2="578.08" y2="549.88"/><line class="h" x1="485" y1="327.43" x2="580.08" y2="272.53"/><line class="h" x1="482.47" y1="415.64" x2="577.55" y2="360.75"/><line class="h" x1="485" y1="422.66" x2="580.08" y2="367.77"/><line class="h" x1="483" y1="509.91" x2="578.08" y2="455.02"/><line class="h" x1="485" y1="517.53" x2="580.08" y2="462.63"/><g class="m"><path class="a" d="M203.37,210a3.66,3.66,0,0,1-4.83,3.71L9.64,163.4a6.88,6.88,0,0,1-4.83-6.29V89.89a3.66,3.66,0,0,1,4.83-3.71l188.9,50.29a6.88,6.88,0,0,1,4.83,6.29Z"/><path class="b" d="M203.37,210a3.66,3.66,0,0,1-4.83,3.71L9.64,163.4a6.88,6.88,0,0,1-4.83-6.29V89.89a3.66,3.66,0,0,1,4.83-3.71l188.9,50.29a6.88,6.88,0,0,1,4.83,6.29Z"/><polygon class="g" points="184.11 196.32 44.52 159.16 44.52 136.18 184.11 173.34 184.11 196.32"/><text class="n" transform="matrix(1, 0.27, 0, 1, 38.26, 115.93)">Clock</text><path class="d" d="M21.94,94.68c-6.09-1.62-11,2-11,8.09s4.94,12.35,11,14,11-2,11-8.09S28,96.3,21.94,94.68Zm5.82,16.41a1.19,1.19,0,0,1-1.34.42,2,2,0,0,1-.78-.41l-4.78-4h0l-.05,0,0,0,0,0,0,0,0,0,0,0v0l0-.05h0a2.25,2.25,0,0,1-.28-.42l0,0,0,0a2,2,0,0,1-.15-.45h0v-.33h0V97.67a1.15,1.15,0,0,1,1.55-1.14,2.17,2.17,0,0,1,1.55,2v6.95l4,3.38A1.81,1.81,0,0,1,27.76,111.09Z"/></g><g class="m"><path class="a" d="M203.37,503.2a3.66,3.66,0,0,1-4.83,3.71L9.64,456.63a6.88,6.88,0,0,1-4.83-6.29V175.85a3.66,3.66,0,0,1,4.83-3.71l188.9,50.29a6.88,6.88,0,0,1,4.83,6.29Z"/><path class="b" d="M203.37,503.2a3.66,3.66,0,0,1-4.83,3.71L9.64,456.63a6.88,6.88,0,0,1-4.83-6.29V175.85a3.66,3.66,0,0,1,4.83-3.71l188.9,50.29a6.88,6.88,0,0,1,4.83,6.29Z"/><polygon class="g" points="184.11 282.28 44.52 245.12 44.52 222.14 184.11 259.3 184.11 282.28"/><polygon class="g" points="184.11 319.14 44.52 281.98 44.52 259 184.11 296.16 184.11 319.14"/><polygon class="g" points="184.11 356 44.52 318.84 44.52 295.86 184.11 333.02 184.11 356"/><polygon class="g" points="184.11 392.86 44.52 355.7 44.52 332.72 184.11 369.88 184.11 392.86"/><polygon class="g" points="184.11 429.72 44.52 392.56 44.52 369.58 184.11 406.74 184.11 429.72"/><polygon class="g" points="184.11 466.57 44.52 429.42 44.52 406.44 184.11 443.6 184.11 466.57"/><text class="n" transform="matrix(1, 0.27, 0, 1, 38.26, 201.89)"><tspan class="o">T</tspan><tspan x="7.15" y="0">able</tspan></text><path class="d" d="M29.65,183.42l-15.35-3.9a1.93,1.93,0,0,0-2.56,1.91v15.35A3.53,3.53,0,0,0,14.3,200l15.35,3.9A1.93,1.93,0,0,0,32.21,202V186.63A3.53,3.53,0,0,0,29.65,183.42ZM18.14,199.68l-3.84-1a1.72,1.72,0,0,1-.9-.61,1.62,1.62,0,0,1-.38-1v-2.56l5.12,1.3Zm0-5.12L13,193.27v-3.84l5.12,1.3Zm0-5.12L13,188.15v-3.84l5.12,1.3Zm6.4,11.86L19.42,200v-3.84l5.12,1.3Zm0-5.12-5.12-1.3v-3.84l5.12,1.3Zm0-5.12-5.12-1.3v-3.84l5.12,1.3Zm6.4,10.58a1,1,0,0,1-.38.81,1,1,0,0,1-.9.15l-3.84-1V197.8l5.12,1.3Zm0-3.84-5.12-1.3v-3.84l5.12,1.3Zm0-5.12-5.12-1.3v-3.84l5.12,1.3Z"/></g><line class="h" x1="122.45" y1="233.48" x2="217.53" y2="178.59"/><line class="h" x1="585.05" y1="239.36" x2="656.42" y2="199.71"/><line class="h" x1="211.99" y1="141.51" x2="285.67" y2="100.58"/><line class="h" x1="203.37" y1="137.75" x2="275.23" y2="97.83"/><line class="h" x1="122.45" y1="328.46" x2="217.53" y2="273.57"/><line class="h" x1="122.45" y1="423.33" x2="217.53" y2="368.44"/><line class="h" x1="122.26" y1="508.29" x2="215.53" y2="454.45"/><line class="h" x1="121.66" y1="319.03" x2="215.53" y2="264.84"/><line class="h" x1="122.01" y1="413.81" x2="215.53" y2="359.82"/><use width="331" height="81" transform="matrix(1.1, 0.29, 0, 1.1, 119.89, 230.79)" xlink:href="#b"/><use width="331" height="81" transform="matrix(1.1, 0.29, 0, 1.1, 119.89, 325.77)" xlink:href="#b"/><use width="331" height="81" transform="matrix(1.1, 0.29, 0, 1.1, 119.89, 420.63)" xlink:href="#b"/></svg> \ No newline at end of file
diff --git a/example/styleguide/res/images/diagram-views.svg b/example/styleguide/res/images/diagram-views.svg
deleted file mode 100644
index c62a2fc3a..000000000
--- a/example/styleguide/res/images/diagram-views.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 800 622.47"><defs><style>.a,.d{fill:#fff;}.b,.c,.k,.m,.n,.o{fill:none;}.b{stroke:#000;stroke-linecap:square;}.b,.c,.d,.f,.k,.m,.n,.o{stroke-miterlimit:10;}.b,.k{stroke-width:2px;}.c{stroke:#ababab;stroke-dasharray:5;}.d,.k,.o{stroke:#737373;}.e{opacity:0.5;fill:url(#a);}.f{fill:#d6d6d6;fill-opacity:0.8;stroke:#fff;}.g{mask:url(#b);}.h{fill:#a8a8a8;}.i{fill:#00b1e2;}.j{fill:#f16e86;}.k{stroke-linecap:round;}.l{mask:url(#c);}.m{stroke:#00b1e2;}.m,.n{stroke-width:10px;}.n{stroke:#f16e86;}</style><linearGradient id="a" x1="523.15" y1="-401.02" x2="513.75" y2="-437.16" gradientTransform="translate(90.62 338.26) rotate(45)" gradientUnits="userSpaceOnUse"><stop offset="0"/><stop offset="1" stop-opacity="0"/></linearGradient><mask id="b" x="-1187.45" y="-269.68" width="1813.84" height="554.26" maskUnits="userSpaceOnUse"><polygon class="a" points="625.33 284.58 452.55 238.2 452.55 170.5 625.33 216.88 625.33 284.58"/><line class="b" x1="486.25" y1="179.55" x2="486.25" y2="247.32"/><line class="b" x1="573.58" y1="202.99" x2="573.58" y2="270.76"/><line class="b" x1="452.46" y1="192.89" x2="625.16" y2="239.25"/><line class="b" x1="452.46" y1="215.48" x2="625.16" y2="261.83"/></mask><mask id="c" x="-1468.9" y="-163.59" width="1866.4" height="673.87" maskUnits="userSpaceOnUse"><polygon class="a" points="344.11 510.28 171.1 462.49 171.1 289.49 344.11 337.28 344.11 510.28"/></mask></defs><title>objects-diagram</title><polyline class="c" points="460.63 153.71 480.14 142.45 513.15 123.39"/><polyline class="c" points="312.69 239.13 336.82 225.2 365.42 208.68"/><polygon class="d" points="742.15 65.53 519.88 5.97 519.88 368.97 742.15 428.53 742.15 65.53"/><polygon class="e" points="742.15 428.53 742.15 377.47 786.44 403 742.15 428.53"/><polyline class="c" points="619.05 308.21 626.95 303.65 636.44 298.17"/><polygon class="f" points="637.26 156.11 443.71 104.25 443.71 402.92 637.26 454.78 637.26 156.11"/><g class="g"><polygon class="h" points="625.33 239.44 452.55 193.07 452.55 170.5 625.33 216.88 625.33 239.44"/><polygon class="i" points="625.33 262.01 452.55 215.64 452.55 193.07 625.33 239.44 625.33 262.01"/><polygon class="j" points="625.33 284.58 452.55 238.2 452.55 215.64 625.33 262.01 625.33 284.58"/></g><polyline class="c" points="467.58 395.66 481.45 387.65 490.54 382.4"/><polygon class="f" points="490.33 240.94 296.78 189.08 296.78 487.75 490.33 539.62 490.33 240.94"/><polygon class="h" points="386.94 387.36 314 367.78 314 238.33 386.94 257.91 386.94 387.36"/><line class="k" x1="402.02" y1="280.97" x2="355.33" y2="284.32"/><line class="k" x1="401.71" y1="316.74" x2="355.33" y2="291.74"/><polygon class="i" points="473.63 301.9 400.69 282.32 400.69 261.6 473.63 281.18 473.63 301.9"/><polygon class="j" points="473.63 337.63 400.69 318.05 400.69 297.32 473.63 316.9 473.63 337.63"/><polyline class="c" points="331.67 474.13 343.85 467.1 356.86 459.58"/><polyline class="c" points="183.35 313.8 207.44 299.89 231.58 285.96"/><polygon class="c" points="710.61 148.05 546.55 104.09 546.55 144.26 710.61 188.22 710.61 148.05"/><polygon class="c" points="710.61 215.05 546.55 171.09 546.55 211.26 710.61 255.22 710.61 215.05"/><line class="c" x1="162.68" y1="266.5" x2="533" y2="52.7"/><line class="c" x1="356.23" y1="617.04" x2="726.54" y2="403.24"/><polygon class="f" points="356.23 318.36 162.68 266.5 162.68 565.18 356.23 617.04 356.23 318.36"/><g class="l"><path class="m" d="M165.1,350.83c12-19.68,24-57.37,47,7s29,42,46,35.71,27,34.46,50,51.81,75,16.72,89,15.59"/><path class="n" d="M349.1,427.78c-9.13-13.05-13.7-31.26-31.2-9.54s-22.07,9.47-35,.86-20.55,6.69-38.05,6.89-67.08-25.38-77.73-30.61"/></g><polygon class="i" points="211.4 436.27 47.34 392.31 47.34 432.48 211.4 476.44 211.4 436.27"/><polygon class="o" points="211.4 436.27 47.34 392.31 47.34 432.48 211.4 476.44 211.4 436.27"/><polygon class="j" points="211.4 503.27 47.34 459.31 47.34 499.48 211.4 543.44 211.4 503.27"/><polygon class="o" points="211.4 503.27 47.34 459.31 47.34 499.48 211.4 543.44 211.4 503.27"/><line class="c" x1="513.15" y1="123.39" x2="544.97" y2="105.02"/><line class="c" x1="365.42" y1="208.68" x2="460.63" y2="153.71"/><line class="c" x1="231.58" y1="285.96" x2="312.69" y2="239.13"/><line class="c" x1="48.79" y1="391.49" x2="183.35" y2="313.8"/><line class="c" x1="636.44" y1="298.17" x2="709.92" y2="255.75"/><line class="c" x1="490.54" y1="382.4" x2="619.05" y2="308.21"/><line class="c" x1="356.86" y1="459.58" x2="467.58" y2="395.66"/><line class="c" x1="212.34" y1="543.03" x2="331.67" y2="474.13"/></svg> \ No newline at end of file
diff --git a/example/styleguide/res/templates/colors.html b/example/styleguide/res/templates/colors.html
deleted file mode 100644
index 47631b0cf..000000000
--- a/example/styleguide/res/templates/colors.html
+++ /dev/null
@@ -1,84 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2016, 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.
--->
-<div ng-init="colors = [
-{ 'category': 'Interface', 'description': 'Colors used in the application envelope, buttons and controls.', 'items': [{ 'name': 'Body Background', 'constant': '$colorBodyBg', 'valEspresso': '#333', 'valSnow': '#fcfcfc' },
-{ 'name': 'Body Foreground', 'constant': '$colorBodyFg', 'valEspresso': '#999', 'valSnow': '#666' },
-{ 'name': 'Key Color Background', 'constant': '$colorKey', 'valEspresso': '#0099cc', 'valSnow': '#0099cc' },
-{ 'name': 'Key Color Foreground', 'constant': '$colorKeyFg', 'valEspresso': '#fff', 'valSnow': '#fff' },
-{ 'name': 'Paused Color Background', 'constant': '$colorPausedBg', 'valEspresso': '#c56f01', 'valSnow': '#ff9900' },
-{ 'name': 'Paused Color Foreground', 'constant': '$colorPausedFg', 'valEspresso': '#fff', 'valSnow': '#fff' }]},
-{ 'category': 'Forms', 'description': 'Colors in forms, mainly to articulate validation status.', 'items': [{ 'name': 'Valid Entry', 'constant': '$colorFormValid', 'valEspresso': '#33cc33', 'valSnow': '#33cc33' },
-{ 'name': 'Errorenous Entry', 'constant': '$colorFormError', 'valEspresso': '#990000', 'valSnow': '#990000' },
-{ 'name': 'Invalid Entry', 'constant': '$colorFormInvalid', 'valEspresso': '#ff3300', 'valSnow': '#ff2200' }]},
-{ 'category': 'Application Status', 'description': 'Colors related to the status of application objects, such as a successful connection to a service.', 'items': [{ 'name': 'Alert Color', 'constant': '$colorAlert', 'valEspresso': '#ff3c00', 'valSnow': '#ff3c00' },
-{ 'name': 'Status Color Foreground', 'constant': '$colorStatusFg', 'valEspresso': '#ccc', 'valSnow': '#fff' },
-{ 'name': 'Default Status Color', 'constant': '$colorStatusDefault', 'valEspresso': '#ccc', 'valSnow': '#ccc' },
-{ 'name': 'Status: Informational Color', 'constant': '$colorStatusInfo', 'valEspresso': '#62ba72', 'valSnow': '#60ba7b' },
-{ 'name': 'Status: Alert Color', 'constant': '$colorStatusAlert', 'valEspresso': '#ffa66d', 'valSnow': '#ffb66c' },
-{ 'name': 'Status: Error Color', 'constant': '$colorStatusError', 'valEspresso': '#d4585c', 'valSnow': '#c96b68' }]},
-{ 'category': 'Telemetry Status', 'description': 'Telemetry status colors used to indicate limit violations and alarm states. Note that these colors should be reserved exclusively for this usage.', 'items': [{ 'name': 'Yellow Limit Color Background', 'constant': '$colorLimitYellowBg', 'valEspresso': 'rgba(255,170,0,0.3)', 'valSnow': 'rgba(255,170,0,0.3)' },
-{ 'name': 'Yellow Limit Color Icon', 'constant': '$colorLimitYellowIc', 'valEspresso': '#ffaa00', 'valSnow': '#ffaa00' },
-{ 'name': 'Red Limit Color Background', 'constant': '$colorLimitRedBg', 'valEspresso': 'rgba(255,0,0,0.3)', 'valSnow': 'rgba(255,0,0,0.3)' },
-{ 'name': 'Red Limit Color Icon', 'constant': '$colorLimitRedIc', 'valEspresso': 'red', 'valSnow': 'red' }]}
-]"></div>
-
-
-
-<div class="l-style-guide s-text">
- <p class="doc-title">Open MCT Style Guide</p>
- <h1>Colors</h1>
-
- <div class="l-section">
- <h2>Overview</h2>
- <p>In mission operations, color is used to convey meaning. Alerts, warnings and status conditions are by convention communicated with colors in the green, yellow and red families. Colors must also be reserved for use in plots. As a result, Open MCT uses color selectively and sparingly. Follow these guidelines:</p>
- <ul>
- <li>Don't use red, orange, yellow or green colors in any element that isn't conveying some kind of status information.</li>
- <li>Each theme has a key color (typically blue-ish) that should be used to emphasize interactive elements and important UI controls.</li>
- <li>Within each theme values are used to push elements back or bring them forward, lowering or raising them in visual importance.
- <span class="themed espresso">In this theme, Espresso, lighter colors are placed on a dark background. The lighter a color is, the more it comes toward the observer and is raised in importance.</span>
- <span class="themed snow">In this theme, Snow, darker colors are placed on a light background. The darker a color is, the more it comes toward the observer and is raised in importance.</span>
- </li>
- <li>For consistency, use a theme's pre-defined status colors.</li>
- </ul>
- </div>
-
- <div class="l-section" ng-repeat="colorSet in colors">
- <h2>{{ colorSet.category }}</h2>
- <p>{{ colorSet.description }}</p>
- <div class="items-holder grid">
- <div class="item swatch-item" ng-repeat="color in colorSet.items">
- <div class="h-swatch">
- <div class="swatch themed espresso" style="background-color: {{ color.valEspresso }}"></div>
- <div class="swatch themed snow" style="background-color: {{ color.valSnow }}"></div>
- </div>
- <table class="details">
- <tr><td class="label">Name</td><td class="value">{{color.name}}</td></tr>
- <tr><td class="label">SASS</td><td class="value">{{color.constant}}</td></tr>
- <tr><td class="label">Value</td><td class="value">
- <span class="themed espresso">{{color.valEspresso}}</span>
- <span class="themed snow">{{color.valSnow}}</span>
- </td></tr>
- </table>
- </div>
- </div>
- </div>
-</div> \ No newline at end of file
diff --git a/example/styleguide/res/templates/controls.html b/example/styleguide/res/templates/controls.html
deleted file mode 100644
index a29f82586..000000000
--- a/example/styleguide/res/templates/controls.html
+++ /dev/null
@@ -1,172 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2016, 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.
--->
-<div class="l-style-guide s-text">
- <p class="doc-title">Open MCT Style Guide</p>
- <h1>Controls</h1>
-
- <div class="l-section">
- <h2>Standard Buttons</h2>
- <div class="cols cols1-1">
- <div class="col">
- <p>Use a standard button in locations where there's sufficient room and you must make it clear that the element is an interactive button element. Buttons can be displayed with only an icon, only text, or with icon and text combined.</p>
- <p>Use an icon whenever possible to aid the user's recognition and recall. If both and icon and text are to be used, the text must be within a <code>span</code> with class <code>.title-label</code>.</p>
- </div>
-<mct-example><a class="s-button icon-pencil" title="Edit"></a>
-<a class="s-button" title="Edit">Edit</a>
-<a class="s-button icon-pencil" title="Edit">
- <span class="title-label">Edit</span>
-</a>
-</mct-example>
- </div>
- </div>
-
- <div class="l-section">
- <h2>&quot;Major&quot; Buttons</h2>
- <div class="cols cols1-1">
- <div class="col">
- <p>Major buttons allow emphasis to be placed on a button. Use this on a single button when the user has a small number of choices, and one choice is a normal default. Just add <code>.major</code> to any element that uses <code>.s-button</code>.</p>
- </div>
-<mct-example><a class="s-button major">Ok</a>
-<a class="s-button">Cancel</a>
-</mct-example>
- </div>
- </div>
-
- <div class="l-section">
- <h2>Button Sets</h2>
- <div class="cols cols1-1">
- <div class="col">
- <p>Use button sets to connect buttons that have related purpose or functionality. Buttons in a set round the outer corners of only the first and last buttons, any other buttons in the middle simply get division spacers.</p>
- <p>To use, simply wrap two or more <code>.s-button</code> elements within <code>.l-btn-set</code>.</p>
- </div>
-<mct-example><span class="l-btn-set">
- <a class="s-button icon-magnify"></a>
- <a class="s-button icon-magnify-in"></a>
- <a class="s-button icon-magnify-out"></a>
-</span>
-</mct-example>
- </div>
- </div>
-
- <div class="l-section">
- <h2>Icon-only Buttons</h2>
- <div class="cols cols1-1">
- <div class="col">
- <p>When a button is presented within another control it may be advantageous to avoid visual clutter by using an icon-only button. These type of controls present an icon without the &quot;base&quot; of standard buttons. Icon-only buttons should only be used in a context where they are clearly an interactive element and not an object-type identifier, and should not be used with text.</p>
- </div>
-<mct-example><a class="s-icon-button icon-pencil" title="Edit"></a>
- </mct-example>
- </div>
- </div>
-
- <div class="l-section">
- <h2>Checkboxes</h2>
- <div class="cols cols1-1">
- <div class="col">
- <p>Checkboxes use a combination of minimal additional markup with CSS to present a custom and common look-and-feel across platforms.</p>
- <p>The basic structure is a <code>label</code> with a checkbox-type input and an <code>em</code> element inside. The <code>em</code> is needed as the holder of the custom element; the input itself is hidden. Putting everything inside the <code>label</code> allows the label itself to act as a clickable element.</p>
- </div>
-<mct-example><label class="checkbox custom no-text">
- <input type="checkbox" />
- <em></em>
-</label>
-<br />
-<label class="checkbox custom no-text">
- <input type="checkbox" checked />
- <em></em>
-</label>
-<br />
-<label class="checkbox custom">Labeled checkbox
- <input type="checkbox" />
- <em></em>
-</label></mct-example>
- </div>
- </div>
-
- <div class="l-section">
- <h2>Radio Buttons</h2>
- <div class="cols cols1-1">
- <div class="col">
- <p>Radio buttons use the same technique as checkboxes above.</p>
- </div>
-<mct-example><label class="radio custom">Red
- <input name="Alarm Status" type="radio" />
- <em></em>
-</label>
-<br />
-<label class="radio custom">Orange
- <input name="Alarm Status" type="radio" checked />
- <em></em>
-</label>
-<br />
-<label class="radio custom">Yellow
- <input name="Alarm Status" type="radio" />
- <em></em>
-</label>
-</mct-example>
- </div>
- </div>
-
- <div class="l-section">
- <h2>Selects</h2>
- <div class="cols cols1-1">
- <div class="col">
- <p>Similar to checkboxes and radio buttons, selects use a combination of minimal additional markup with CSS to present a custom and common look-and-feel across platforms. The <code>select</code> element is wrapped by another element, such as a <code>div</code>, which acts as the main display element for the styling. The <code>select</code> provides the click and select functionality, while having all of its native look-and-feel suppressed.</p>
- </div>
-<mct-example><div class="select">
- <select>
- <option value="" selected="selected">- Select One -</option>
- <option value="Colussus">Colussus</option>
- <option value="HAL 9000">HAL 9000</option>
- <option value="Mother">Mother</option>
- <option value="Skynet">Skynet</option>
- </select>
-</div>
-</mct-example>
- </div>
- </div>
-
- <div class="l-section">
- <h2>Local Controls</h2>
- <div class="cols cols1-1">
- <div class="col">
- <p>Local controls are typically buttons and selects that provide actions in close proximity to a component.</p>
- <p>These controls can optionally be hidden to reduce clutter until the user hovers their cursor over an enclosing element. To use this approach, apply the class <code>.has-local-controls</code> to the element that should be aware of the hover and ensure that element encloses <code>.h-local-controls</code>.</p>
- </div>
- <mct-example><div class="plot-display-area" style="padding: 10px; position: relative;">
- Some content in here
- <div class="h-local-controls h-local-controls-overlay-content l-btn-set">
- <a class="s-button icon-arrow-left" title="Restore previous pan/zoom"></a>
- <a class="s-button icon-reset" title="Reset pan/zoom"></a>
- </div>
-</div>
-<div class="plot-display-area has-local-controls" style="padding: 10px; position: relative;">
- Hover here
- <div class="h-local-controls h-local-controls-overlay-content local-controls-hidden l-btn-set">
- <a class="s-button icon-arrow-left" title="Restore previous pan/zoom"></a>
- <a class="s-button icon-reset" title="Reset pan/zoom"></a>
- </div>
-</div></mct-example>
- </div>
- </div>
-
-</div>
diff --git a/example/styleguide/res/templates/glyphs.html b/example/styleguide/res/templates/glyphs.html
deleted file mode 100644
index f55e333a6..000000000
--- a/example/styleguide/res/templates/glyphs.html
+++ /dev/null
@@ -1,216 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2016, 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.
--->
-<div ng-init="general= [{ 'meaning': 'Pay attention', 'cssClass': 'icon-alert-rect', 'cssContent': 'e900', 'htmlEntity': '&amp;#xe900' },
-{ 'meaning': 'Warning', 'cssClass': 'icon-alert-triangle', 'cssContent': 'e901', 'htmlEntity': '&amp;#xe901' },
-{ 'meaning': 'Invoke menu', 'cssClass': 'icon-arrow-down', 'cssContent': 'e902', 'htmlEntity': '&amp;#xe902' },
-{ 'meaning': 'General usage arrow pointing left', 'cssClass': 'icon-arrow-left', 'cssContent': 'e903', 'htmlEntity': '&amp;#xe903' },
-{ 'meaning': 'General usage arrow pointing right', 'cssClass': 'icon-arrow-right', 'cssContent': 'e904', 'htmlEntity': '&amp;#xe904' },
-{ 'meaning': 'Upper limit, red', 'cssClass': 'icon-arrow-double-up', 'cssContent': 'e905', 'htmlEntity': '&amp;#xe905' },
-{ 'meaning': 'Upper limit, yellow', 'cssClass': 'icon-arrow-tall-up', 'cssContent': 'e906', 'htmlEntity': '&amp;#xe906' },
-{ 'meaning': 'Lower limit, yellow', 'cssClass': 'icon-arrow-tall-down', 'cssContent': 'e907', 'htmlEntity': '&amp;#xe907' },
-{ 'meaning': 'Lower limit, red', 'cssClass': 'icon-arrow-double-down', 'cssContent': 'e908', 'htmlEntity': '&amp;#xe908' },
-{ 'meaning': 'General usage arrow pointing up', 'cssClass': 'icon-arrow-up', 'cssContent': 'e909', 'htmlEntity': '&amp;#xe909' },
-{ 'meaning': 'Required form element', 'cssClass': 'icon-asterisk', 'cssContent': 'e910', 'htmlEntity': '&amp;#xe910' },
-{ 'meaning': 'Alert', 'cssClass': 'icon-bell', 'cssContent': 'e911', 'htmlEntity': '&amp;#xe911' },
-{ 'meaning': 'General usage box symbol', 'cssClass': 'icon-box', 'cssContent': 'e912', 'htmlEntity': '&amp;#xe912' },
-{ 'meaning': 'Click on or into', 'cssClass': 'icon-box-with-arrow', 'cssContent': 'e913', 'htmlEntity': '&amp;#xe913' },
-{ 'meaning': 'General usage checkmark, used in checkboxes; complete', 'cssClass': 'icon-check', 'cssContent': 'e914', 'htmlEntity': '&amp;#xe914' },
-{ 'meaning': 'Connected', 'cssClass': 'icon-connectivity', 'cssContent': 'e915', 'htmlEntity': '&amp;#xe915' },
-{ 'meaning': 'Status: DB connected', 'cssClass': 'icon-database-in-brackets', 'cssContent': 'e916', 'htmlEntity': '&amp;#xe916' },
-{ 'meaning': 'View or make visible', 'cssClass': 'icon-eye-open', 'cssContent': 'e917', 'htmlEntity': '&amp;#xe917' },
-{ 'meaning': 'Settings, properties', 'cssClass': 'icon-gear', 'cssContent': 'e918', 'htmlEntity': '&amp;#xe918' },
-{ 'meaning': 'Process, progress, time', 'cssClass': 'icon-hourglass', 'cssContent': 'e919', 'htmlEntity': '&amp;#xe919' },
-{ 'meaning': 'Info', 'cssClass': 'icon-info', 'cssContent': 'e920', 'htmlEntity': '&amp;#xe920' },
-{ 'meaning': 'Link (alias)', 'cssClass': 'icon-link', 'cssContent': 'e921', 'htmlEntity': '&amp;#xe921' },
-{ 'meaning': 'Locked', 'cssClass': 'icon-lock', 'cssContent': 'e922', 'htmlEntity': '&amp;#xe922' },
-{ 'meaning': 'General usage minus symbol; used in timer object', 'cssClass': 'icon-minus', 'cssContent': 'e923', 'htmlEntity': '&amp;#xe923' },
-{ 'meaning': 'An item that is shared', 'cssClass': 'icon-people', 'cssContent': 'e924', 'htmlEntity': '&amp;#xe924' },
-{ 'meaning': 'User profile or belonging to an individual', 'cssClass': 'icon-person', 'cssContent': 'e925', 'htmlEntity': '&amp;#xe925' },
-{ 'meaning': 'General usage plus symbol; used in timer object', 'cssClass': 'icon-plus', 'cssContent': 'e926', 'htmlEntity': '&amp;#xe926' },
-{ 'meaning': 'Delete', 'cssClass': 'icon-trash', 'cssContent': 'e927', 'htmlEntity': '&amp;#xe927' },
-{ 'meaning': 'Close, remove', 'cssClass': 'icon-x', 'cssContent': 'e928', 'htmlEntity': '&amp;#xe928' },
-{ 'meaning': 'Enclosing, inclusive; used in Time Conductor', 'cssClass': 'icon-brackets', 'cssContent': 'e929', 'htmlEntity': '&amp;#xe929' },
-{ 'meaning': 'Something is targeted', 'cssClass': 'icon-crosshair', 'cssContent': 'e930', 'htmlEntity': '&amp;#xe930' },
-{ 'meaning': 'Draggable', 'cssClass': 'icon-grippy', 'cssContent': 'e931', 'htmlEntity': '&amp;#xe931' }
-]; controls= [{ 'meaning': 'Reset zoom/pam', 'cssClass': 'icon-arrows-out', 'cssContent': 'e1000', 'htmlEntity': '&amp;#xe1000' },
-{ 'meaning': 'Expand vertically', 'cssClass': 'icon-arrows-right-left', 'cssContent': 'e1001', 'htmlEntity': '&amp;#xe1001' },
-{ 'meaning': 'View scrolling', 'cssClass': 'icon-arrows-up-down', 'cssContent': 'e1002', 'htmlEntity': '&amp;#xe1002' },
-{ 'meaning': 'Bullet; used in radio buttons', 'cssClass': 'icon-bullet', 'cssContent': 'e1004', 'htmlEntity': '&amp;#xe1004' },
-{ 'meaning': 'Invoke datetime picker', 'cssClass': 'icon-calendar', 'cssContent': 'e1005', 'htmlEntity': '&amp;#xe1005' },
-{ 'meaning': 'Web link', 'cssClass': 'icon-chain-links', 'cssContent': 'e1006', 'htmlEntity': '&amp;#xe1006' },
-{ 'meaning': 'Collapse left', 'cssClass': 'icon-collapse-pane-left', 'cssContent': 'e1007', 'htmlEntity': '&amp;#xe1007' },
-{ 'meaning': 'Collapse right', 'cssClass': 'icon-collapse-pane-right', 'cssContent': 'e1008', 'htmlEntity': '&amp;#xe1008' },
-{ 'meaning': 'Download', 'cssClass': 'icon-download', 'cssContent': 'e1009', 'htmlEntity': '&amp;#xe1009' },
-{ 'meaning': 'Copy/Duplicate', 'cssClass': 'icon-duplicate', 'cssContent': 'e1010', 'htmlEntity': '&amp;#xe1010' },
-{ 'meaning': 'New folder', 'cssClass': 'icon-folder-new', 'cssContent': 'e1011', 'htmlEntity': '&amp;#xe1011' },
-{ 'meaning': 'Exit fullscreen mode', 'cssClass': 'icon-fullscreen-collapse', 'cssContent': 'e1012', 'htmlEntity': '&amp;#xe1012' },
-{ 'meaning': 'Display fullscreen', 'cssClass': 'icon-fullscreen-expand', 'cssContent': 'e1013', 'htmlEntity': '&amp;#xe1013' },
-{ 'meaning': 'Layer order', 'cssClass': 'icon-layers', 'cssContent': 'e1014', 'htmlEntity': '&amp;#xe1014' },
-{ 'meaning': 'Line color', 'cssClass': 'icon-line-horz', 'cssContent': 'e1015', 'htmlEntity': '&amp;#xe1015' },
-{ 'meaning': 'Search', 'cssClass': 'icon-magnify', 'cssContent': 'e1016', 'htmlEntity': '&amp;#xe1016' },
-{ 'meaning': 'Zoom in', 'cssClass': 'icon-magnify-in', 'cssContent': 'e1017', 'htmlEntity': '&amp;#xe1017' },
-{ 'meaning': 'Zoom out', 'cssClass': 'icon-magnify-out', 'cssContent': 'e1018', 'htmlEntity': '&amp;#xe1018' },
-{ 'meaning': 'Menu', 'cssClass': 'icon-menu-hamburger', 'cssContent': 'e1019', 'htmlEntity': '&amp;#xe1019' },
-{ 'meaning': 'Move', 'cssClass': 'icon-move', 'cssContent': 'e1020', 'htmlEntity': '&amp;#xe1020' },
-{ 'meaning': 'Open in new window', 'cssClass': 'icon-new-window', 'cssContent': 'e1021', 'htmlEntity': '&amp;#xe1021' },
-{ 'meaning': 'Fill', 'cssClass': 'icon-paint-bucket', 'cssContent': 'e1022', 'htmlEntity': '&amp;#xe1022' },
-{ 'meaning': 'Pause real-time streaming', 'cssClass': 'icon-pause', 'cssContent': 'e1023', 'htmlEntity': '&amp;#xe1023' },
-{ 'meaning': 'Edit', 'cssClass': 'icon-pencil', 'cssContent': 'e1024', 'htmlEntity': '&amp;#xe1024' },
-{ 'meaning': 'Stop pause, resume real-time streaming', 'cssClass': 'icon-play', 'cssContent': 'e1025', 'htmlEntity': '&amp;#xe1025' },
-{ 'meaning': 'Plot resources', 'cssClass': 'icon-plot-resource', 'cssContent': 'e1026', 'htmlEntity': '&amp;#xe1026' },
-{ 'meaning': 'Previous', 'cssClass': 'icon-pointer-left', 'cssContent': 'e1027', 'htmlEntity': '&amp;#xe1027' },
-{ 'meaning': 'Next, navigate to', 'cssClass': 'icon-pointer-right', 'cssContent': 'e1028', 'htmlEntity': '&amp;#xe1028' },
-{ 'meaning': 'Refresh', 'cssClass': 'icon-refresh', 'cssContent': 'e1029', 'htmlEntity': '&amp;#xe1029' },
-{ 'meaning': 'Save', 'cssClass': 'icon-save', 'cssContent': 'e1030', 'htmlEntity': '&amp;#xe1030' },
-{ 'meaning': 'View plot', 'cssClass': 'icon-sine', 'cssContent': 'e1031', 'htmlEntity': '&amp;#xe1031' },
-{ 'meaning': 'Text color', 'cssClass': 'icon-T', 'cssContent': 'e1032', 'htmlEntity': '&amp;#xe1032' },
-{ 'meaning': 'Image thumbs strip; view items grid', 'cssClass': 'icon-thumbs-strip', 'cssContent': 'e1033', 'htmlEntity': '&amp;#xe1033' },
-{ 'meaning': 'Two part item, both parts', 'cssClass': 'icon-two-parts-both', 'cssContent': 'e1034', 'htmlEntity': '&amp;#xe1034' },
-{ 'meaning': 'Two part item, one only', 'cssClass': 'icon-two-parts-one-only', 'cssContent': 'e1035', 'htmlEntity': '&amp;#xe1035' },
-{ 'meaning': 'Resync', 'cssClass': 'icon-resync', 'cssContent': 'e1036', 'htmlEntity': '&amp;#xe1036' },
-{ 'meaning': 'Reset', 'cssClass': 'icon-reset', 'cssContent': 'e1037', 'htmlEntity': '&amp;#xe1037' },
-{ 'meaning': 'Clear', 'cssClass': 'icon-x-in-circle', 'cssContent': 'e1038', 'htmlEntity': '&amp;#xe1038' },
-{ 'meaning': 'Brightness', 'cssClass': 'icon-brightness', 'cssContent': 'e1039', 'htmlEntity': '&amp;#xe1039' },
-{ 'meaning': 'Contrast', 'cssClass': 'icon-contrast', 'cssContent': 'e1040', 'htmlEntity': '&amp;#xe1040' },
-{ 'meaning': 'Expand', 'cssClass': 'icon-expand', 'cssContent': 'e1041', 'htmlEntity': '&amp;#xe1041' },
-{ 'meaning': 'View items in a tabular list', 'cssClass': 'icon-list-view', 'cssContent': 'e1042', 'htmlEntity': '&amp;#xe1042' },
-{ 'meaning': 'Snap an object corner to a grid', 'cssClass': 'icon-grid-snap-to', 'cssContent': 'e1043', 'htmlEntity': '&amp;#xe1043' },
-{ 'meaning': 'Do not snap an object corner to a grid', 'cssClass': 'icon-grid-snap-no', 'cssContent': 'e1044', 'htmlEntity': '&amp;#xe1044' },
-{ 'meaning': 'Show an object frame in a Display Layout', 'cssClass': 'icon-frame-show', 'cssContent': 'e1045', 'htmlEntity': '&amp;#xe1045' },
-{ 'meaning': 'Do not show an object frame in a Display Layout', 'cssClass': 'icon-frame-hide', 'cssContent': 'e1046', 'htmlEntity': '&amp;#xe1046' }
-]; objects= [{ 'meaning': 'Activity', 'cssClass': 'icon-activity', 'cssContent': 'e1100', 'htmlEntity': '&amp;#xe1100' },
-{ 'meaning': 'Activity Mode', 'cssClass': 'icon-activity-mode', 'cssContent': 'e1101', 'htmlEntity': '&amp;#xe1101' },
-{ 'meaning': 'Auto-flow Tabular view', 'cssClass': 'icon-autoflow-tabular', 'cssContent': 'e1102', 'htmlEntity': '&amp;#xe1102' },
-{ 'meaning': 'Clock object type', 'cssClass': 'icon-clock', 'cssContent': 'e1103', 'htmlEntity': '&amp;#xe1103' },
-{ 'meaning': 'Database', 'cssClass': 'icon-database', 'cssContent': 'e1104', 'htmlEntity': '&amp;#xe1104' },
-{ 'meaning': 'Data query', 'cssClass': 'icon-database-query', 'cssContent': 'e1105', 'htmlEntity': '&amp;#xe1105' },
-{ 'meaning': 'Data Set domain object', 'cssClass': 'icon-dataset', 'cssContent': 'e1106', 'htmlEntity': '&amp;#xe1106' },
-{ 'meaning': 'Datatable, channel table', 'cssClass': 'icon-datatable', 'cssContent': 'e1107', 'htmlEntity': '&amp;#xe1107' },
-{ 'meaning': 'Dictionary', 'cssClass': 'icon-dictionary', 'cssContent': 'e1108', 'htmlEntity': '&amp;#xe1108' },
-{ 'meaning': 'Folder', 'cssClass': 'icon-folder', 'cssContent': 'e1109', 'htmlEntity': '&amp;#xe1109' },
-{ 'meaning': 'Imagery', 'cssClass': 'icon-image', 'cssContent': 'e1110', 'htmlEntity': '&amp;#xe1110' },
-{ 'meaning': 'Display Layout', 'cssClass': 'icon-layout', 'cssContent': 'e1111', 'htmlEntity': '&amp;#xe1111' },
-{ 'meaning': 'Generic Object', 'cssClass': 'icon-object', 'cssContent': 'e1112', 'htmlEntity': '&amp;#xe1112' },
-{ 'meaning': 'Unknown object type', 'cssClass': 'icon-object-unknown', 'cssContent': 'e1113', 'htmlEntity': '&amp;#xe1113' },
-{ 'meaning': 'Packet domain object', 'cssClass': 'icon-packet', 'cssContent': 'e1114', 'htmlEntity': '&amp;#xe1114' },
-{ 'meaning': 'Page', 'cssClass': 'icon-page', 'cssContent': 'e1115', 'htmlEntity': '&amp;#xe1115' },
-{ 'meaning': 'Overlay plot', 'cssClass': 'icon-plot-overlay', 'cssContent': 'e1116', 'htmlEntity': '&amp;#xe1116' },
-{ 'meaning': 'Stacked plot', 'cssClass': 'icon-plot-stacked', 'cssContent': 'e1117', 'htmlEntity': '&amp;#xe1117' },
-{ 'meaning': 'Session object', 'cssClass': 'icon-session', 'cssContent': 'e1118', 'htmlEntity': '&amp;#xe1118' },
-{ 'meaning': 'Table', 'cssClass': 'icon-tabular', 'cssContent': 'e1119', 'htmlEntity': '&amp;#xe1119' },
-{ 'meaning': 'Latest available data object', 'cssClass': 'icon-tabular-lad', 'cssContent': 'e1120', 'htmlEntity': '&amp;#xe1120' },
-{ 'meaning': 'Latest available data set', 'cssClass': 'icon-tabular-lad-set', 'cssContent': 'e1121', 'htmlEntity': '&amp;#xe1121' },
-{ 'meaning': 'Real-time table view', 'cssClass': 'icon-tabular-realtime', 'cssContent': 'e1122', 'htmlEntity': '&amp;#xe1122' },
-{ 'meaning': 'Real-time scrolling table', 'cssClass': 'icon-tabular-scrolling', 'cssContent': 'e1123', 'htmlEntity': '&amp;#xe1123' },
-{ 'meaning': 'Telemetry element', 'cssClass': 'icon-telemetry', 'cssContent': 'e1124', 'htmlEntity': '&amp;#xe1124' },
-{ 'meaning': 'Telemetry Panel object', 'cssClass': 'icon-telemetry-panel', 'cssContent': 'e1125', 'htmlEntity': '&amp;#xe1125' },
-{ 'meaning': 'Timeline object', 'cssClass': 'icon-timeline', 'cssContent': 'e1126', 'htmlEntity': '&amp;#xe1126' },
-{ 'meaning': 'Timer object', 'cssClass': 'icon-timer', 'cssContent': 'e1127', 'htmlEntity': '&amp;#xe1127' },
-{ 'meaning': 'Data Topic', 'cssClass': 'icon-topic', 'cssContent': 'e1128', 'htmlEntity': '&amp;#xe1128' },
-{ 'meaning': 'Fixed Position object', 'cssClass': 'icon-box-with-dashed-lines', 'cssContent': 'e1129', 'htmlEntity': '&amp;#xe1129' },
-{ 'meaning': 'Summary Widget', 'cssClass': 'icon-summary-widget', 'cssContent': 'e1130', 'htmlEntity': '&amp;#xe1130' },
-{ 'meaning': 'Notebook object', 'cssClass': 'icon-notebook', 'cssContent': 'e1131', 'htmlEntity': '&amp;#xe1131' }
-];
-"></div>
-
-<div class="l-style-guide s-text">
- <p class="doc-title">Open MCT Style Guide</p>
- <h1>Glyphs</h1>
- <div class="l-section">
- <p>Symbolic glyphs are used extensively in Open MCT to call attention to interactive elements, identify objects, and aid in visual recall. Glyphs are made available in a custom symbols font, and have associated CSS classes for their usage. Using a font in this way (versus using images or sprites) has advantages in that each symbol is in effect a scalable vector that can be sized up or down as needed. Color can also quite easily be applied via CSS.</p>
- <p>New glyphs can be added if needed. Take care to observe the following guidelines:
- <ul>
- <li>Symbols should be created at 512 pixels high, and no more than 512 pixels wide. This size is based on a &quot;crisp&quot; 16px approach. Find out more about <a class="link" target="_blank" href="http://asimpleframe.com/writing/custom-icon-font-tutorial-icomoon">crisp symbol fonts</a>.</li>
- <li>In general, the symbol should occupy most of a square area as possible; avoid symbol aspect ratios that are squat or tall.</li>
- <li>For consistency and legibility, symbols are designed as mostly solid shapes. Avoid using thin lines or fine detail that will be lost when the icon is sized down. In general, no stroke should be less than 32 pixels.</li>
- <li>Symbols should be legible down to a minimum of 12 x 12 pixels.</li>
-
- </ul>
- </p>
- </div>
-
- <div class="l-section">
- <h2>How to Use Glyphs</h2>
- <div class="cols cols1-1">
- <div class="col">
- <p>The easiest way to use a glyph is to include its CSS class in an element. The CSS adds a pseudo <code>:before</code> HTML element to whatever element it's attached to that makes proper use of the symbols font.</p>
- <p>Alternately, you can use the <code>.ui-symbol</code> class in an object that contains encoded HTML entities. This method is only recommended if you cannot use the aforementioned CSS class approach.</p>
- </div>
- <mct-example><a class="s-button icon-gear" title="Settings"></a>
-<br /><br />
-<a class="s-icon-button icon-gear" title="Settings"></a>
-<br /><br />
-<div class="ui-symbol">&#xe901 &#xe914 &#xe922</div>
-</mct-example>
- </div>
- </div>
-
- <div class="l-section">
- <h2>General User Interface Glyphs</h2>
- <p>Glyphs suitable for denoting general user interface verbs and nouns.</p>
- <div class="items-holder grid">
- <div class="item glyph-item" ng-repeat="glyph in general">
- <div class="glyph" ng-class="glyph.cssClass"></div>
- <table class="details">
- <tr><td class="label">Class</td><td class="value">.{{glyph.cssClass}}</td></tr>
- <tr><td class="label">Meaning</td><td class="value">{{glyph.meaning}}</td></tr>
- <tr><td class="label">CSS Content</td><td class="value">\{{glyph.cssContent}}</td></tr>
- <tr><td class="label">HTML Entity</td><td class="value">{{glyph.htmlEntity}}</td></tr>
- </table>
- </div>
- </div>
- </div>
-
- <div class="l-section">
- <h2>Control Glyphs</h2>
- <p>Glyphs created for use in various controls.</p>
- <div class="items-holder grid">
- <div class="item glyph-item" ng-repeat="glyph in controls">
- <div class="glyph" ng-class="glyph.cssClass"></div>
- <table class="details">
- <tr><td class="label">Class</td><td class="value">.{{glyph.cssClass}}</td></tr>
- <tr><td class="label">Meaning</td><td class="value">{{glyph.meaning}}</td></tr>
- <tr><td class="label">CSS Content</td><td class="value">\{{glyph.cssContent}}</td></tr>
- <tr><td class="label">HTML Entity</td><td class="value">{{glyph.htmlEntity}}</td></tr>
- </table>
- </div>
- </div>
- </div>
-
- <div class="l-section">
- <h2>Object Type Glyphs</h2>
- <p>These glyphs are reserved exclusively to denote types of objects in the application. Only use them if you are referring to a pre-existing object type.</p>
- <div class="items-holder grid">
- <div class="item glyph-item" ng-repeat="glyph in objects">
- <div class="glyph" ng-class="glyph.cssClass"></div>
- <table class="details">
- <tr><td class="label">Class</td><td class="value">.{{glyph.cssClass}}</td></tr>
- <tr><td class="label">Meaning</td><td class="value">{{glyph.meaning}}</td></tr>
- <tr><td class="label">CSS Content</td><td class="value">\{{glyph.cssContent}}</td></tr>
- <tr><td class="label">HTML Entity</td><td class="value">{{glyph.htmlEntity}}</td></tr>
- </table>
- </div>
- </div>
- </div>
-
-</div>
-
diff --git a/example/styleguide/res/templates/input.html b/example/styleguide/res/templates/input.html
deleted file mode 100644
index eae33098b..000000000
--- a/example/styleguide/res/templates/input.html
+++ /dev/null
@@ -1,75 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2016, 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.
--->
-<div class="l-style-guide s-text">
- <p class="doc-title">Open MCT Style Guide</p>
- <h1>Text Input</h1>
- <div class="l-section">
- <p>Text inputs and textareas have a consistent look-and-feel across the application. The input's <code>placeholder</code> attribute is styled to appear visually different from an entered value.</p>
- </div>
-
- <div class="l-section">
- <h2>Text Inputs</h2>
- <div class="cols cols1-1">
- <div class="col">
- <p>Use a text input where the user should enter relatively short text entries.</p>
- <p>A variety of size styles are available: <code>.lg</code>, <code>.med</code> and <code>.sm</code>. <code>.lg</code> text inputs dynamically scale their width to 100% of their container's width. Numeric inputs that benefit from right-alignment can be styled by adding <code>.numeric</code>.</p>
- </div>
-<mct-example><input type="text" placeholder="Enter a value" />
-<br /><br />
-<input type="text" placeholder="Enter a value" value="An entered value" />
-<br /><br />
-<input type="text" placeholder="Enter a value" class="sm" value="Small" />
-<br /><br />
-<input type="text" placeholder="Enter a value" class="med" value="A medium input" />
-<br /><br />
-<input type="text" placeholder="Enter a value" class="lg" value="A large input" />
-<br /><br />
-<input type="text" placeholder="Enter a value" class="sm numeric" value="10.9" />
-</mct-example>
- </div>
- </div>
-
- <div class="l-section">
- <h2>Textareas</h2>
- <div class="cols cols1-1">
- <div class="col">
- <p>Use a textarea where the user should enter relatively longer or multi-line text entries.</p>
- <p>By default, textareas are styled to expand to 100% of the width and height of their container; additionally there are three size styles available that control the height of the element: <code>.lg</code>, <code>.med</code> and <code>.sm</code>.</p>
- </div>
-<mct-example><div style="position: relative; height: 100px">
- <textarea placeholder="Enter a value"></textarea>
-</div>
-<br />
-<div style="position: relative; height: 100px">
- <textarea placeholder="Enter a value">An entered value</textarea>
-</div>
-<br /><br />
-<textarea placeholder="Enter a value" class="sm">A small textarea</textarea>
-<br /><br />
-<textarea placeholder="Enter a value" class="med">A medium textarea</textarea>
-<br /><br />
-<textarea placeholder="Enter a value" class="lg">A large textarea</textarea>
-</mct-example>
- </div>
- </div>
-</div>
-
diff --git a/example/styleguide/res/templates/intro.html b/example/styleguide/res/templates/intro.html
deleted file mode 100644
index 72fbe4bb4..000000000
--- a/example/styleguide/res/templates/intro.html
+++ /dev/null
@@ -1,73 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2016, 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.
--->
-<div class="l-style-guide s-text">
- <p class="doc-title">Open MCT Style Guide</p>
- <h1>Introduction</h1>
- <div class="l-section">
- <p>Open MCT is a robust, extensible telemetry monitoring and situational awareness system that provides a framework supporting fast and efficient multi-mission deployment. This guide will explore the major concepts and design elements of Open MCT. Its overall goal is to guide you in creating new features and plugins that seamlessly integrate with the base application.</p>
- </div>
-
- <div class="l-section">
- <h2>Everything Is An Object</h2>
- <div class="cols cols1-1">
- <div class="col">
- <p>First and foremost, Open MCT uses a “object-oriented” approach: everything in the system is an object. Objects come in different types, and some objects can contain other objects of given types. This is similar to how the file management system of all modern computers works: a folder object can contain any other type of object, a presentation file can contain an image. This is conceptually the same in Open MCT.</p>
- <p>As you develop plugins for Open MCT, consider how a generalized component might be combined with others when designing to create a rich and powerful larger object, rather than adding a single monolithic, non-modular plugin. To solve a particular problem or allow a new feature in Open MCT, you may need to introduce more than just one new object type.</p>
- </div>
- <div class="col">
- <img src="../images/diagram-objects.svg" />
- </div>
- </div>
- </div>
-
- <div class="l-section">
- <h2>Object Types</h2>
- <div class="cols cols1-1">
- <div class="col">
- <p>In the same way that different types of files might be opened and edited by different applications, objects in Open MCT also have different types. For example, a Display Layout provides a way that other objects that display information can be combined and laid out in a canvas area to create a recallable display that suits the needs of the user that created it. A Telemetry Panel allows a user to collect together Telemetry Points and visualize them as a plot or a table.</p>
- <p>Object types provide a containment model that guides the user in their choices while creating a new object, and allows view normalization when flipping between different views. When a given object may only contain other objects of certain types, advantages emerge: the result of adding new objects is more predictable, more alternate views can be provided because the similarities between the contained objects is close, and we can provide more helpful and pointed guidance to the user because we know what types of objects they might be working with at a given time.</p>
- <p>The types of objects that a container can hold should be based on the purpose of the container and the views that it affords. For example, a Folder’s purpose is to allow a user to conceptually organize objects of all other types; a Folder must therefore be able to contain an object of any type.</p>
- </div>
- <div class="col">
- <img src="../images/diagram-containment.svg" />
- </div>
- </div>
- </div>
-
- <div class="l-section">
- <h2>Object Views</h2>
- <div class="cols cols1-1">
- <div class="col">
- <p>Views are simply different ways to view the content of a given object. For example, telemetry data could be viewed as a plot or a table. A clock can display its time in analog fashion or with digital numbers. In each view, all of the content is present; it’s just represented differently. When providing views for an object, all the content of the object should be present in each view.</p>
- </div>
- <div class="col">
- <img src="../images/diagram-views.svg" />
- </div>
- </div>
- </div>
-
-
-
- <p></p>
- <p></p>
- <p></p>
-</div>
diff --git a/example/styleguide/res/templates/mct-example.html b/example/styleguide/res/templates/mct-example.html
deleted file mode 100644
index a7fb0b5d0..000000000
--- a/example/styleguide/res/templates/mct-example.html
+++ /dev/null
@@ -1,8 +0,0 @@
-<div class="col">
- <h3>Markup</h3>
- <span class="w-markup">
- <pre></pre>
- </span>
- <h3>Example</h3>
- <div class="w-mct-example"></div>
-</div>
diff --git a/example/styleguide/res/templates/menus.html b/example/styleguide/res/templates/menus.html
deleted file mode 100644
index a18a10d21..000000000
--- a/example/styleguide/res/templates/menus.html
+++ /dev/null
@@ -1,168 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2016, 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.
--->
-<div class="l-style-guide s-text">
- <p class="doc-title">Open MCT Style Guide</p>
- <h1>Menus</h1>
-
- <div class="l-section">
- <h2>Context Menus</h2>
- <div class="cols cols1-1">
- <div class="col">
- <p>Context menus are used extensively in Open MCT. They are created dynamically upon a contextual click and positioned at the user's cursor position coincident with the element that invoked them. Context menus must use absolute position and utilize a z-index that places them above other in-page elements.</p>
- <p>See <a class="link" href="http://localhost:8080/#/browse/styleguide:home/controls?view=styleguide.standards">User Interface Standards</a> for more details on z-indexing in Open MCT. Context menus should be destroyed if the user clicks outside the menu element.</p>
- </div>
- <mct-example><div style="height: 120px">
- <div class="menu-element context-menu-wrapper mobile-disable-select">
- <div class="menu context-menu">
- <ul>
- <li onclick="alert('Perform an action')" title="Open in a new browser tab" class="icon-new-window">Open In New Tab</li>
- <li onclick="alert('Perform an action')" title="Remove this object from its containing object." class="icon-trash">Remove</li>
- <li onclick="alert('Perform an action')" title="Create Link to object in another location." class="icon-link">Create Link</li>
- </ul>
- </div>
- </div>
-</div></mct-example>
- </div>
- </div>
-
- <div class="l-section">
- <h2>Dropdown Menus</h2>
- <div class="cols cols1-1">
- <div class="col">
- <p>Dropdown menus are a dedicated, more discoverable context menu for a given object. Like context menus, dropdown menus are used extensively in Open MCT, and are most often associated with object header elements. They visually manifest as a downward pointing arrow <span class="context-available"></span> associated with an element, and when clicked displays a context menu at that location. See guidelines above about context menus in regards to z-indexing and element lifecycle.</p>
- <p>Use a dropdown menu to encapsulate important the actions of an object in the object's header, or in a place that you'd use a context menu, but want to make the availability of the menu more apparent.</p>
- </div>
-<mct-example><div style="height: 220px" title="Ignore me, I'm just here to provide space for this example.">
-
-<div class="l-flex-row flex-elem grows object-header">
- <span class="type-icon flex-elem icon-layout"></span>
- <span class="l-elem-wrapper l-flex-row flex-elem grows">
- <span class="title-label flex-elem holder flex-can-shrink ng-binding">Object Header</span>
- <span class="flex-elem context-available-w">
- <span ng-controller="MenuArrowController as menuArrow" class="ng-scope">
- <a class="context-available" ng-click="menuArrow.showMenu($event)"></a>
- </span>
- </span>
- </span>
-</div>
-
-</div></mct-example>
- </div>
- </div>
-
- <div class="l-section">
- <h2>Checkbox Menus</h2>
- <div class="cols cols1-1">
- <div class="col">
- <p>Checkbox menus add checkbox options to each item of a dropdown menu. Use this to </p>
- <p>Use a dropdown menu to encapsulate important the actions of an object in the object's header, or in a place that you'd use a context menu, but want to make the availability of the menu more apparent.</p>
- </div>
-<mct-example><div style="height: 220px" title="Ignore me, I'm just here to provide space for this example.">
-<div ng-controller="SearchMenuController as controller" class="ng-scope">
- <div class="menu checkbox-menu" mct-click-elsewhere="parameters.menuVisible(false)">
- <ul>
- <!-- First element is special - it's a reset option -->
- <li class="search-menu-item special icon-asterisk" title="Select all filters" ng-click="ngModel.checkAll = !ngModel.checkAll; controller.checkAll()">
- <label class="checkbox custom no-text">
- <input type="checkbox" class="checkbox ng-untouched ng-valid ng-dirty" ng-model="ngModel.checkAll" ng-change="controller.checkAll()">
- <em></em>
- </label>
- All
- </li>
- <li class="search-menu-item icon-folder">
- <label class="checkbox custom no-text">
- <input type="checkbox" class="checkbox">
- <em></em>
- </label>
- Folder
- </li>
- <li class="search-menu-item icon-layout">
- <label class="checkbox custom no-text">
- <input type="checkbox" class="checkbox">
- <em></em>
- </label>
- Display Layout
- </li>
- <li class="search-menu-item icon-box-with-dashed-lines">
- <label class="checkbox custom no-text">
- <input type="checkbox" class="checkbox">
- <em></em>
- </label>
- Fixed Position Display
- </li>
- </ul>
- </div>
-</div>
-</div>
-</mct-example>
- </div>
-</div>
-
- <div class="l-section">
- <h2>Palettes</h2>
- <div class="cols cols1-1">
- <div class="col">
- <p>Use a palette to provide color choices. Similar to context menus and dropdowns, palettes should be dismissed when a choice is made within them, or if the user clicks outside one. Selected palette choices should utilize the <code>selected</code> CSS class to visualize indicate that state.</p>
- <p>Note that while this example uses static markup for illustrative purposes, don't do this - use a front-end framework with repeaters to build the color choices.</p>
- </div>
- <mct-example><div style="height: 220px" title="Ignore me, I'm just here to provide space for this example.">
-
-<div class="s-button s-menu-button menu-element t-color-palette icon-paint-bucket" ng-controller="ClickAwayController as toggle">
- <span class="l-click-area" ng-click="toggle.toggle()"></span>
- <span class="color-swatch" style="background: rgb(255, 0, 0);"></span>
- <div class="menu l-palette l-color-palette" ng-show="toggle.isActive()">
- <div class="l-palette-row l-option-row">
- <div class="l-palette-item s-palette-item no-selection"></div>
- <span class="l-palette-item-label">None</span>
- </div>
- <div class="l-palette-row">
- <div class="l-palette-item s-palette-item" style="background: rgb(0, 0, 0);"></div>
- <div class="l-palette-item s-palette-item" style="background: rgb(28, 28, 28);"></div>
- <div class="l-palette-item s-palette-item" style="background: rgb(57, 57, 57);"></div>
- <div class="l-palette-item s-palette-item" style="background: rgb(85, 85, 85);"></div>
- <div class="l-palette-item s-palette-item" style="background: rgb(113, 113, 113);"></div>
- <div class="l-palette-item s-palette-item" style="background: rgb(142, 142, 142);"></div>
- <div class="l-palette-item s-palette-item" style="background: rgb(170, 170, 170);"></div>
- <div class="l-palette-item s-palette-item" style="background: rgb(198, 198, 198);"></div>
- <div class="l-palette-item s-palette-item" style="background: rgb(227, 227, 227);"></div>
- <div class="l-palette-item s-palette-item" style="background: rgb(255, 255, 255);"></div>
- </div>
- <div class="l-palette-row">
- <div class="l-palette-item s-palette-item selected" style="background: rgb(255, 0, 0);"></div>
- <div class="l-palette-item s-palette-item" style="background: rgb(224, 64, 64);"></div>
- <div class="l-palette-item s-palette-item" style="background: rgb(240, 160, 72);"></div>
- <div class="l-palette-item s-palette-item" style="background: rgb(255, 248, 96);"></div>
- <div class="l-palette-item s-palette-item" style="background: rgb(128, 240, 72);"></div>
- <div class="l-palette-item s-palette-item" style="background: rgb(128, 248, 248);"></div>
- <div class="l-palette-item s-palette-item" style="background: rgb(88, 144, 224);"></div>
- <div class="l-palette-item s-palette-item" style="background: rgb(0, 72, 240);"></div>
- <div class="l-palette-item s-palette-item" style="background: rgb(136, 80, 240);"></div>
- <div class="l-palette-item s-palette-item" style="background: rgb(224, 96, 248);"></div>
- </div>
- </div>
-</div>
-
-</div></mct-example>
- </div>
- </div>
-
-</div>
diff --git a/example/styleguide/res/templates/standards.html b/example/styleguide/res/templates/standards.html
deleted file mode 100644
index 675e69c73..000000000
--- a/example/styleguide/res/templates/standards.html
+++ /dev/null
@@ -1,48 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2016, 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.
--->
-<div class="l-style-guide s-text">
- <p class="doc-title">Open MCT Style Guide</p>
- <h1>Standards</h1>
-
- <div class="l-section">
- <h2>Absolute Positioning and Z-Indexing</h2>
- <p>Absolute positioning is used in Open MCT in the main envelope interface to handle layout and draggable pane splitters, for elements that must be dynamically created and positioned (like context menus) and for buttons that are placed over other elements, such as a plot's zoom/pan history and reset buttons. When using absolute positioning, follow these guidelines:</p>
- <ul>
- <li>Don't specify a z-index if you don't have to.</li>
- <li>If you must specify a z-index, use the lowest number you that prevents your element from being covered and puts it at the correct level per the table below.</li>
- </ul>
- <!-- This content maintained at https://docs.google.com/spreadsheets/d/1AzhUY0P3hLCfT8yPa2Cb1dwOOsQXBuSgCrOkhIoVm0A/edit#gid=0 -->
- <table>
- <tr class='header'><td>Type</td><td>Description</td><td>Z-index Range</td></tr>
- <tr><td>Base interface items</td><td>Base level elements</td><td>0 - 1</td></tr>
- <tr><td>Primary pane</td><td>Elements in the primary "view area" pane</td><td>2</td></tr>
- <tr><td>Inspector pane, splitters</td><td>Elements in the Inspector, and splitters themselves</td><td>3</td></tr>
- <tr><td>More base interface stuff</td><td>Base level elements</td><td>4 - 9</td></tr>
- <tr><td>Treeview</td><td>Lefthand treeview elements</td><td>30 - 39</td></tr>
- <tr><td>Help bubbles, rollover hints</td><td>Infobubbles, and similar</td><td>50 - 59</td></tr>
- <tr><td>Context, button and dropdown menus</td><td>Context menus, button menus, etc. that must overlay other elements</td><td>70 - 79</td></tr>
- <tr><td>Overlays</td><td>Modal overlay displays</td><td>100 - 109</td></tr>
- <tr><td>Event messages</td><td>Alerts, event dialogs</td><td>1000</td></tr>
- </table>
- </div>
-
-</div> \ No newline at end of file
diff --git a/example/styleguide/res/templates/status.html b/example/styleguide/res/templates/status.html
deleted file mode 100644
index 3680c3e65..000000000
--- a/example/styleguide/res/templates/status.html
+++ /dev/null
@@ -1,227 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2016, 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.
--->
-<style>
- .w-mct-example div[class*="s-status"],
- .w-mct-example span[class*="s-status"],
- .w-mct-example div[class*="s-limit"],
- .w-mct-example span[class*="s-limit"] {
- border-radius: 3px;
- padding: 2px 5px;
- }
- .w-mct-example table {
- width: 100%;
- }
-</style>
-<div class="l-style-guide s-text">
- <p class="doc-title">Open MCT Style Guide</p>
- <h1>Status Indication</h1>
-
- <div class="l-section">
- <h2>Status Classes</h2>
- <div class="cols cols1-1">
- <div class="col">
- <p>Status classes allow any block or inline-block element to be decorated in order to articulate a
- status. Provided classes include color-only and color plus icon; custom icons can easily be
- employed by using a color-only status class in combination with an <a class="link" href="#/browse/styleguide:home/glyphs?tc.mode=local&tc.timeSystem=utc&tc.startDelta=1800000&tc.endDelta=0&view=styleguide.glyphs">glyph</a>.</p>
- <ul>
- <li>Color only</li>
- <ul>
- <li><code>s-status-warning-hi</code></li>
- <li><code>s-status-warning-lo</code></li>
- <li><code>s-status-diagnostic</code></li>
- <li><code>s-status-info</code></li>
- <li><code>s-status-ok</code></li>
- </ul>
- <li>Color and icon</li>
- <ul>
- <li><code>s-status-icon-warning-hi</code></li>
- <li><code>s-status-icon-warning-lo</code></li>
- <li><code>s-status-icon-diagnostic</code></li>
- <li><code>s-status-icon-info</code></li>
- <li><code>s-status-icon-ok</code></li>
- </ul>
- </ul>
- </div>
-<mct-example><!-- Color alone examples -->
-<div class="s-status-warning-hi">WARNING HI</div>
-<div class="s-status-warning-lo">WARNING LOW</div>
-<div class="s-status-diagnostic">DIAGNOSTIC</div>
-<div class="s-status-info">INFO</div>
-<div class="s-status-ok">OK</div>
-
-<!-- Color and icon examples -->
-<div class="s-status-icon-warning-hi">WARNING HI with icon</div>
-<div class="s-status-icon-warning-lo">WARNING LOW with icon</div>
-<div class="s-status-icon-diagnostic">DIAGNOSTIC with icon</div>
-<div class="s-status-icon-info">INFO with icon</div>
-<div class="s-status-icon-ok">OK with icon</div>
-<div class="s-status-warning-hi icon-alert-triangle">WARNING HI with custom icon</div>
-<div>Some text with an <span class="s-status-icon-diagnostic">inline element</span> showing a Diagnostic status.</div>
-</mct-example>
- </div>
- </div>
-
- <div class="l-section">
- <h2>Limit Classes</h2>
- <div class="cols cols1-1">
- <div class="col">
- <p>Limit classes are a specialized form of status, specifically meant to be applied to telemetry
- displays to indicate that a limit threshold has been violated. Open MCT provides both severity
- and direction classes; severity (yellow and red) can be used alone or in combination
- with direction (upper or lower). Direction classes cannot be used on their own.</p>
- <p>Like Status classes, Limits can be used as color-only, or color plus icon. Custom icons can
- be applied in the same fashion as described above.</p>
- <ul>
- <li>Severity color alone</li>
- <ul>
- <li><code>s-limit-yellow</code>: A yellow limit.</li>
- <li><code>s-limit-red</code>: A red limit.</li>
- </ul>
- <li>Severity color and icon</li>
- <ul>
- <li><code>s-limit-icon-yellow</code>: A yellow limit with icon.</li>
- <li><code>s-limit-icon-red</code>: A red limit with icon.</li>
- </ul>
- <li>Direction indicators. MUST be used with a &quot;color alone&quot; limit class. See
- examples for more.</li>
- <ul>
- <li><code>s-limit-upr</code>: Upper limit.</li>
- <li><code>s-limit-lwr</code>: Lower limit.</li>
- </ul>
- </ul>
- </div>
-<mct-example><!-- Color alone examples -->
-<div class="s-limit-yellow">Yellow limit</div>
-<div class="s-limit-red">Red limit</div>
-
-<!-- Color and icon examples -->
-<div class="s-limit-icon-yellow">Yellow limit with icon</div>
-<div class="s-limit-icon-red">Red limit with icon</div>
-<div class="s-limit-red icon-alert-rect">Red Limit with a custom icon</div>
-<div>Some text with an <span class="s-limit-icon-yellow">inline element</span> showing a yellow limit.</div>
-
-<!-- Severity and direction examples -->
-<div class="s-limit-yellow s-limit-lwr">Lower yellow limit</div>
-<div class="s-limit-red s-limit-upr">Upper red limit</div>
-
-<!-- Limits applied in a table -->
-<table>
- <tr class='header'><td>Name</td><td>Value 1</td><td>Value 2</td></tr>
- <tr><td>ENG_PWR 4991</td><td>7.023</td><td class="s-limit-yellow s-limit-upr">70.23</td></tr>
- <tr><td>ENG_PWR 4992</td><td>49.784</td><td class="s-limit-red s-limit-lwr">-121.22</td></tr>
- <tr><td>ENG_PWR 4993</td><td class="s-limit-yellow icon-alert-triangle">0.451</td><td>1.007</td></tr>
-</table>
-</mct-example>
- </div>
- </div>
-
- <div class="l-section">
- <h2>Status Bar Indicators</h2>
- <div class="cols cols1-1">
- <div class="col">
- <p>Indicators are small iconic notification elements that appear in the Status Bar area of
- the application at the window's bottom. Indicators should be used to articulate the state of a
- system and optionally provide gestures related to that system. They use a combination of icon and
- color to identify themselves and articulate a state respectively.</p>
- <h3>Recommendations</h3>
- <ul>
- <li><strong>Keep the icon consistent</strong>. The icon is the principal identifier of the system and is a valuable
- recall aid for the user. Don't change the icon as a system's state changes, use color and
- text for that purpose.</li>
- <li><strong>Don't use the same icon more than once</strong>. Select meaningful and distinct icons so the user
- will be able to quickly identify what they're looking for.</li>
- </ul>
-
- <h3>States</h3>
- <ul>
- <li><strong>Disabled</strong>: The system is not available to the user.</li>
- <li><strong>Off / Available</strong>: The system is accessible to the user but is not currently
- &quot;On&quot; or has not been configured. If the Indicator directly provides gestures
- related to the system, such as opening a configuration dialog box, then use
- &quot;Available&quot;; if the user must act elsewhere or the system isn't user-controllable,
- use &quot;Off&quot;.</li>
- <li><strong>On</strong>: The system is enabled or configured; it is having an effect on the larger application.</li>
- <li><strong>Alert / Error</strong>: There has been a problem with the system. Generally, &quot;Alert&quot;
- should be used to call attention to an issue that isn't critical, while &quot;Error&quot;
- should be used to call attention to a problem that the user should really be aware of or do
- something about.</li>
- </ul>
-
- <h3>Structure</h3>
- <p>Indicators consist of a <code>.ls-indicator</code>
- wrapper element with <code>.icon-*</code> classes for the type of thing they represent and
- <code>.s-status-*</code> classes to articulate the current state. Title attributes should be used
- to provide more verbose information about the thing and/or its status.</p>
- <p>The wrapper encloses a <code>.label</code> element that is displayed on hover. This element should
- include a brief statement of the current status, and can also include clickable elements
- as <code>&lt;a&gt;</code> tags. An optional <code>.count</code> element can be included to display
- information such as a number of messages.</p>
- <p>Icon classes are as defined on the
- <a class="link" href="#/browse/styleguide:home/glyphs?tc.mode=local&tc.timeSystem=utc&tc.startDelta=1800000&tc.endDelta=0&view=styleguide.glyphs">
- Glyphs page</a>. Status classes applicable to Indicators are as follows:</p>
- <ul>
- <li><code>s-status-disabled</code></li>
- <li><code>s-status-off</code></li>
- <li><code>s-status-available</code></li>
- <li><code>s-status-on</code></li>
- <li><code>s-status-alert</code></li>
- <li><code>s-status-error</code></li>
- </ul>
- </div>
-<mct-example><div class="s-ue-bottom-bar status-holder s-status-bar">
- <span class="ls-indicator icon-database s-status-disabled" title="The system is currently disabled.">
- <span class="label">System not enabled.</span>
- </span>
-</div>
-
-<div class="s-ue-bottom-bar status-holder s-status-bar">
- <span class="ls-indicator icon-database s-status-available" title="Configure data connection.">
- <span class="label">Data connection available
- <a class="icon-gear">Configure</a>
- </span>
- </span>
-</div>
-
-<div class="s-ue-bottom-bar status-holder s-status-bar">
- <span class="ls-indicator icon-database s-status-on" title="Data connected.">
- <span class="label">Connected to Skynet
- <a class="icon-gear">Change</a>
- <a>Disconnect</a>
- </span>
- </span>
-</div>
-
-<div class="s-ue-bottom-bar status-holder s-status-bar">
- <span class="ls-indicator icon-database s-status-alert" title="System is self-aware.">
- <span class="label">Skynet at Turing Level 5</span>
- </span>
- <span class="ls-indicator icon-bell s-status-error" title="You have alerts.">
- <span class="label">
- <a class="icon-alert-triangle">View Alerts</a>
- </span>
- <span class="count">495</span>
- </span>
-</div>
-</mct-example>
- </div>
- </div>
-</div>
diff --git a/example/styleguide/src/ExampleStyleGuideModelProvider.js b/example/styleguide/src/ExampleStyleGuideModelProvider.js
deleted file mode 100644
index 80f170a9e..000000000
--- a/example/styleguide/src/ExampleStyleGuideModelProvider.js
+++ /dev/null
@@ -1,82 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2016, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
- "use strict";
-
- function ExampleStyleGuideModelProvider($q) {
- var pages = {};
-
- // Add pages
- pages.intro = {
- name: "Introduction",
- type: "styleguide.intro",
- location: "styleguide:home"
- };
- pages.standards = {
- name: "Standards",
- type: "styleguide.standards",
- location: "styleguide:home"
- };
- pages.colors = {
- name: "Colors",
- type: "styleguide.colors",
- location: "styleguide:home"
- };
- pages.glyphs = {
- name: "Glyphs",
- type: "styleguide.glyphs",
- location: "styleguide:home"
- };
- pages.status = {
- name: "Status Indication",
- type: "styleguide.status",
- location: "styleguide:home"
- };
- pages.controls = {
- name: "Controls",
- type: "styleguide.controls",
- location: "styleguide:ui-elements"
- };
- pages.input = {
- name: "Text Inputs",
- type: "styleguide.input",
- location: "styleguide:ui-elements"
- };
- pages.menus = {
- name: "Menus",
- type: "styleguide.menus",
- location: "styleguide:ui-elements"
- };
-
- return {
- getModels: function () {
- return $q.when(pages);
- }
- };
- }
-
- return ExampleStyleGuideModelProvider;
- }
-);
diff --git a/example/styleguide/src/MCTExample.js b/example/styleguide/src/MCTExample.js
deleted file mode 100644
index 43b82a2c4..000000000
--- a/example/styleguide/src/MCTExample.js
+++ /dev/null
@@ -1,30 +0,0 @@
-define([
- '../res/templates/mct-example.html'
-], function (
- MCTExampleTemplate
-) {
-
- function MCTExample() {
- function link($scope, $element, $attrs, controller, $transclude) {
- var codeEl = $element.find('pre');
- var exampleEl = $element.find('div');
-
- $transclude(function (clone) {
- exampleEl.append(clone);
- codeEl.text(exampleEl.html()
- .replace(/ class="ng-scope"/g, "")
- .replace(/ ng-scope"/g, '"'));
- });
- }
-
- return {
- restrict: "E",
- template: MCTExampleTemplate,
- transclude: true,
- link: link,
- replace: true
- };
- }
-
- return MCTExample;
-});
diff --git a/index.html b/index.html
index 78e5fbf5c..d8ec226c4 100644
--- a/index.html
+++ b/index.html
@@ -1,5 +1,5 @@
<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
+ Open MCT, Copyright (c) 2014-2022, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
@@ -75,13 +75,15 @@
const TWO_HOURS = ONE_HOUR * 2;
const ONE_DAY = ONE_HOUR * 24;
-
openmct.install(openmct.plugins.LocalStorage());
+ openmct.install(openmct.plugins.example.Generator());
+ openmct.install(openmct.plugins.example.EventGeneratorPlugin());
+ openmct.install(openmct.plugins.example.ExampleImagery());
+ openmct.install(openmct.plugins.example.ExampleTags());
+
openmct.install(openmct.plugins.Espresso());
openmct.install(openmct.plugins.MyItems());
- openmct.install(openmct.plugins.Generator());
- openmct.install(openmct.plugins.ExampleImagery());
openmct.install(openmct.plugins.PlanLayout());
openmct.install(openmct.plugins.Timeline());
openmct.install(openmct.plugins.Hyperlink());
@@ -188,11 +190,14 @@
openmct.install(openmct.plugins.Filters(['table', 'telemetry.plot.overlay']));
openmct.install(openmct.plugins.ObjectMigration());
openmct.install(openmct.plugins.ClearData(
- ['table', 'telemetry.plot.overlay', 'telemetry.plot.stacked'],
- {indicator: true}
+ ['table', 'telemetry.plot.overlay', 'telemetry.plot.stacked', 'example.imagery'],
+ { indicator: true }
));
openmct.install(openmct.plugins.Clock({ enableClockIndicator: true }));
openmct.install(openmct.plugins.Timer());
+ openmct.install(openmct.plugins.Timelist());
+ openmct.install(openmct.plugins.BarChart());
+ openmct.install(openmct.plugins.ScatterPlot());
openmct.start();
</script>
</html>
diff --git a/indexTest.js b/indexTest.js
index 6c3549186..8ae5eea82 100644
--- a/indexTest.js
+++ b/indexTest.js
@@ -1,3 +1,2 @@
-const testsContext = require.context('.', true, /\/(src|platform)\/.*Spec.js$/);
-
+const testsContext = require.context('.', true, /^\.\/(src|example)\/.*Spec.js$/);
testsContext.keys().forEach(testsContext);
diff --git a/karma.conf.js b/karma.conf.js
index e0afe2ee1..e07aa9467 100644
--- a/karma.conf.js
+++ b/karma.conf.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -22,31 +22,10 @@
/*global module,process*/
-const browsers = [process.env.NODE_ENV === 'debug' ? 'ChromeDebugging' : 'ChromeHeadless'];
-const coverageEnabled = process.env.COVERAGE === 'true';
-const reporters = ['spec', 'junit'];
-
-if (coverageEnabled) {
- reporters.push('coverage-istanbul');
-}
-
module.exports = (config) => {
- const webpackConfig = require('./webpack.dev.js');
+ const webpackConfig = require('./webpack.coverage.js');
delete webpackConfig.output;
- if (coverageEnabled) {
- webpackConfig.module.rules.push({
- test: /\.js$/,
- exclude: /node_modules|example|lib|dist/,
- use: {
- loader: 'istanbul-instrumenter-loader',
- options: {
- esModules: true
- }
- }
- });
- }
-
config.set({
basePath: '',
frameworks: ['jasmine'],
@@ -59,11 +38,15 @@ module.exports = (config) => {
{
pattern: 'dist/inMemorySearchWorker.js*',
included: false
+ },
+ {
+ pattern: 'dist/generatorWorker.js*',
+ included: false
}
],
port: 9876,
- reporters: reporters,
- browsers: browsers,
+ reporters: ['spec', 'junit', 'coverage-istanbul'],
+ browsers: [process.env.NODE_ENV === 'debug' ? 'ChromeDebugging' : 'ChromeHeadless'],
client: {
jasmine: {
random: false,
@@ -84,12 +67,6 @@ module.exports = (config) => {
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
- // HTML test reporting.
- // htmlReporter: {
- // outputDir: "dist/reports/tests",
- // preserveDescribeNesting: true,
- // foldAll: false
- // },
junitReporter: {
outputDir: "dist/reports/tests",
outputFile: "test-results.xml",
@@ -97,15 +74,8 @@ module.exports = (config) => {
},
coverageIstanbulReporter: {
fixWebpackSourcePaths: true,
- dir: process.env.CIRCLE_ARTIFACTS
- ? process.env.CIRCLE_ARTIFACTS + '/coverage'
- : "dist/reports/coverage",
- reports: ['lcovonly', 'text-summary'],
- thresholds: {
- global: {
- lines: 66
- }
- }
+ dir: "coverage/unit",
+ reports: ['lcovonly']
},
specReporter: {
maxLogLines: 5,
@@ -121,8 +91,7 @@ module.exports = (config) => {
},
webpack: webpackConfig,
webpackMiddleware: {
- stats: 'errors-only',
- logLevel: 'warn'
+ stats: 'errors-warnings'
},
concurrency: 1,
singleRun: true,
diff --git a/openmct.js b/openmct.js
index fcebd1bc2..fe4ce8e62 100644
--- a/openmct.js
+++ b/openmct.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/package.json b/package.json
index 2157be1c1..0e00d4fe1 100644
--- a/package.json
+++ b/package.json
@@ -1,110 +1,113 @@
{
"name": "openmct",
- "version": "1.8.3-SNAPSHOT",
+ "version": "2.0.5",
"description": "The Open MCT core platform",
"devDependencies": {
- "@braintree/sanitize-url": "^5.0.2",
- "@percy/cli": "^1.0.0-beta.70",
- "@percy/playwright": "^1.0.1",
- "@playwright/test": "^1.16.3",
- "allure-playwright": "^2.0.0-beta.14",
- "angular": ">=1.8.0",
- "angular-route": "1.4.14",
- "babel-eslint": "10.1.0",
- "comma-separated-values": "^3.6.4",
- "concurrently": "^3.6.1",
- "copy-webpack-plugin": "^9.0.0",
- "cross-env": "^6.0.3",
- "css-loader": "^4.0.0",
- "d3-axis": "1.0.x",
- "d3-scale": "1.0.x",
- "d3-selection": "1.3.x",
- "eslint": "7.0.0",
- "eslint-plugin-playwright": "0.7.1",
- "eslint-plugin-vue": "^7.5.0",
- "eslint-plugin-you-dont-need-lodash-underscore": "^6.10.0",
- "eventemitter3": "^1.2.0",
- "exports-loader": "^0.7.0",
- "express": "^4.13.1",
- "file-loader": "^6.1.0",
- "file-saver": "^1.3.8",
- "git-rev-sync": "^1.4.0",
- "glob": ">= 3.0.0",
- "html-loader": "^0.5.5",
- "html2canvas": "^1.0.0-rc.7",
- "imports-loader": "^0.8.0",
- "istanbul-instrumenter-loader": "^3.0.1",
- "jasmine-core": "^4.0.0",
- "jsdoc": "^3.3.2",
- "karma": "6.3.9",
- "karma-chrome-launcher": "3.1.0",
+ "@babel/eslint-parser": "7.18.2",
+ "@braintree/sanitize-url": "6.0.0",
+ "@percy/cli": "1.2.1",
+ "@percy/playwright": "1.0.4",
+ "@playwright/test": "1.23.0",
+ "@types/eventemitter3": "^1.0.0",
+ "@types/jasmine": "^4.0.1",
+ "@types/karma": "^6.3.2",
+ "@types/lodash": "^4.14.178",
+ "@types/mocha": "^9.1.0",
+ "babel-loader": "8.2.3",
+ "babel-plugin-istanbul": "6.1.1",
+ "comma-separated-values": "3.6.4",
+ "codecov":"3.8.3",
+ "copy-webpack-plugin": "11.0.0",
+ "cross-env": "7.0.3",
+ "css-loader": "4.0.0",
+ "d3-axis": "3.0.0",
+ "d3-scale": "3.3.0",
+ "d3-selection": "3.0.0",
+ "eslint": "8.13.0",
+ "eslint-plugin-compat": "4.0.2",
+ "eslint-plugin-playwright": "0.9.0",
+ "eslint-plugin-vue": "9.1.0",
+ "eslint-plugin-you-dont-need-lodash-underscore": "6.12.0",
+ "eventemitter3": "1.2.0",
+ "express": "4.13.1",
+ "file-saver": "2.0.5",
+ "git-rev-sync": "3.0.2",
+ "html2canvas": "1.4.1",
+ "imports-loader": "0.8.0",
+ "jasmine-core": "4.1.1",
+ "jsdoc": "3.5.5",
+ "karma": "6.3.20",
+ "karma-chrome-launcher": "3.1.1",
"karma-cli": "2.0.0",
- "karma-coverage": "2.1.0",
+ "karma-coverage": "2.2.0",
"karma-coverage-istanbul-reporter": "3.0.3",
"karma-firefox-launcher": "2.1.2",
"karma-jasmine": "4.0.1",
"karma-junit-reporter": "2.0.1",
"karma-sourcemap-loader": "0.3.8",
- "karma-spec-reporter": "0.0.32",
- "karma-webpack": "^5.0.0",
- "location-bar": "^3.0.1",
- "lodash": "^4.17.12",
- "markdown-toc": "^0.11.7",
- "marked": "^0.3.5",
- "mini-css-extract-plugin": "^1.6.0",
- "minimist": "^1.2.5",
- "moment": "2.25.3",
- "moment-duration-format": "^2.2.2",
- "moment-timezone": "0.5.28",
- "node-bourbon": "^4.2.3",
- "painterro": "^1.2.56",
- "playwright": "^1.16.3",
- "plotly.js-basic-dist": "^2.5.0",
- "plotly.js-gl2d-dist": "^2.5.0",
- "printj": "^1.2.1",
- "raw-loader": "^0.5.1",
- "request": "^2.69.0",
- "resolve-url-loader": "^4.0.0",
- "sass": "^1.42.1",
- "sass-loader": "^12.1.0",
- "sinon": "^12.0.1",
- "split": "^1.0.0",
+ "karma-spec-reporter": "0.0.34",
+ "karma-webpack": "5.0.0",
+ "lighthouse": "9.6.1",
+ "location-bar": "3.0.1",
+ "lodash": "4.17.21",
+ "mini-css-extract-plugin": "2.6.0",
+ "moment": "2.29.3",
+ "moment-duration-format": "2.3.2",
+ "moment-timezone": "0.5.34",
+ "node-bourbon": "4.2.3",
+ "painterro": "1.2.56",
+ "nyc":"15.1.0",
+ "plotly.js-basic-dist": "2.12.0",
+ "plotly.js-gl2d-dist": "2.12.0",
+ "printj": "1.3.1",
+ "request": "2.88.2",
+ "resolve-url-loader": "5.0.0",
+ "sass": "1.52.2",
+ "sass-loader": "12.6.0",
+ "sinon": "14.0.0",
"style-loader": "^1.0.1",
- "uuid": "^3.3.3",
- "v8-compile-cache": "^1.1.0",
- "vue": "2.5.6",
- "vue-eslint-parser": "8.0.1",
+ "uuid": "8.3.2",
+ "vue": "2.6.14",
+ "vue-eslint-parser": "9.0.2",
"vue-loader": "15.9.8",
- "vue-template-compiler": "2.5.6",
- "webpack": "^5.53.0",
- "webpack-cli": "^4.0.0",
- "webpack-dev-middleware": "^3.1.3",
- "webpack-hot-middleware": "^2.22.3",
- "webpack-merge": "^5.8.0",
- "zepto": "^1.2.0"
+ "vue-template-compiler": "2.6.14",
+ "webpack": "5.68.0",
+ "webpack-cli": "4.9.2",
+ "webpack-dev-middleware": "5.3.3",
+ "webpack-hot-middleware": "2.25.1",
+ "webpack-merge": "5.8.0"
},
"scripts": {
- "clean": "rm -rf ./dist /node_modules; rm package-lock.json",
- "clean-test-lint": "npm run clean; npm install ; npm run test; npm run lint",
+ "clean": "rm -rf ./dist ./node_modules ./package-lock.json",
+ "clean-test-lint": "npm run clean; npm install; npm run test; npm run lint",
"start": "node app.js",
- "lint": "eslint platform example src --ext .js,.vue openmct.js",
- "lint:fix": "eslint platform example src --ext .js,.vue openmct.js --fix",
+ "lint": "eslint example src e2e --ext .js,.vue openmct.js --max-warnings=0",
+ "lint:fix": "eslint example src e2e --ext .js,.vue openmct.js --fix",
"build:prod": "cross-env webpack --config webpack.prod.js",
"build:dev": "webpack --config webpack.dev.js",
+ "build:coverage": "webpack --config webpack.coverage.js",
"build:watch": "webpack --config webpack.dev.js --watch",
- "test": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" karma start --single-run",
+ "info": "npx envinfo --system --browsers --npmPackages --binaries --languages --markdown",
+ "test": "cross-env NODE_ENV=test NODE_OPTIONS=\"--max_old_space_size=4096\" karma start --single-run",
+ "test:firefox": "cross-env NODE_ENV=test NODE_OPTIONS=\"--max_old_space_size=4096\" karma start --single-run --browsers=FirefoxHeadless",
"test:debug": "cross-env NODE_ENV=debug karma start --no-single-run",
- "test:coverage": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" COVERAGE=true karma start --single-run",
- "test:coverage:firefox": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" karma start --single-run --browsers=FirefoxHeadless",
- "test:e2e:ci": "npx playwright test --config=e2e/playwright-ci.config.js smoke",
- "test:e2e:local": "npx playwright test --config=e2e/playwright-local.config.js",
- "test:e2e:visual": "percy exec -- npx playwright test --config=e2e/playwright-visual.config.js default",
+ "test:e2e": "npx playwright test",
+ "test:e2e:ci": "npx playwright test --config=e2e/playwright-ci.config.js --project=chrome smoke branding default condition timeConductor clock exampleImagery persistence performance grandsearch notebook/tags",
+ "test:e2e:local": "npx playwright test --config=e2e/playwright-local.config.js --project=chrome",
+ "test:e2e:updatesnapshots": "npx playwright test --config=e2e/playwright-ci.config.js --project=chrome --grep @snapshot --update-snapshots",
+ "test:e2e:visual": "percy exec --config ./e2e/.percy.yml -- npx playwright test --config=e2e/playwright-visual.config.js",
"test:e2e:full": "npx playwright test --config=e2e/playwright-ci.config.js",
- "test:watch": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" karma start --no-single-run",
- "verify": "concurrently 'npm:test' 'npm:lint'",
+ "test:perf": "npx playwright test --config=e2e/playwright-performance.config.js",
+ "test:watch": "cross-env NODE_ENV=test NODE_OPTIONS=\"--max_old_space_size=4096\" karma start --no-single-run",
"jsdoc": "jsdoc -c jsdoc.json -R API.md -r -d dist/docs/api",
+ "update-about-dialog-copyright": "perl -pi -e 's/20\\d\\d\\-202\\d/2014\\-2022/gm' ./src/ui/layout/AboutDialog.vue",
+ "update-copyright-date": "npm run update-about-dialog-copyright && grep -lr --null --include=*.{js,scss,vue,ts,sh,html,md,frag} 'Copyright (c) 20' . | xargs -r0 perl -pi -e 's/Copyright\\s\\(c\\)\\s20\\d\\d\\-20\\d\\d/Copyright \\(c\\)\\ 2014\\-2022/gm'",
"otherdoc": "node docs/gendocs.js --in docs/src --out dist/docs --suppress-toc 'docs/src/index.md|docs/src/process/index.md'",
"docs": "npm run jsdoc ; npm run otherdoc",
+ "cov:e2e:report":"nyc report --reporter=lcovonly --report-dir=./coverage/e2e",
+ "cov:e2e:full:publish":"codecov --disable=gcov -f ./coverage/e2e/lcov.info -F e2e-full",
+ "cov:e2e:ci:publish":"codecov --disable=gcov -f ./coverage/e2e/lcov.info -F e2e-ci",
+ "cov:unit:publish":"codecov --disable=gcov -f ./coverage/unit/lcov.info -F unit",
"prepare": "npm run build:prod"
},
"repository": {
@@ -112,8 +115,18 @@
"url": "https://github.com/nasa/openmct.git"
},
"engines": {
- "node": ">=10.12.2 <16.0.0"
+ "node": ">=14.19.1"
},
+ "overrides": {
+ "core-js": "3.21.1"
+ },
+ "browserslist": [
+ "Firefox ESR",
+ "not IE 11",
+ "last 2 Chrome versions",
+ "unreleased Chrome versions",
+ "ios_saf > 15"
+ ],
"author": "",
"license": "Apache-2.0",
"private": true
diff --git a/platform/README.md b/platform/README.md
deleted file mode 100644
index e5144829f..000000000
--- a/platform/README.md
+++ /dev/null
@@ -1,2 +0,0 @@
-This directory contains all bundles for the Open MCT platform, as well
-as the framework which runs them.
diff --git a/platform/commonUI/README.md b/platform/commonUI/README.md
deleted file mode 100644
index 29e0f86c1..000000000
--- a/platform/commonUI/README.md
+++ /dev/null
@@ -1,17 +0,0 @@
-This directory contains bundles containing common user interface
-elements of Open MCT; that is, the user interface for the application
-as a whole (as opposed to for specific features) is implemented here.
-
-# Extensions
-
-This bundles adds a `stylesheets` extension category used to inject CSS
-from bundles. These extensions are declaration-only (no scripted
-implementation is needed or used); a single property, `stylesheetUrl`,
-should be provided, with a path to the relevant CSS file (including
-extension) relative to the resources directory for that bundle.
-
-Links to these CSS files are appended to the head when the application
-is started. These are added in standard priority order (see documentation
-for the framework layer); the order of inclusion of style sheets can
-change the way they are handled/understood by the browser, so priority
-can be used to provide control over this order.
diff --git a/platform/commonUI/browse/bundle.js b/platform/commonUI/browse/bundle.js
deleted file mode 100644
index f0034dd08..000000000
--- a/platform/commonUI/browse/bundle.js
+++ /dev/null
@@ -1,158 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
- "./src/navigation/NavigationService",
- "./src/navigation/NavigateAction",
- "./src/navigation/OrphanNavigationHandler",
- "./res/templates/browse.html",
- "./res/templates/browse-object.html",
- "./res/templates/browse/object-header.html",
- "./res/templates/browse/object-header-frame.html",
- "./res/templates/menu-arrow.html",
- "./res/templates/back-arrow.html",
- "./res/templates/browse/object-properties.html",
- "./res/templates/browse/inspector-region.html"
-], function (
- NavigationService,
- NavigateAction,
- OrphanNavigationHandler,
- browseTemplate,
- browseObjectTemplate,
- objectHeaderTemplate,
- objectHeaderFrameTemplate,
- menuArrowTemplate,
- backArrowTemplate,
- objectPropertiesTemplate,
- inspectorRegionTemplate
-) {
-
- return {
- name: "platform/commonUI/browse",
- definition: {
- "extensions": {
- "routes": [
- ],
- "constants": [
- {
- "key": "DEFAULT_PATH",
- "value": "mine",
- "priority": "fallback"
- }
- ],
- "representations": [
- {
- "key": "browse-object",
- "template": browseObjectTemplate,
- "gestures": [
- "drop"
- ],
- "uses": [
- "view"
- ]
- },
- {
- "key": "object-header",
- "template": objectHeaderTemplate,
- "uses": [
- "type"
- ]
- },
- {
- "key": "object-header-frame",
- "template": objectHeaderFrameTemplate,
- "uses": [
- "type"
- ]
- },
- {
- "key": "menu-arrow",
- "template": menuArrowTemplate,
- "uses": [
- "action"
- ],
- "gestures": [
- "menu"
- ]
- },
- {
- "key": "back-arrow",
- "uses": [
- "context"
- ],
- "template": backArrowTemplate
- },
- {
- "key": "object-properties",
- "template": objectPropertiesTemplate
- },
- {
- "key": "inspector-region",
- "template": inspectorRegionTemplate
- }
- ],
- "services": [
- {
- "key": "navigationService",
- "implementation": NavigationService,
- "depends": [
- "$window"
- ]
- }
- ],
- "actions": [
- {
- "key": "navigate",
- "implementation": NavigateAction,
- "depends": [
- "navigationService"
- ]
- }
- ],
- "runs": [
- {
- "implementation": OrphanNavigationHandler,
- "depends": [
- "throttle",
- "topic",
- "navigationService"
- ]
- }
- ],
- "templates": [
- {
- key: "browseRoot",
- template: browseTemplate
- },
- {
- key: "browseObject",
- template: browseObjectTemplate
- },
- {
- key: "inspectorRegion",
- template: inspectorRegionTemplate
- }
- ]
- }
- }
- };
-});
diff --git a/platform/commonUI/browse/res/templates/back-arrow.html b/platform/commonUI/browse/res/templates/back-arrow.html
deleted file mode 100644
index 16bbccb48..000000000
--- a/platform/commonUI/browse/res/templates/back-arrow.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
- as represented by the Administrator of the National Aeronautics and Space
- Administration. All rights reserved.
-
- Open MCT is licensed under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0.
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Open MCT includes source code licensed under additional open source
- licenses. See the Open Source Licenses file (LICENSES.md) included with
- this source code distribution or the Licensing information page available
- at runtime from the About dialog for additional information.
--->
-
-<a class='s-icon-button icon-pointer-left'
- ng-show="context.getPath().length > 2"
- ng-click="context.getParent().getCapability('action').perform('navigate')">
-</a>
-
diff --git a/platform/commonUI/browse/res/templates/browse-object.html b/platform/commonUI/browse/res/templates/browse-object.html
deleted file mode 100644
index 6d0319100..000000000
--- a/platform/commonUI/browse/res/templates/browse-object.html
+++ /dev/null
@@ -1,69 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
- as represented by the Administrator of the National Aeronautics and Space
- Administration. All rights reserved.
-
- Open MCT is licensed under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0.
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Open MCT includes source code licensed under additional open source
- licenses. See the Open Source Licenses file (LICENSES.md) included with
- this source code distribution or the Licensing information page available
- at runtime from the About dialog for additional information.
--->
-<div ng-controller="BrowseObjectController" class="abs l-flex-col">
- <div class="holder flex-elem l-flex-row object-browse-bar t-primary">
- <div class="items-select left flex-elem l-flex-row grows">
- <mct-representation key="'back-arrow'"
- mct-object="domainObject"
- class="flex-elem l-back"></mct-representation>
- <mct-representation key="'object-header'"
- mct-object="domainObject"
- class="l-flex-row flex-elem grows object-header">
- </mct-representation>
- </div>
- <div class="btn-bar right l-flex-row flex-elem flex-justify-end flex-fixed">
- <span class="l-object-action-buttons">
- <mct-representation key="'switcher'"
- mct-object="domainObject"
- ng-model="representation">
- </mct-representation>
- <!-- Temporarily, on mobile, the action buttons are hidden-->
- <mct-representation key="'action-group'"
- mct-object="domainObject"
- parameters="{ category: 'view-control' }"
- class="mobile-hide l-object-action-buttons">
- </mct-representation>
- </span>
- </div>
- </div>
- <div class="holder l-flex-col flex-elem grows l-object-wrapper l-controls-visible l-time-controller-visible">
- <div class="holder l-flex-col flex-elem grows l-object-wrapper-inner">
- <!-- Toolbar and Save/Cancel buttons -->
- <div class="l-edit-controls flex-elem l-flex-row flex-align-end">
- <mct-representation key="'edit-action-buttons'"
- mct-object="domainObject"
- class='flex-elem conclude-editing'>
- </mct-representation>
-
- </div>
- <mct-representation key="representation.selected.key"
- mct-object="representation.selected.key && domainObject"
- class="abs flex-elem grows object-holder-main scroll"
- mct-selectable="{
- item: domainObject.useCapability('adapter'),
- oldItem: domainObject
- }"
- mct-init-select>
- </mct-representation>
- </div>
- </div>
-</div>
diff --git a/platform/commonUI/browse/res/templates/browse.html b/platform/commonUI/browse/res/templates/browse.html
deleted file mode 100644
index 37eecfb61..000000000
--- a/platform/commonUI/browse/res/templates/browse.html
+++ /dev/null
@@ -1,90 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
- as represented by the Administrator of the National Aeronautics and Space
- Administration. All rights reserved.
-
- Open MCT is licensed under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0.
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Open MCT includes source code licensed under additional open source
- licenses. See the Open Source Licenses file (LICENSES.md) included with
- this source code distribution or the Licensing information page available
- at runtime from the About dialog for additional information.
--->
-
-<div class="abs holder-all" ng-controller="BrowseController">
- <mct-include key="'topbar-browse'"></mct-include>
- <div class="abs holder holder-main browse-area s-browse-area browse-wrapper"
- ng-controller="PaneController as modelPaneTree"
- ng-class="modelPaneTree.visible() ? 'pane-tree-showing' : 'pane-tree-hidden'" hide-parameter="hideTree">
- <mct-split-pane class='abs contents'
- anchor='left' alias="leftSide">
- <div class='split-pane-component treeview pane left'>
- <div class="abs holder l-flex-col holder-treeview-elements">
- <mct-representation key="'create-button'"
- mct-object="navigatedObject"
- class="holder flex-elem create-button-holder">
- </mct-representation>
- <mct-include key="'search'"
- ng-model="treeModel"
- class="holder l-flex-col flex-elem search-holder"
- ng-class="{ active: treeModel.search, grows: treeModel.search }">
- </mct-include>
- <mct-representation key="'tree'"
- mct-object="domainObject"
- parameters="tree"
- ng-model="treeModel"
- class="holder flex-elem grows vscroll tree-holder"
- ng-hide="treeModel.search">
- </mct-representation>
- </div>
- </div>
-
- <mct-splitter class="splitter-treeview mobile-hide"></mct-splitter>
-
- <div class='split-pane-component items pane primary-pane right'>
- <a class="mini-tab-icon anchor-left toggle-pane toggle-tree"
- title="{{ modelPaneTree.visible()? 'Hide' : 'Show' }} this pane"
- ng-click="modelPaneTree.toggle()"
- ng-class="{ collapsed : !modelPaneTree.visible() }"></a>
-
- <div class='holder holder-object-and-inspector abs' id='content-area'
- ng-controller="InspectorPaneController as modelPaneInspect"
- ng-class="modelPaneInspect.visible() ? 'pane-inspect-showing' : 'pane-inspect-hidden'"
- hide-parameter="hideInspector">
-
- <mct-split-pane class='l-object-and-inspector contents abs' anchor='right' alias="rightSide">
- <div class='split-pane-component t-object pane primary-pane left'>
- <mct-representation mct-object="navigatedObject"
- key="navigatedObject.getCapability('status').get('editing') ? 'edit-object' : 'browse-object'"
- class="abs holder holder-object t-main-view">
- </mct-representation>
- <a class="mini-tab-icon anchor-right mobile-hide toggle-pane toggle-inspect flush-right"
- title="{{ modelPaneInspect.visible()? 'Hide' : 'Show' }} the Inspection pane"
- ng-click="modelPaneInspect.toggle()"
- ng-class="{ collapsed : !modelPaneInspect.visible() }"></a>
- </div>
-
- <mct-splitter class="splitter-inspect mobile-hide flush-right edge-shdw"></mct-splitter>
-
- <div class="split-pane-component t-inspect pane right mobile-hide">
- <mct-representation key="'object-inspector'"
- mct-object="navigatedObject"
- ng-model="treeModel">
- </mct-representation>
- </div>
- </mct-split-pane>
- </div>
- </div>
- </mct-split-pane>
- </div>
- <mct-include key="'bottombar'"></mct-include>
-</div>
diff --git a/platform/commonUI/browse/res/templates/browse/inspector-region.html b/platform/commonUI/browse/res/templates/browse/inspector-region.html
deleted file mode 100644
index fbddf4c26..000000000
--- a/platform/commonUI/browse/res/templates/browse/inspector-region.html
+++ /dev/null
@@ -1,39 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
- as represented by the Administrator of the National Aeronautics and Space
- Administration. All rights reserved.
-
- Open MCT is licensed under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0.
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Open MCT includes source code licensed under additional open source
- licenses. See the Open Source Licenses file (LICENSES.md) included with
- this source code distribution or the Licensing information page available
- at runtime from the About dialog for additional information.
--->
-<div ng-controller="InspectorController as controller">
- <mct-representation
- key="'object-properties'"
- mct-object="controller.selectedItem()"
- ng-model="ngModel">
- </mct-representation>
-
- <div ng-if="!controller.hasProviderView()">
- <mct-representation
- key="inspectorKey"
- mct-object="controller.selectedItem()"
- ng-model="ngModel">
- </mct-representation>
- </div>
-
- <div class='inspector-provider-view'>
- </div>
-</div>
diff --git a/platform/commonUI/browse/res/templates/browse/object-header-frame.html b/platform/commonUI/browse/res/templates/browse/object-header-frame.html
deleted file mode 100644
index ff1172cf7..000000000
--- a/platform/commonUI/browse/res/templates/browse/object-header-frame.html
+++ /dev/null
@@ -1,31 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
- as represented by the Administrator of the National Aeronautics and Space
- Administration. All rights reserved.
-
- Open MCT is licensed under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0.
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Open MCT includes source code licensed under additional open source
- licenses. See the Open Source Licenses file (LICENSES.md) included with
- this source code distribution or the Licensing information page available
- at runtime from the About dialog for additional information.
--->
-<span class='type-icon flex-elem {{type.getCssClass()}}'></span>
-<span class="l-elem-wrapper l-flex-row flex-elem grows" ng-controller="ObjectHeaderController as controller">
- <span ng-if="parameters.mode" class='action flex-elem'>{{parameters.mode}}</span>
- <span class='title-label flex-elem holder flex-can-shrink s-input-inline'>{{model.name}}</span>
- <span class='t-object-alert t-alert-unsynced flex-elem holder' title='This object is not currently displaying real-time data'></span>
- <mct-representation
- key="'menu-arrow'"
- mct-object='domainObject'
- class="flex-elem context-available-w"></mct-representation>
-</span>
diff --git a/platform/commonUI/browse/res/templates/browse/object-header.html b/platform/commonUI/browse/res/templates/browse/object-header.html
deleted file mode 100644
index 3deb3ccfc..000000000
--- a/platform/commonUI/browse/res/templates/browse/object-header.html
+++ /dev/null
@@ -1,35 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
- as represented by the Administrator of the National Aeronautics and Space
- Administration. All rights reserved.
-
- Open MCT is licensed under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0.
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Open MCT includes source code licensed under additional open source
- licenses. See the Open Source Licenses file (LICENSES.md) included with
- this source code distribution or the Licensing information page available
- at runtime from the About dialog for additional information.
--->
-<span class='type-icon flex-elem {{type.getCssClass()}}'></span>
-<span class="l-elem-wrapper l-flex-row flex-elem grows" ng-controller="ObjectHeaderController as controller">
- <span ng-if="parameters.mode" class='action flex-elem'>{{parameters.mode}}</span>
- <span ng-attr-contenteditable="{{ controller.editable ? true : undefined }}"
- class='title-label flex-elem holder flex-can-shrink s-input-inline'
- ng-click="controller.edit()"
- ng-blur="controller.updateName($event)"
- ng-keypress="controller.updateName($event)">{{model.name}}</span>
- <span class='t-object-alert t-alert-unsynced flex-elem holder' title='This object is not currently displaying real-time data'></span>
- <mct-representation
- key="'menu-arrow'"
- mct-object='domainObject'
- class="flex-elem context-available-w"></mct-representation>
-</span>
diff --git a/platform/commonUI/browse/res/templates/browse/object-properties.html b/platform/commonUI/browse/res/templates/browse/object-properties.html
deleted file mode 100644
index efe5a64ad..000000000
--- a/platform/commonUI/browse/res/templates/browse/object-properties.html
+++ /dev/null
@@ -1,64 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
- as represented by the Administrator of the National Aeronautics and Space
- Administration. All rights reserved.
-
- Open MCT is licensed under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0.
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Open MCT includes source code licensed under additional open source
- licenses. See the Open Source Licenses file (LICENSES.md) included with
- this source code distribution or the Licensing information page available
- at runtime from the About dialog for additional information.
--->
-<div ng-controller="ObjectInspectorController as controller" class="grid-properties">
- <ul class="l-inspector-part">
- <h2 class="first">Properties</h2>
- <li class="t-repeat grid-row"
- ng-repeat="data in metadata"
- ng-class="{ first:$index === 0 }">
- <div class="grid-cell label">{{ data.name }}</div>
- <div class="grid-cell value">{{ data.value }}</div>
- </li>
- </ul>
-
- <ul class="l-inspector-part" ng-if="contextutalParents.length > 0">
- <h2 title="The location of this linked object.">Location</h2>
- <li class="grid-row">
- <div class="label" ng-if="primaryParents.length > 0">This Link</div>
- <div class="grid-cell value">
- <div class="t-repeat inspector-location"
- ng-repeat="parent in contextutalParents"
- ng-class="{ last:($index + 1) === contextualParents.length }">
- <mct-representation key="'label'"
- mct-object="parent"
- ng-click="parent.getCapability('action').perform('navigate')"
- class="location-item">
- </mct-representation>
- </div>
- </div>
- </li>
- <li class="grid-row" ng-if="primaryParents.length > 0">
- <div class="grid-cell label">Original</div>
- <div class="grid-cell value">
- <div class="t-repeat inspector-location value"
- ng-repeat="parent in primaryParents"
- ng-class="{ last:($index + 1) === primaryParents.length }">
- <mct-representation key="'label'"
- mct-object="parent"
- ng-click="parent.getCapability('action').perform('navigate')"
- class="location-item">
- </mct-representation>
- </div>
- </div>
- </li>
- </ul>
-</div>
diff --git a/platform/commonUI/browse/res/templates/menu-arrow.html b/platform/commonUI/browse/res/templates/menu-arrow.html
deleted file mode 100644
index 719dd86c4..000000000
--- a/platform/commonUI/browse/res/templates/menu-arrow.html
+++ /dev/null
@@ -1,26 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
- as represented by the Administrator of the National Aeronautics and Space
- Administration. All rights reserved.
-
- Open MCT is licensed under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0.
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Open MCT includes source code licensed under additional open source
- licenses. See the Open Source Licenses file (LICENSES.md) included with
- this source code distribution or the Licensing information page available
- at runtime from the About dialog for additional information.
--->
-
-<span ng-controller="MenuArrowController as menuArrow">
- <a class='context-available'
- ng-click='menuArrow.showMenu($event)'></a>
-</span>
diff --git a/platform/commonUI/browse/src/InspectorRegion.js b/platform/commonUI/browse/src/InspectorRegion.js
deleted file mode 100644
index d519c9521..000000000
--- a/platform/commonUI/browse/src/InspectorRegion.js
+++ /dev/null
@@ -1,67 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [
- '../../regions/src/Region'
- ],
- function (Region) {
-
- /**
- * Defines the a default Inspector region. Captured in a class to
- * allow for modular extension and customization of regions based on
- * the typical case.
- * @memberOf platform/commonUI/regions
- * @constructor
- */
- function InspectorRegion() {
- Region.call(this, {'name': 'Inspector'});
-
- this.buildRegion();
- }
-
- InspectorRegion.prototype = Object.create(Region.prototype);
- InspectorRegion.prototype.constructor = Region;
-
- /**
- * @private
- */
- InspectorRegion.prototype.buildRegion = function () {
- var metadataRegion = {
- name: 'metadata',
- title: 'Metadata Region',
- // Which modes should the region part be visible in? If
- // nothing provided here, then assumed that part is visible
- // in both. The visibility or otherwise of a region part
- // should be decided by a policy. In this case, 'modes' is a
- // shortcut that is used by the EditableRegionPolicy.
- modes: ['browse', 'edit'],
- content: {
- key: 'object-properties'
- }
- };
- this.addRegion(new Region(metadataRegion), 0);
- };
-
- return InspectorRegion;
- }
-);
diff --git a/platform/commonUI/browse/src/navigation/NavigateAction.js b/platform/commonUI/browse/src/navigation/NavigateAction.js
deleted file mode 100644
index 0637d5b51..000000000
--- a/platform/commonUI/browse/src/navigation/NavigateAction.js
+++ /dev/null
@@ -1,69 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Module defining NavigateAction. Created by vwoeltje on 11/10/14.
- */
-define(
- [],
- function () {
-
- /**
- * The navigate action navigates to a specific domain object.
- * @memberof platform/commonUI/browse
- * @constructor
- * @implements {Action}
- */
- function NavigateAction(navigationService, context) {
- this.domainObject = context.domainObject;
- this.navigationService = navigationService;
- }
-
- /**
- * Navigate to the object described in the context.
- * @returns {Promise} a promise that is resolved once the
- * navigation has been updated
- */
- NavigateAction.prototype.perform = function () {
- if (this.navigationService.shouldNavigate()) {
- this.navigationService.setNavigation(this.domainObject, true);
-
- return Promise.resolve({});
- }
-
- return Promise.reject('Navigation Prevented by User');
- };
-
- /**
- * Navigate as an action is only applicable when a domain object
- * is described in the action context.
- * @param {ActionContext} context the context in which the action
- * will be performed
- * @returns {boolean} true if applicable
- */
- NavigateAction.appliesTo = function (context) {
- return context.domainObject !== undefined;
- };
-
- return NavigateAction;
- }
-);
diff --git a/platform/commonUI/browse/src/navigation/NavigationService.js b/platform/commonUI/browse/src/navigation/NavigationService.js
deleted file mode 100644
index 25612a2fe..000000000
--- a/platform/commonUI/browse/src/navigation/NavigationService.js
+++ /dev/null
@@ -1,203 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Module defining NavigationService. Created by vwoeltje on 11/10/14.
- */
-define(
- [],
- function () {
-
- /**
- * The navigation service maintains the application's current
- * navigation state, and allows listening for changes thereto.
- *
- * @memberof platform/commonUI/browse
- * @constructor
- */
- function NavigationService($window) {
- this.navigated = undefined;
- this.callbacks = [];
- this.checks = [];
- this.$window = $window;
-
- this.oldUnload = $window.onbeforeunload;
- $window.onbeforeunload = this.onBeforeUnload.bind(this);
- }
-
- /**
- * Get the current navigation state.
- *
- * @returns {DomainObject} the object that is navigated-to
- */
- NavigationService.prototype.getNavigation = function () {
- return this.navigated;
- };
-
- /**
- * Navigate to a specified object. If navigation checks exist and
- * return reasons to prevent navigation, it will prompt the user before
- * continuing. Trying to navigate to the currently navigated object will
- * do nothing.
- *
- * If a truthy value is passed for `force`, it will skip navigation
- * and will not prevent navigation to an already selected object.
- *
- * @param {DomainObject} domainObject the domain object to navigate to
- * @param {Boolean} force if true, force navigation to occur.
- * @returns {Boolean} true if navigation occurred, otherwise false.
- */
- NavigationService.prototype.setNavigation = function (domainObject, force) {
- if (force) {
- this.doNavigation(domainObject);
-
- return true;
- }
-
- if (this.navigated === domainObject) {
- return true;
- }
-
- var doNotNavigate = this.shouldWarnBeforeNavigate();
- if (doNotNavigate && !this.$window.confirm(doNotNavigate)) {
- return false;
- }
-
- this.doNavigation(domainObject);
-
- return true;
- };
-
- /**
- * Listen for changes in navigation. The passed callback will
- * be invoked with the new domain object of navigation when
- * this changes.
- *
- * @param {function} callback the callback to invoke when
- * navigation state changes
- */
- NavigationService.prototype.addListener = function (callback) {
- this.callbacks.push(callback);
- };
-
- /**
- * Stop listening for changes in navigation state.
- *
- * @param {function} callback the callback which should
- * no longer be invoked when navigation state
- * changes
- */
- NavigationService.prototype.removeListener = function (callback) {
- this.callbacks = this.callbacks.filter(function (cb) {
- return cb !== callback;
- });
- };
-
- /**
- * Check if navigation should proceed. May prompt a user for input
- * if any checkFns return messages. Returns true if the user wishes to
- * navigate, otherwise false. If using this prior to calling
- * `setNavigation`, you should call `setNavigation` with `force=true`
- * to prevent duplicate dialogs being displayed to the user.
- *
- * @returns {Boolean} true if the user wishes to navigate, otherwise false.
- */
- NavigationService.prototype.shouldNavigate = function () {
- var doNotNavigate = this.shouldWarnBeforeNavigate();
-
- return !doNotNavigate || this.$window.confirm(doNotNavigate);
- };
-
- /**
- * Register a check function to be called before any navigation occurs.
- * Check functions should return a human readable "message" if
- * there are any reasons to prevent navigation. Otherwise, they should
- * return falsy. Returns a function which can be called to remove the
- * check function.
- *
- * @param {Function} checkFn a function to call before navigation occurs.
- * @returns {Function} removeCheck call to remove check
- */
- NavigationService.prototype.checkBeforeNavigation = function (checkFn) {
- this.checks.push(checkFn);
-
- return function removeCheck() {
- this.checks = this.checks.filter(function (fn) {
- return checkFn !== fn;
- });
- }.bind(this);
- };
-
- /**
- * Private method to actually perform navigation.
- *
- * @private
- */
- NavigationService.prototype.doNavigation = function (value) {
- this.navigated = value;
- this.callbacks.forEach(function (callback) {
- callback(value);
- });
- };
-
- /**
- * Returns either a false value, or a string that should be displayed
- * to the user before navigation is allowed.
- *
- * @private
- */
- NavigationService.prototype.shouldWarnBeforeNavigate = function () {
- var reasons = [];
- this.checks.forEach(function (checkFn) {
- var reason = checkFn();
- if (reason) {
- reasons.push(reason);
- }
- });
-
- if (reasons.length) {
- return reasons.join('\n');
- }
-
- return false;
- };
-
- /**
- * Listener for window on before unload event-- will warn before
- * navigation is allowed.
- *
- * @private
- */
- NavigationService.prototype.onBeforeUnload = function () {
- var shouldWarnBeforeNavigate = this.shouldWarnBeforeNavigate();
- if (shouldWarnBeforeNavigate) {
- return shouldWarnBeforeNavigate;
- }
-
- if (this.oldUnload) {
- return this.oldUnload.apply(undefined, [].slice.apply(arguments));
- }
- };
-
- return NavigationService;
- }
-);
diff --git a/platform/commonUI/browse/src/navigation/OrphanNavigationHandler.js b/platform/commonUI/browse/src/navigation/OrphanNavigationHandler.js
deleted file mode 100644
index 198f57d9c..000000000
--- a/platform/commonUI/browse/src/navigation/OrphanNavigationHandler.js
+++ /dev/null
@@ -1,76 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([], function () {
-
- /**
- * Navigates away from orphan objects whenever they are detected.
- *
- * An orphan object is an object whose apparent parent does not
- * actually contain it. This may occur in certain circumstances, such
- * as when persistence succeeds for a newly-created object but fails
- * for its parent.
- *
- * @param throttle the `throttle` service
- * @param topic the `topic` service
- * @param navigationService the `navigationService`
- * @constructor
- */
- function OrphanNavigationHandler(throttle, topic, navigationService) {
- var throttledCheckNavigation;
-
- function getParent(domainObject) {
- var context = domainObject.getCapability('context');
-
- return context.getParent();
- }
-
- function preventOrphanNavigation(domainObject) {
- var parent = getParent(domainObject);
- parent.useCapability('composition')
- .then(function (composees) {
- var isOrphan = composees.every(function (c) {
- return c.getId() !== domainObject.getId();
- });
- if (isOrphan) {
- parent.getCapability('action').perform('navigate');
- }
- });
- }
-
- function checkNavigation() {
- var navigatedObject = navigationService.getNavigation();
- if (navigatedObject && navigatedObject.hasCapability('context')) {
- if (!navigatedObject.getCapability('editor').isEditContextRoot()) {
- preventOrphanNavigation(navigatedObject);
- }
- }
- }
-
- throttledCheckNavigation = throttle(checkNavigation);
-
- navigationService.addListener(throttledCheckNavigation);
- topic('mutation').listen(throttledCheckNavigation);
- }
-
- return OrphanNavigationHandler;
-});
diff --git a/platform/commonUI/browse/test/InspectorRegionSpec.js b/platform/commonUI/browse/test/InspectorRegionSpec.js
deleted file mode 100644
index 2f228cb99..000000000
--- a/platform/commonUI/browse/test/InspectorRegionSpec.js
+++ /dev/null
@@ -1,43 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * MCTIncudeSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../src/InspectorRegion"],
- function (InspectorRegion) {
-
- describe("The inspector region", function () {
- var inspectorRegion;
-
- beforeEach(function () {
- inspectorRegion = new InspectorRegion();
- });
-
- it("creates default region parts", function () {
- expect(inspectorRegion.regions.length).toBe(1);
- });
-
- });
- }
-);
diff --git a/platform/commonUI/browse/test/navigation/NavigateActionSpec.js b/platform/commonUI/browse/test/navigation/NavigateActionSpec.js
deleted file mode 100644
index 158714b63..000000000
--- a/platform/commonUI/browse/test/navigation/NavigateActionSpec.js
+++ /dev/null
@@ -1,85 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * MCTRepresentationSpec. Created by vwoeltje on 11/6/14.
- */
-define([
- "../../src/navigation/NavigateAction"
-], function (
- NavigateAction
-) {
-
- describe("The navigate action", function () {
- var mockNavigationService,
- mockDomainObject,
- action;
-
- beforeEach(function () {
- mockNavigationService = jasmine.createSpyObj(
- "navigationService",
- [
- "shouldNavigate",
- "setNavigation"
- ]
- );
-
- mockDomainObject = {};
-
- action = new NavigateAction(
- mockNavigationService,
- { domainObject: mockDomainObject }
- );
- });
-
- it("sets navigation if it is allowed", function () {
- mockNavigationService.shouldNavigate.and.returnValue(true);
-
- return action.perform()
- .then(function () {
- expect(mockNavigationService.setNavigation)
- .toHaveBeenCalledWith(mockDomainObject, true);
- });
- });
-
- it("does not set navigation if it is not allowed", function () {
- mockNavigationService.shouldNavigate.and.returnValue(false);
- var onSuccess = jasmine.createSpy('onSuccess');
-
- return action.perform()
- .then(onSuccess, function () {
- expect(onSuccess).not.toHaveBeenCalled();
- expect(mockNavigationService.setNavigation)
- .not
- .toHaveBeenCalledWith(mockDomainObject);
- });
- });
-
- it("is only applicable when a domain object is in context", function () {
- expect(NavigateAction.appliesTo({})).toBeFalsy();
- expect(NavigateAction.appliesTo({
- domainObject: mockDomainObject
- })).toBeTruthy();
- });
-
- });
-});
diff --git a/platform/commonUI/browse/test/navigation/NavigationServiceSpec.js b/platform/commonUI/browse/test/navigation/NavigationServiceSpec.js
deleted file mode 100644
index 7d3520b2b..000000000
--- a/platform/commonUI/browse/test/navigation/NavigationServiceSpec.js
+++ /dev/null
@@ -1,88 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * MCTRepresentationSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/navigation/NavigationService"],
- function (NavigationService) {
-
- describe("The navigation service", function () {
- var $window,
- navigationService;
-
- beforeEach(function () {
- $window = jasmine.createSpyObj('$window', ['confirm']);
- navigationService = new NavigationService($window);
- });
-
- it("stores navigation state", function () {
- var testObject = { someKey: 42 },
- otherObject = { someKey: "some value" };
- expect(navigationService.getNavigation())
- .toBeUndefined();
- navigationService.setNavigation(testObject);
- expect(navigationService.getNavigation())
- .toBe(testObject);
- expect(navigationService.getNavigation())
- .toBe(testObject);
- navigationService.setNavigation(otherObject);
- expect(navigationService.getNavigation())
- .toBe(otherObject);
- });
-
- it("notifies listeners on change", function () {
- var testObject = { someKey: 42 },
- callback = jasmine.createSpy("callback");
-
- navigationService.addListener(callback);
- expect(callback).not.toHaveBeenCalled();
-
- navigationService.setNavigation(testObject);
- expect(callback).toHaveBeenCalledWith(testObject);
- });
-
- it("does not notify listeners when no changes occur", function () {
- var testObject = { someKey: 42 },
- callback = jasmine.createSpy("callback");
-
- navigationService.addListener(callback);
- navigationService.setNavigation(testObject);
- navigationService.setNavigation(testObject);
- expect(callback.calls.count()).toEqual(1);
- });
-
- it("stops notifying listeners after removal", function () {
- var testObject = { someKey: 42 },
- callback = jasmine.createSpy("callback");
-
- navigationService.addListener(callback);
- navigationService.removeListener(callback);
-
- navigationService.setNavigation(testObject);
- expect(callback).not.toHaveBeenCalled();
- });
-
- });
- }
-);
diff --git a/platform/commonUI/browse/test/navigation/OrphanNavigationHandlerSpec.js b/platform/commonUI/browse/test/navigation/OrphanNavigationHandlerSpec.js
deleted file mode 100644
index 904b2db2f..000000000
--- a/platform/commonUI/browse/test/navigation/OrphanNavigationHandlerSpec.js
+++ /dev/null
@@ -1,182 +0,0 @@
-/*****************************************************************************
-* Open MCT, Copyright (c) 2014-2021, United States Government
-* as represented by the Administrator of the National Aeronautics and Space
-* Administration. All rights reserved.
-*
-* Open MCT is licensed under the Apache License, Version 2.0 (the
-* "License"); you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-* http://www.apache.org/licenses/LICENSE-2.0.
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-* License for the specific language governing permissions and limitations
-* under the License.
-*
-* Open MCT includes source code licensed under additional open source
-* licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
-* at runtime from the About dialog for additional information.
-*****************************************************************************/
-
-define([
- '../../src/navigation/OrphanNavigationHandler'
-], function (OrphanNavigationHandler) {
- describe("OrphanNavigationHandler", function () {
- var mockTopic,
- mockThrottle,
- mockMutationTopic,
- mockNavigationService,
- mockDomainObject,
- mockParentObject,
- mockContext,
- mockActionCapability,
- mockEditor,
- testParentComposition,
- testId,
- mockThrottledFns;
-
- beforeEach(function () {
- testId = 'some-identifier';
-
- mockThrottledFns = [];
-
- mockTopic = jasmine.createSpy('topic');
- mockThrottle = jasmine.createSpy('throttle');
- mockNavigationService = jasmine.createSpyObj('navigationService', [
- 'getNavigation',
- 'addListener'
- ]);
- mockMutationTopic = jasmine.createSpyObj('mutationTopic', [
- 'listen'
- ]);
- mockDomainObject = jasmine.createSpyObj('domainObject', [
- 'getId',
- 'getCapability',
- 'hasCapability'
- ]);
- mockParentObject = jasmine.createSpyObj('domainObject', [
- 'getId',
- 'getCapability',
- 'useCapability'
- ]);
- mockContext = jasmine.createSpyObj('context', ['getParent']);
- mockActionCapability = jasmine.createSpyObj('action', ['perform']);
- mockEditor = jasmine.createSpyObj('editor', ['isEditContextRoot']);
-
- mockThrottle.and.callFake(function (fn) {
- var mockThrottledFn =
- jasmine.createSpy('throttled-' + mockThrottledFns.length);
- mockThrottledFn.and.callFake(fn);
- mockThrottledFns.push(mockThrottledFn);
-
- return mockThrottledFn;
- });
- mockTopic.and.returnValue(mockMutationTopic);
- mockDomainObject.getId.and.returnValue(testId);
- mockDomainObject.getCapability.and.callFake(function (c) {
- return {
- context: mockContext,
- editor: mockEditor
- }[c];
- });
- mockDomainObject.hasCapability.and.callFake(function (c) {
- return Boolean(mockDomainObject.getCapability(c));
- });
- mockParentObject.getCapability.and.callFake(function (c) {
- return {
- action: mockActionCapability
- }[c];
- });
- testParentComposition = [];
- mockParentObject.useCapability.and.returnValue(Promise.resolve(testParentComposition));
-
- mockContext.getParent.and.returnValue(mockParentObject);
- mockNavigationService.getNavigation.and.returnValue(mockDomainObject);
- mockEditor.isEditContextRoot.and.returnValue(false);
-
- return new OrphanNavigationHandler(
- mockThrottle,
- mockTopic,
- mockNavigationService
- );
- });
-
- it("listens for mutation with a throttled function", function () {
- expect(mockMutationTopic.listen)
- .toHaveBeenCalledWith(jasmine.any(Function));
- expect(mockThrottledFns.indexOf(
- mockMutationTopic.listen.calls.mostRecent().args[0]
- )).not.toEqual(-1);
- });
-
- it("listens for navigation changes with a throttled function", function () {
- expect(mockNavigationService.addListener)
- .toHaveBeenCalledWith(jasmine.any(Function));
- expect(mockThrottledFns.indexOf(
- mockNavigationService.addListener.calls.mostRecent().args[0]
- )).not.toEqual(-1);
- });
-
- [false, true].forEach(function (isOrphan) {
- var prefix = isOrphan ? "" : "non-";
- describe("for " + prefix + "orphan objects", function () {
- beforeEach(function () {
- if (!isOrphan) {
- testParentComposition.push(mockDomainObject);
- }
- });
-
- [false, true].forEach(function (isEditRoot) {
- var caseName = isEditRoot
- ? "that are being edited" : "that are not being edited";
-
- function itNavigatesAsExpected() {
- if (isOrphan && !isEditRoot) {
- it("navigates to the parent", function () {
- return Promise.resolve().then(function () {
- expect(mockActionCapability.perform)
- .toHaveBeenCalledWith('navigate');
- });
- });
- } else {
- it("does nothing", function () {
- return Promise.resolve().then(function () {
- expect(mockActionCapability.perform)
- .not.toHaveBeenCalled();
- });
- });
- }
- }
-
- describe(caseName, function () {
- beforeEach(function () {
- mockEditor.isEditContextRoot.and.returnValue(isEditRoot);
- });
-
- describe("when navigation changes", function () {
- beforeEach(function () {
- mockNavigationService.addListener.calls.mostRecent()
- .args[0](mockDomainObject);
- });
- itNavigatesAsExpected();
- });
-
- describe("when mutation occurs", function () {
- beforeEach(function () {
- mockMutationTopic.listen.calls.mostRecent()
- .args[0](mockParentObject);
- });
-
- itNavigatesAsExpected();
- });
-
- });
- });
- });
- });
-
- });
-});
-
diff --git a/platform/commonUI/dialog/README.md b/platform/commonUI/dialog/README.md
deleted file mode 100644
index c42cf3a95..000000000
--- a/platform/commonUI/dialog/README.md
+++ /dev/null
@@ -1,27 +0,0 @@
-This bundle provides `dialogService`, which can be used to prompt
-for user input.
-
-## `getUserChoice`
-
-The `getUserChoice` method is useful for displaying a message and a set of
-buttons. This method returns a promise which will resolve to the user's
-chosen option (or, more specifically, its `key`), and will be rejected if
-the user closes the dialog with the X in the top-right;
-
-The `dialogModel` given as an argument to this method should have the
-following properties.
-
-* `title`: The title to display at the top of the dialog.
-* `hint`: Short message to display below the title.
-* `template`: Identifying key (as will be passed to `mct-include`) for
- the template which will be used to populate the inner area of the dialog.
-* `model`: Model to pass in the `ng-model` attribute of
- `mct-include`.
-* `parameters`: Parameters to pass in the `parameters` attribute of
- `mct-include`.
-* `options`: An array of options describing each button at the bottom.
- Each option may have the following properties:
- * `name`: Human-readable name to display in the button.
- * `key`: Machine-readable key, to pass as the result of the resolved
- promise when clicked.
- * `description`: Description to show in tool tip on hover.
diff --git a/platform/commonUI/dialog/bundle.js b/platform/commonUI/dialog/bundle.js
deleted file mode 100644
index 1b00d138b..000000000
--- a/platform/commonUI/dialog/bundle.js
+++ /dev/null
@@ -1,112 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
- "./src/DialogService",
- "./src/OverlayService",
- "./res/templates/overlay-dialog.html",
- "./res/templates/overlay-options.html",
- "./res/templates/dialog.html",
- "./res/templates/overlay-blocking-message.html",
- "./res/templates/message.html",
- "./res/templates/notification-message.html",
- "./res/templates/overlay-message-list.html",
- "./res/templates/overlay.html"
-], function (
- DialogService,
- OverlayService,
- overlayDialogTemplate,
- overlayOptionsTemplate,
- dialogTemplate,
- overlayBlockingMessageTemplate,
- messageTemplate,
- notificationMessageTemplate,
- overlayMessageListTemplate,
- overlayTemplate
-) {
-
- return {
- name: "platform/commonUI/dialog",
- definition: {
- "extensions": {
- "services": [
- {
- "key": "dialogService",
- "implementation": DialogService,
- "depends": [
- "overlayService",
- "$q",
- "$log",
- "$document"
- ]
- },
- {
- "key": "overlayService",
- "implementation": OverlayService,
- "depends": [
- "$document",
- "$compile",
- "$rootScope",
- "$timeout"
- ]
- }
- ],
- "templates": [
- {
- "key": "overlay-dialog",
- "template": overlayDialogTemplate
- },
- {
- "key": "overlay-options",
- "template": overlayOptionsTemplate
- },
- {
- "key": "form-dialog",
- "template": dialogTemplate
- },
- {
- "key": "overlay-blocking-message",
- "template": overlayBlockingMessageTemplate
- },
- {
- "key": "message",
- "template": messageTemplate
- },
- {
- "key": "notification-message",
- "template": notificationMessageTemplate
- },
- {
- "key": "overlay-message-list",
- "template": overlayMessageListTemplate
- }
- ],
- "containers": [
- {
- "key": "overlay",
- "template": overlayTemplate
- }
- ]
- }
- }
- };
-});
diff --git a/platform/commonUI/dialog/res/templates/dialog.html b/platform/commonUI/dialog/res/templates/dialog.html
deleted file mode 100644
index 348fddc0f..000000000
--- a/platform/commonUI/dialog/res/templates/dialog.html
+++ /dev/null
@@ -1,43 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
- as represented by the Administrator of the National Aeronautics and Space
- Administration. All rights reserved.
-
- Open MCT is licensed under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0.
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Open MCT includes source code licensed under additional open source
- licenses. See the Open Source Licenses file (LICENSES.md) included with
- this source code distribution or the Licensing information page available
- at runtime from the About dialog for additional information.
--->
-<div class="c-overlay__top-bar">
- <div class="c-overlay__dialog-title">{{ngModel.title}}</div>
- <div class="c-overlay__dialog-hint hint">All fields marked <span class="req icon-asterisk"></span> are required.</div>
-</div>
-<div class='c-overlay__contents-main'>
- <mct-form ng-model="ngModel.value"
- structure="ngModel.structure"
- class="validates"
- name="createForm">
- </mct-form>
-</div>
-<div class="c-overlay__button-bar">
- <button class='c-button c-button--major'
- ng-class="{ disabled: !createForm.$valid }"
- ng-click="ngModel.confirm()">
- OK
- </button>
- <button class='c-button '
- ng-click="ngModel.cancel()">
- Cancel
- </button>
-</div>
diff --git a/platform/commonUI/dialog/res/templates/message.html b/platform/commonUI/dialog/res/templates/message.html
deleted file mode 100644
index c75878cfc..000000000
--- a/platform/commonUI/dialog/res/templates/message.html
+++ /dev/null
@@ -1,32 +0,0 @@
-<div class="c-message"
- ng-class="'message-severity-' + ngModel.severity">
- <div class="w-message-contents">
- <div class="c-message__top-bar">
- <div class="c-message__title">{{ngModel.title}}</div>
- </div>
- <div class="c-message__hint" ng-hide="ngModel.hint === undefined">
- {{ngModel.hint}}
- <span ng-if="ngModel.timestamp !== undefined">[{{ngModel.timestamp}}]</span>
- </div>
- <div class="message-body">
- <div class="message-action">
- {{ngModel.actionText}}
- </div>
- <mct-include key="'progress-bar'"
- ng-model="ngModel"
- ng-show="ngModel.progress !== undefined || ngModel.unknownProgress"></mct-include>
- </div>
- <div class="c-overlay__button-bar">
- <button ng-repeat="dialogOption in ngModel.options"
- class="c-button"
- ng-click="dialogOption.callback()">
- {{dialogOption.label}}
- </button>
- <button class="c-button c-button--major"
- ng-if="ngModel.primaryOption"
- ng-click="ngModel.primaryOption.callback()">
- {{ngModel.primaryOption.label}}
- </button>
- </div>
- </div>
-</div> \ No newline at end of file
diff --git a/platform/commonUI/dialog/res/templates/notification-message.html b/platform/commonUI/dialog/res/templates/notification-message.html
deleted file mode 100644
index 8fabb8108..000000000
--- a/platform/commonUI/dialog/res/templates/notification-message.html
+++ /dev/null
@@ -1,25 +0,0 @@
-<div class="c-message"
- ng-class="'message-severity-' + ngModel.severity">
- <div class="w-message-contents">
- <div class="c-message__top-bar">
- <div class="c-message__title">{{ngModel.message}}</div>
- </div>
- <div class="message-body">
- <mct-include key="'progress-bar'"
- ng-model="ngModel"
- ng-show="ngModel.progressPerc !== undefined"></mct-include>
- </div>
- </div>
- <div class="c-overlay__button-bar">
- <button ng-repeat="dialogOption in ngModel.options"
- class="c-button"
- ng-click="dialogOption.callback()">
- {{dialogOption.label}}
- </button>
- <button class="c-button c-button--major"
- ng-if="ngModel.primaryOption"
- ng-click="ngModel.primaryOption.callback()">
- {{ngModel.primaryOption.label}}
- </button>
- </div>
-</div>
diff --git a/platform/commonUI/dialog/res/templates/overlay-blocking-message.html b/platform/commonUI/dialog/res/templates/overlay-blocking-message.html
deleted file mode 100644
index 1730aa5a5..000000000
--- a/platform/commonUI/dialog/res/templates/overlay-blocking-message.html
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
- as represented by the Administrator of the National Aeronautics and Space
- Administration. All rights reserved.
-
- Open MCT is licensed under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0.
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Open MCT includes source code licensed under additional open source
- licenses. See the Open Source Licenses file (LICENSES.md) included with
- this source code distribution or the Licensing information page available
- at runtime from the About dialog for additional information.
--->
-<mct-container key="overlay" class="t-message-single">
- <mct-include key="'message'" ng-model="ngModel">
- </mct-include>
-</mct-container>
diff --git a/platform/commonUI/dialog/res/templates/overlay-dialog.html b/platform/commonUI/dialog/res/templates/overlay-dialog.html
deleted file mode 100644
index 357c3f664..000000000
--- a/platform/commonUI/dialog/res/templates/overlay-dialog.html
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
- as represented by the Administrator of the National Aeronautics and Space
- Administration. All rights reserved.
-
- Open MCT is licensed under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0.
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Open MCT includes source code licensed under additional open source
- licenses. See the Open Source Licenses file (LICENSES.md) included with
- this source code distribution or the Licensing information page available
- at runtime from the About dialog for additional information.
--->
-<mct-container key="overlay">
- <mct-include key="'form-dialog'" ng-model="ngModel">
- </mct-include>
-</mct-container>
diff --git a/platform/commonUI/dialog/res/templates/overlay-message-list.html b/platform/commonUI/dialog/res/templates/overlay-message-list.html
deleted file mode 100644
index caabd7d4b..000000000
--- a/platform/commonUI/dialog/res/templates/overlay-message-list.html
+++ /dev/null
@@ -1,29 +0,0 @@
-<mct-container key="overlay">
- <div class="t-message-list c-overlay__contents">
- <div class="c-overlay__top-bar">
- <div class="c-overlay__dialog-title">{{ngModel.dialog.title}}</div>
- <div class="c-overlay__dialog-hint">Displaying {{ngModel.dialog.messages.length}} message<span
- ng-show="ngModel.dialog.messages.length > 1 ||
- ngModel.dialog.messages.length == 0">s</span>
- </div>
- <button
- ng-if="ngModel.dialog.topBarButton"
- class="c-button c-button--major"
- ng-click="ngModel.topBarButton.onClick">
- {{ ngModel.topBarButton.label }}
- </button>
- </div>
- <div class="w-messages c-overlay__messages">
- <mct-include
- ng-repeat="msg in ngModel.dialog.messages | orderBy: '-'"
- key="'notification-message'" ng-model="msg.model"></mct-include>
- </div>
- <div class="c-overlay__bottom-bar">
- <button ng-repeat="dialogAction in ngModel.dialog.actions"
- class="c-button c-button--major"
- ng-click="dialogAction.action()">
- {{ dialogAction.label }}
- </button>
- </div>
- </div>
-</mct-container>
diff --git a/platform/commonUI/dialog/res/templates/overlay-options.html b/platform/commonUI/dialog/res/templates/overlay-options.html
deleted file mode 100644
index 9dc0ed193..000000000
--- a/platform/commonUI/dialog/res/templates/overlay-options.html
+++ /dev/null
@@ -1,43 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
- as represented by the Administrator of the National Aeronautics and Space
- Administration. All rights reserved.
-
- Open MCT is licensed under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0.
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Open MCT includes source code licensed under additional open source
- licenses. See the Open Source Licenses file (LICENSES.md) included with
- this source code distribution or the Licensing information page available
- at runtime from the About dialog for additional information.
--->
-<mct-container key="c-overlay__contents">
- <div class="c-overlay__top-bar">
- <div class="c-overlay__dialog-title">{{ngModel.dialog.title}}</div>
- <div class="c-overlay__dialog-hint hint">{{ngModel.dialog.hint}}</div>
- </div>
- <div class='c-overlay__contents-main'>
- <mct-include key="ngModel.dialog.template"
- parameters="ngModel.dialog.parameters"
- ng-model="ngModel.dialog.model">
- </mct-include>
- </div>
- <div class="c-overlay__button-bar">
- <button ng-repeat="option in ngModel.dialog.options"
- href=''
- class="s-button lg"
- title="{{option.description}}"
- ng-click="ngModel.confirm(option.key)"
- ng-class="{ major: $first, subtle: !$first }">
- {{option.name}}
- </button>
- </div>
-</mct-container>
diff --git a/platform/commonUI/dialog/res/templates/overlay.html b/platform/commonUI/dialog/res/templates/overlay.html
deleted file mode 100644
index b6e282903..000000000
--- a/platform/commonUI/dialog/res/templates/overlay.html
+++ /dev/null
@@ -1,30 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
- as represented by the Administrator of the National Aeronautics and Space
- Administration. All rights reserved.
-
- Open MCT is licensed under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0.
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Open MCT includes source code licensed under additional open source
- licenses. See the Open Source Licenses file (LICENSES.md) included with
- this source code distribution or the Licensing information page available
- at runtime from the About dialog for additional information.
--->
-<div class="c-overlay l-overlay-small" ng-class="{'delayEntry100ms' : ngModel.delay}">
- <div class="c-overlay__blocker"></div>
- <div class="c-overlay__outer">
- <button ng-click="ngModel.cancel()"
- ng-if="ngModel.cancel"
- class="c-click-icon c-overlay__close-button icon-x"></button>
- <div class="c-overlay__contents" ng-transclude></div>
- </div>
-</div>
diff --git a/platform/commonUI/dialog/src/DialogService.js b/platform/commonUI/dialog/src/DialogService.js
deleted file mode 100644
index 85f784728..000000000
--- a/platform/commonUI/dialog/src/DialogService.js
+++ /dev/null
@@ -1,271 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * This bundle implements the dialog service, which can be used to
- * launch dialogs for user input & notifications.
- * @namespace platform/commonUI/dialog
- */
-define(
- [],
- function () {
- /**
- * The dialog service is responsible for handling window-modal
- * communication with the user, such as displaying forms for user
- * input.
- * @memberof platform/commonUI/dialog
- * @constructor
- */
- function DialogService(overlayService, $q, $log, $document) {
- this.overlayService = overlayService;
- this.$q = $q;
- this.$log = $log;
- this.activeOverlay = undefined;
-
- this.findBody = function () {
- return $document.find('body');
- };
- }
-
- /**
- * @private
- */
- DialogService.prototype.dismissOverlay = function (overlay) {
- //Dismiss the overlay
- overlay.dismiss();
-
- //If dialog is the current active one, dismiss it
- if (overlay === this.activeOverlay) {
- this.activeOverlay = undefined;
- }
- };
-
- DialogService.prototype.getDialogResponse = function (key, model, resultGetter, typeClass) {
- // We will return this result as a promise, because user
- // input is asynchronous.
- var deferred = this.$q.defer(),
- self = this,
- overlay,
- handleEscKeydown;
-
- // Confirm function; this will be passed in to the
- // overlay-dialog template and associated with a
- // OK button click
- function confirm(value) {
- // Pass along the result
- deferred.resolve(resultGetter ? resultGetter() : value);
- self.dismissOverlay(overlay);
- }
-
- // Cancel function; this will be passed in to the
- // overlay-dialog template and associated with a
- // Cancel or X button click
- function cancel() {
- deferred.reject();
- self.findBody().off('keydown', handleEscKeydown);
- self.dismissOverlay(overlay);
- }
-
- handleEscKeydown = function (event) {
- if (event.keyCode === 27) {
- cancel();
- }
- };
-
- // Add confirm/cancel callbacks
- model.confirm = confirm;
- model.cancel = cancel;
-
- this.findBody().on('keydown', handleEscKeydown);
-
- if (this.canShowDialog(model)) {
- // Add the overlay using the OverlayService, which
- // will handle actual insertion into the DOM
- overlay = this.activeOverlay = this.overlayService.createOverlay(
- key,
- model,
- typeClass || "t-dialog"
- );
- } else {
- deferred.reject();
- }
-
- return deferred.promise;
- };
-
- /**
- * Request user input via a window-modal dialog.
- *
- * @param {FormModel} formModel a description of the form
- * to be shown (see platform/forms)
- * @param {object} value the initial state of the form
- * @returns {Promise} a promise for the form value that the
- * user has supplied; this may be rejected if
- * user input cannot be obtained (for instance,
- * because the user cancelled the dialog)
- */
- DialogService.prototype.getUserInput = function (formModel, value) {
- var overlayModel = {
- title: formModel.name,
- message: formModel.message,
- structure: formModel,
- value: value
- };
-
- // Provide result from the model
- function resultGetter() {
- return overlayModel.value;
- }
-
- // Show the overlay-dialog
- return this.getDialogResponse(
- "overlay-dialog",
- overlayModel,
- resultGetter
- );
- };
-
- /**
- * Request that the user chooses from a set of options,
- * which will be shown as buttons.
- *
- * @param dialogModel a description of the dialog to show
- * @return {Promise} a promise for the user's choice
- */
- DialogService.prototype.getUserChoice = function (dialogModel) {
- // Show the overlay-options dialog
- return this.getDialogResponse(
- "overlay-options",
- { dialog: dialogModel }
- );
- };
-
- /**
- * Tests if a dialog can be displayed. A modal dialog may only be
- * displayed if one is not already visible.
- * Will log a warning message if it can't display a dialog.
- * @returns {boolean} true if dialog is currently visible, false
- * otherwise
- */
- DialogService.prototype.canShowDialog = function (dialogModel) {
- if (this.activeOverlay) {
- // Only one dialog should be shown at a time.
- // The application design should be such that
- // we never even try to do this.
- this.$log.warn([
- "Dialog already showing; ",
- "unable to show ",
- dialogModel.title
- ].join(""));
-
- return false;
- } else {
- return true;
- }
- };
-
- /**
- * A user action that can be performed from a blocking dialog. These
- * actions will be rendered as buttons within a blocking dialog.
- *
- * @typedef DialogOption
- * @property {string} label a label to be displayed as the button
- * text for this action
- * @property {function} callback a function to be called when the
- * button is clicked
- */
-
- /**
- * @typedef DialogHandle
- * @property {function} dismiss a function to dismiss the given dialog
- */
-
- /**
- * A description of the model options that may be passed to the
- * showBlockingMessage method. Note that the DialogModel described
- * here is shared with the Notifications framework.
- * @see NotificationService
- *
- * @typedef DialogModel
- * @property {string} title the title to use for the dialog
- * @property {string} severity the severity level of this message.
- * These are defined in a bundle constant with key 'dialogSeverity'
- * @property {string} hint the 'hint' message to show below the title
- * @property {string} actionText text that indicates a current action,
- * shown above a progress bar to indicate what's happening.
- * @property {number} progress a percentage value (1-100)
- * indicating the completion of the blocking task
- * @property {boolean} delay adds a brief delay before loading
- * the dialog. Useful for removing the dialog flicker when the
- * conditions for displaying the dialog change rapidly.
- * @property {string} progressText the message to show below a
- * progress bar to indicate progress. For example, this might be
- * used to indicate time remaining, or items still to process.
- * @property {boolean} unknownProgress some tasks may be
- * impossible to provide an estimate for. Providing a true value for
- * this attribute will indicate to the user that the progress and
- * duration cannot be estimated.
- * @property {DialogOption} primaryOption an action that will
- * be added to the dialog as a button. The primary action can be
- * used as the suggested course of action for the user. Making it
- * distinct from other actions allows it to be styled differently,
- * and treated preferentially in banner mode.
- * @property {DialogOption[]} options a list of actions that will
- * be added to the dialog as buttons.
- */
-
- /**
- * Displays a blocking (modal) dialog. This dialog can be used for
- * displaying messages that require the user's
- * immediate attention. The message may include an indication of
- * progress, as well as a series of actions that
- * the user can take if necessary
- * @param {DialogModel} dialogModel defines options for the dialog
- * @param {typeClass} string tells overlayService that this overlay should use appropriate CSS class
- * @returns {boolean | {DialogHandle}}
- */
- DialogService.prototype.showBlockingMessage = function (dialogModel) {
- if (this.canShowDialog(dialogModel)) {
- // Add the overlay using the OverlayService, which
- // will handle actual insertion into the DOM
- var self = this,
- overlay = this.overlayService.createOverlay(
- "overlay-blocking-message",
- dialogModel,
- "t-dialog-sm"
- );
-
- this.activeOverlay = overlay;
-
- return {
- dismiss: function () {
- self.dismissOverlay(overlay);
- }
- };
- } else {
- return false;
- }
- };
-
- return DialogService;
- }
-);
diff --git a/platform/commonUI/dialog/src/OverlayService.js b/platform/commonUI/dialog/src/OverlayService.js
deleted file mode 100644
index d53be9574..000000000
--- a/platform/commonUI/dialog/src/OverlayService.js
+++ /dev/null
@@ -1,113 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- // Template to inject into the DOM to show the dialog; really just points to
- // the a specific template that can be included via mct-include
- var TEMPLATE = '<mct-include ng-model="overlay" key="key" ng-class="typeClass"></mct-include>';
-
- /**
- * The OverlayService is responsible for pre-pending templates to
- * the body of the document, which is useful for displaying templates
- * which need to block the full screen.
- *
- * This is intended to be used by the DialogService; by design, it
- * does not have any protections in place to prevent multiple overlays
- * from being shown at once. (The DialogService does have these
- * protections, and should be used for most overlay-type interactions,
- * particularly where a multiple-overlay effect is not specifically
- * desired).
- *
- * @memberof platform/commonUI/dialog
- * @constructor
- */
- function OverlayService($document, $compile, $rootScope, $timeout) {
- this.$compile = $compile;
- this.$timeout = $timeout;
-
- // Don't include $document and $rootScope directly;
- // avoids https://docs.angularjs.org/error/ng/cpws
- this.findBody = function () {
- return $document.find('body');
- };
-
- this.newScope = function () {
- return $rootScope.$new();
- };
- }
-
- /**
- * Add a new overlay to the document. This will be
- * prepended to the document body; the overlay's
- * template (as pointed to by the `key` argument) is
- * responsible for having a useful z-order, and for
- * blocking user interactions if appropriate.
- *
- * @param {string} key the symbolic key which identifies
- * the template of the overlay to be shown
- * @param {object} overlayModel the model to pass to the
- * included overlay template (this will be passed
- * in via ng-model)
- * @param {string} typeClass the element class to use in rendering
- * the overlay. Can be specified to provide custom styling of
- * overlays
- */
- OverlayService.prototype.createOverlay = function (key, overlayModel, typeClass) {
- // Create a new scope for this overlay
- var scope = this.newScope(),
- element;
-
- // Stop showing the overlay; additionally, release the scope
- // that it uses.
- function dismiss() {
- scope.$destroy();
- element.remove();
- }
-
- // If no model is supplied, just fill in a default "cancel"
- overlayModel = overlayModel || { cancel: dismiss };
-
- // Populate the scope; will be passed directly to the template
- scope.overlay = overlayModel;
- scope.key = key;
- scope.typeClass = typeClass || 't-dialog';
-
- this.$timeout(() => {
- // Create the overlay element and add it to the document's body
- element = this.$compile(TEMPLATE)(scope);
-
- // Append so that most recent dialog is last in DOM. This means the most recent dialog will be on top when
- // multiple overlays with the same z-index are active.
- this.findBody().append(element);
- });
-
- return {
- dismiss: dismiss
- };
- };
-
- return OverlayService;
- }
-);
diff --git a/platform/commonUI/dialog/test/DialogServiceSpec.js b/platform/commonUI/dialog/test/DialogServiceSpec.js
deleted file mode 100644
index d08c3acd1..000000000
--- a/platform/commonUI/dialog/test/DialogServiceSpec.js
+++ /dev/null
@@ -1,214 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * MCTIncudeSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../src/DialogService"],
- function (DialogService) {
-
- describe("The dialog service", function () {
- var mockOverlayService,
- mockQ,
- mockLog,
- mockOverlay,
- mockDeferred,
- mockDocument,
- mockBody,
- dialogService;
-
- beforeEach(function () {
- mockOverlayService = jasmine.createSpyObj(
- "overlayService",
- ["createOverlay"]
- );
- mockQ = jasmine.createSpyObj(
- "$q",
- ["defer"]
- );
- mockLog = jasmine.createSpyObj(
- "$log",
- ["warn", "info", "debug"]
- );
- mockOverlay = jasmine.createSpyObj(
- "overlay",
- ["dismiss"]
- );
- mockDeferred = jasmine.createSpyObj(
- "deferred",
- ["resolve", "reject"]
- );
- mockDocument = jasmine.createSpyObj(
- "$document",
- ["find"]
- );
- mockBody = jasmine.createSpyObj('body', ['on', 'off']);
- mockDocument.find.and.returnValue(mockBody);
-
- mockDeferred.promise = "mock promise";
-
- mockQ.defer.and.returnValue(mockDeferred);
- mockOverlayService.createOverlay.and.returnValue(mockOverlay);
-
- dialogService = new DialogService(
- mockOverlayService,
- mockQ,
- mockLog,
- mockDocument
- );
- });
-
- it("adds an overlay when user input is requested", function () {
- dialogService.getUserInput({}, {});
- expect(mockOverlayService.createOverlay).toHaveBeenCalled();
- });
-
- it("allows user input to be canceled", function () {
- dialogService.getUserInput({}, { someKey: "some value" });
- mockOverlayService.createOverlay.calls.mostRecent().args[1].cancel();
- expect(mockDeferred.reject).toHaveBeenCalled();
- expect(mockDeferred.resolve).not.toHaveBeenCalled();
- });
-
- it("passes back the result of user input when confirmed", function () {
- var value = { someKey: 42 };
- dialogService.getUserInput({}, value);
- mockOverlayService.createOverlay.calls.mostRecent().args[1].confirm();
- expect(mockDeferred.reject).not.toHaveBeenCalled();
- expect(mockDeferred.resolve).toHaveBeenCalledWith(value);
- });
-
- it("logs a warning when a dialog is already showing", function () {
- dialogService.getUserInput({}, {});
- expect(mockLog.warn).not.toHaveBeenCalled();
- dialogService.getUserInput({}, {});
- expect(mockLog.warn).toHaveBeenCalled();
- expect(mockDeferred.reject).toHaveBeenCalled();
- });
-
- it("can show multiple dialogs if prior ones are dismissed", function () {
- dialogService.getUserInput({}, {});
- expect(mockLog.warn).not.toHaveBeenCalled();
- mockOverlayService.createOverlay.calls.mostRecent().args[1].confirm();
- dialogService.getUserInput({}, {});
- expect(mockLog.warn).not.toHaveBeenCalled();
- expect(mockDeferred.reject).not.toHaveBeenCalled();
- });
-
- it("provides an options dialogs", function () {
- var dialogModel = {};
- dialogService.getUserChoice(dialogModel);
- expect(mockOverlayService.createOverlay).toHaveBeenCalledWith(
- 'overlay-options',
- {
- dialog: dialogModel,
- confirm: jasmine.any(Function),
- cancel: jasmine.any(Function)
- },
- 't-dialog'
- );
- });
-
- it("invokes the overlay service with the correct parameters when"
- + " a blocking dialog is requested", function () {
- var dialogModel = {};
- expect(dialogService.showBlockingMessage(dialogModel)).not.toBe(false);
- expect(mockOverlayService.createOverlay).toHaveBeenCalledWith(
- "overlay-blocking-message",
- dialogModel,
- "t-dialog-sm"
- );
- });
-
- it("adds a keydown event listener to the body", function () {
- dialogService.getUserInput({}, {});
- expect(mockDocument.find).toHaveBeenCalledWith("body");
- expect(mockBody.on).toHaveBeenCalledWith("keydown", jasmine.any(Function));
- });
-
- it("destroys the event listener when the dialog is cancelled", function () {
- dialogService.getUserInput({}, {});
- mockOverlayService.createOverlay.calls.mostRecent().args[1].cancel();
- expect(mockBody.off).toHaveBeenCalledWith("keydown", jasmine.any(Function));
- });
-
- it("cancels the dialog when an escape keydown event is triggered", function () {
- dialogService.getUserInput({}, {});
- mockBody.on.calls.mostRecent().args[1]({
- keyCode: 27
- });
- expect(mockDeferred.reject).toHaveBeenCalled();
- expect(mockDeferred.resolve).not.toHaveBeenCalled();
- });
-
- it("ignores non escape keydown events", function () {
- dialogService.getUserInput({}, {});
- mockBody.on.calls.mostRecent().args[1]({
- keyCode: 13
- });
- expect(mockDeferred.reject).not.toHaveBeenCalled();
- expect(mockDeferred.resolve).not.toHaveBeenCalled();
- });
-
- describe("the blocking message dialog", function () {
- var dialogModel = {};
- var dialogHandle;
-
- beforeEach(function () {
- dialogHandle = dialogService.showBlockingMessage(dialogModel);
- });
-
- it("returns a handle to the dialog", function () {
- expect(dialogHandle).not.toBe(undefined);
- });
-
- it("dismissing the dialog dismisses the overlay", function () {
- dialogHandle.dismiss();
- expect(mockOverlay.dismiss).toHaveBeenCalled();
- });
-
- it("individual dialogs can be dismissed", function () {
- var secondDialogHandle,
- secondMockOverlay;
-
- dialogHandle.dismiss();
-
- secondMockOverlay = jasmine.createSpyObj(
- "overlay",
- ["dismiss"]
- );
- mockOverlayService.createOverlay.and.returnValue(secondMockOverlay);
- secondDialogHandle = dialogService.showBlockingMessage(dialogModel);
-
- //Dismiss the first dialog. It should only dismiss if it
- // is active
- dialogHandle.dismiss();
- expect(secondMockOverlay.dismiss).not.toHaveBeenCalled();
- secondDialogHandle.dismiss();
- expect(secondMockOverlay.dismiss).toHaveBeenCalled();
- });
- });
-
- });
- }
-);
diff --git a/platform/commonUI/dialog/test/OverlayServiceSpec.js b/platform/commonUI/dialog/test/OverlayServiceSpec.js
deleted file mode 100644
index e360011a9..000000000
--- a/platform/commonUI/dialog/test/OverlayServiceSpec.js
+++ /dev/null
@@ -1,104 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * MCTIncudeSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../src/OverlayService"],
- function (OverlayService) {
-
- describe("The overlay service", function () {
- var mockDocument,
- mockCompile,
- mockRootScope,
- mockBody,
- mockTemplate,
- mockElement,
- mockScope,
- mockTimeout,
- overlayService;
-
- beforeEach(function () {
- mockDocument = jasmine.createSpyObj("$document", ["find"]);
- mockCompile = jasmine.createSpy("$compile");
- mockRootScope = jasmine.createSpyObj("$rootScope", ["$new"]);
- mockBody = jasmine.createSpyObj("body", ["append"]);
- mockTemplate = jasmine.createSpy("template");
- mockElement = jasmine.createSpyObj("element", ["remove"]);
- mockScope = jasmine.createSpyObj("scope", ["$destroy"]);
- mockTimeout = function (callback) {
- callback();
- };
-
- mockDocument.find.and.returnValue(mockBody);
- mockCompile.and.returnValue(mockTemplate);
- mockRootScope.$new.and.returnValue(mockScope);
- mockTemplate.and.returnValue(mockElement);
-
- overlayService = new OverlayService(
- mockDocument,
- mockCompile,
- mockRootScope,
- mockTimeout
- );
- });
-
- it("prepends an mct-include to create overlays", function () {
- overlayService.createOverlay("test", {});
- expect(mockCompile).toHaveBeenCalled();
- expect(mockCompile.calls.mostRecent().args[0].indexOf("mct-include"))
- .not.toEqual(-1);
- });
-
- it("adds the templated element to the body", function () {
- overlayService.createOverlay("test", {});
- expect(mockBody.append).toHaveBeenCalledWith(mockElement);
- });
-
- it("places the provided model/key in its template's scope", function () {
- overlayService.createOverlay("test", { someKey: 42 });
- expect(mockScope.overlay).toEqual({ someKey: 42 });
- expect(mockScope.key).toEqual("test");
-
- // Make sure this is actually what was rendered, too
- expect(mockTemplate).toHaveBeenCalledWith(mockScope);
- });
-
- it("removes the prepended element on request", function () {
- var overlay = overlayService.createOverlay("test", {});
-
- // Verify precondition
- expect(mockElement.remove).not.toHaveBeenCalled();
- expect(mockScope.$destroy).not.toHaveBeenCalled();
-
- // Dismiss the overlay
- overlay.dismiss();
-
- // Now it should have been removed, and the scope destroyed
- expect(mockElement.remove).toHaveBeenCalled();
- expect(mockScope.$destroy).toHaveBeenCalled();
- });
-
- });
- }
-);
diff --git a/platform/commonUI/edit/README.md b/platform/commonUI/edit/README.md
deleted file mode 100644
index ca52b78f1..000000000
--- a/platform/commonUI/edit/README.md
+++ /dev/null
@@ -1,45 +0,0 @@
-Contains sources and resources associated with Edit mode.
-
-# Extensions
-
-# Toolbars
-
-Views may specify the contents of a toolbar through a `toolbar`
-property in their bundle definition. This should appear as the
-structure one would provide to the `mct-toolbar` directive,
-except additional properties are recognized to support the
-mediation between toolbar contents, user interaction, and the
-current selection (as read from the `selection` property of the
-view's scope.) These additional properties are:
-
-* `property`: Name of the property within a selected object. If,
- for any given object in the selection, that field is a function,
- then that function is assumed to be an accessor-mutator function
- (that is, it will be called with no arguments to get, and with
- an argument to set.)
-* `method`: Name of a method to invoke upon a selected object when
- a control is activated, e.g. on a button click.
-* `exclusive`: Optional; true if this control should be considered
- applicable only when all elements in the selection has
- the associated property. Otherwise, only at least one member of the
- current selection must have this property for the control to be shown.
-
-Controls in the toolbar are shown based on applicability to the
-current selection. Applicability for a given member of the selection
-is determined by the presence of absence of the named `property`
-field. As a consequence of this, if `undefined` is a valid value for
-that property, an accessor-mutator function must be used. Likewise,
-if toolbar properties are meant to be view-global (as opposed to
-per-selection) then the view must include some object to act as its
-proxy in the current selection (in addition to whatever objects the
-user will conceive of as part of the current selection), typically
-with `inclusive` set to `true`.
-
-## Selection
-
-The `selection` property of a view's scope in Edit mode will be
-initialized to an empty array. This array's contents may be modified
-to implicitly change the contents of the toolbar based on the rules
-described above. Care should be taken to modify this array in-place
-instead of shadowing it (as the selection will typically
-be a few scopes up the hierarchy from the view's actual scope.)
diff --git a/platform/commonUI/edit/bundle.js b/platform/commonUI/edit/bundle.js
deleted file mode 100644
index dc0299ce6..000000000
--- a/platform/commonUI/edit/bundle.js
+++ /dev/null
@@ -1,209 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
- "./src/controllers/EditActionController",
- "./src/controllers/EditPanesController",
- "./src/controllers/EditObjectController",
- "./src/actions/EditAndComposeAction",
- "./src/actions/EditAction",
- "./src/actions/SaveAction",
- "./src/actions/SaveAndStopEditingAction",
- "./src/actions/CancelAction",
- "./src/policies/EditPersistableObjectsPolicy",
- "./src/representers/EditRepresenter",
- "./src/capabilities/EditorCapability",
- "./res/templates/library.html",
- "./res/templates/edit-object.html",
- "./res/templates/edit-action-buttons.html",
- "./res/templates/topbar-edit.html"
-], function (
- EditActionController,
- EditPanesController,
- EditObjectController,
- EditAndComposeAction,
- EditAction,
- SaveAction,
- SaveAndStopEditingAction,
- CancelAction,
- EditPersistableObjectsPolicy,
- EditRepresenter,
- EditorCapability,
- libraryTemplate,
- editObjectTemplate,
- editActionButtonsTemplate,
- topbarEditTemplate
-) {
- return {
- name: "platform/commonUI/edit",
- definition: {
- "extensions": {
- "controllers": [
- {
- "key": "EditActionController",
- "implementation": EditActionController,
- "depends": [
- "$scope"
- ]
- },
- {
- "key": "EditPanesController",
- "implementation": EditPanesController,
- "depends": [
- "$scope"
- ]
- },
- {
- "key": "EditObjectController",
- "implementation": EditObjectController,
- "depends": [
- "$scope",
- "$location",
- "navigationService"
- ]
- }
- ],
- "actions": [
- {
- "key": "compose",
- "implementation": EditAndComposeAction
- },
- {
- "key": "edit",
- "implementation": EditAction,
- "depends": [
- "$location",
- "navigationService",
- "$log"
- ],
- "description": "Edit",
- "category": "view-control",
- "cssClass": "major icon-pencil",
- "group": "action",
- "priority": 10
- },
- {
- "key": "save-and-stop-editing",
- "category": "save",
- "implementation": SaveAndStopEditingAction,
- "name": "Save and Finish Editing",
- "cssClass": "icon-save labeled",
- "description": "Save changes made to these objects.",
- "depends": [
- "dialogService",
- "notificationService"
- ]
- },
- {
- "key": "save",
- "category": "save",
- "implementation": SaveAction,
- "name": "Save and Continue Editing",
- "cssClass": "icon-save labeled",
- "description": "Save changes made to these objects.",
- "depends": [
- "dialogService",
- "notificationService"
- ]
- },
- {
- "key": "cancel",
- "category": "conclude-editing",
- "implementation": CancelAction,
- // Because we use the name as label for edit buttons and mct-control buttons need
- // the label to be set to undefined in order to not apply the labeled CSS rule.
- "name": undefined,
- "cssClass": "icon-x no-label",
- "description": "Discard changes made to these objects.",
- "depends": []
- }
- ],
- "policies": [
- {
- "category": "action",
- "implementation": EditPersistableObjectsPolicy,
- "depends": ["openmct"]
- }
- ],
- "templates": [
- {
- "key": "edit-library",
- "template": libraryTemplate
- }
- ],
- "representations": [
- {
- "key": "edit-object",
- "template": editObjectTemplate,
- "uses": [
- "view"
- ],
- "gestures": [
- "drop"
- ]
- },
- {
- "key": "edit-action-buttons",
- "template": editActionButtonsTemplate,
- "uses": [
- "action"
- ]
- },
- {
- "key": "topbar-edit",
- "template": topbarEditTemplate
- }
- ],
- "representers": [
- {
- "implementation": EditRepresenter,
- "depends": [
- "$log"
- ]
- }
- ],
- "capabilities": [
- {
- "key": "editor",
- "name": "Editor Capability",
- "description": "Provides transactional editing capabilities",
- "implementation": EditorCapability,
- "depends": [
- "openmct"
- ]
- }
- ],
- "runs": [
- {
- depends: [
- "toolbars[]",
- "openmct"
- ],
- implementation: function (toolbars, openmct) {
- toolbars.forEach(openmct.toolbars.addProvider, openmct.toolbars);
- }
- }
- ]
- }
- }
- };
-});
diff --git a/platform/commonUI/edit/res/templates/edit-action-buttons.html b/platform/commonUI/edit/res/templates/edit-action-buttons.html
deleted file mode 100644
index 52fa2ec72..000000000
--- a/platform/commonUI/edit/res/templates/edit-action-buttons.html
+++ /dev/null
@@ -1,53 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
- as represented by the Administrator of the National Aeronautics and Space
- Administration. All rights reserved.
-
- Open MCT is licensed under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0.
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Open MCT includes source code licensed under additional open source
- licenses. See the Open Source Licenses file (LICENSES.md) included with
- this source code distribution or the Licensing information page available
- at runtime from the About dialog for additional information.
--->
-<span ng-controller="EditActionController">
- <!-- If there's a single save action show a button, otherwise show a dropdown with all save actions. -->
- <span ng-if="saveActions.length === 1">
- <mct-control key="'button'"
- structure="{
- text: saveActions[0].getMetadata().name,
- click: actionPerformer(saveActions[0]),
- cssClass: 'major ' + saveActions[0].getMetadata().cssClass
- }">
- </mct-control>
- </span>
-
- <span ng-if="saveActions.length > 1">
- <mct-control key="'menu-button'"
- structure="{
- options: saveActionsAsMenuOptions,
- click: saveActionMenuClickHandler,
- cssClass: 'btn-bar right icon-save no-label major'
- }">
- </mct-control>
- </span>
-
- <span ng-repeat="currentAction in otherEditActions">
- <mct-control key="'button'"
- structure="{
- text: currentAction.getMetadata().name,
- click: actionPerformer(currentAction),
- cssClass: currentAction.getMetadata().cssClass
- }">
- </mct-control>
- </span>
-</span>
diff --git a/platform/commonUI/edit/res/templates/edit-object.html b/platform/commonUI/edit/res/templates/edit-object.html
deleted file mode 100644
index 6304215f0..000000000
--- a/platform/commonUI/edit/res/templates/edit-object.html
+++ /dev/null
@@ -1,78 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
- as represented by the Administrator of the National Aeronautics and Space
- Administration. All rights reserved.
-
- Open MCT is licensed under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0.
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Open MCT includes source code licensed under additional open source
- licenses. See the Open Source Licenses file (LICENSES.md) included with
- this source code distribution or the Licensing information page available
- at runtime from the About dialog for additional information.
--->
-<div class="abs l-flex-col" ng-controller="EditObjectController as EditObjectController">
- <div class="holder flex-elem l-flex-row object-browse-bar ">
- <div class="items-select left flex-elem l-flex-row grows">
- <mct-representation key="'back-arrow'"
- mct-object="domainObject"
- class="flex-elem l-back">
- </mct-representation>
- <mct-representation key="'object-header'"
- mct-object="domainObject"
- class="l-flex-row flex-elem grows object-header">
- </mct-representation>
- </div>
- <div class="btn-bar right l-flex-row flex-elem flex-justify-end flex-fixed">
- <mct-representation key="'switcher'"
- mct-object="domainObject"
- ng-model="representation">
- </mct-representation>
- <!-- Temporarily, on mobile, the action buttons are hidden-->
- <mct-representation key="'action-group'"
- mct-object="domainObject"
- parameters="{ category: 'view-control' }"
- class="mobile-hide">
- </mct-representation>
- </div>
- </div>
- <div class="holder l-flex-col flex-elem grows l-object-wrapper">
- <div class="holder l-flex-col flex-elem grows l-object-wrapper-inner">
- <!-- Toolbar and Save/Cancel buttons -->
- <div class="l-edit-controls flex-elem l-flex-row flex-align-end">
- <mct-toolbar name="mctToolbar"
- structure="editToolbar.structure"
- ng-model="editToolbar.state"
- class="flex-elem grows">
- </mct-toolbar>
- <mct-representation key="'edit-action-buttons'"
- mct-object="domainObject"
- class='flex-elem conclude-editing'>
- </mct-representation>
-
- </div>
- <mct-representation key="representation.selected.key"
- mct-object="representation.selected.key && domainObject"
- class="abs flex-elem grows object-holder-main scroll"
- mct-selectable="{
- item: domainObject.useCapability('adapter'),
- oldItem: domainObject
- }"
- mct-init-select>
- </mct-representation>
- </div><!--/ l-object-wrapper-inner -->
- </div>
- <mct-representation mct-object="domainObject"
- key="'time-conductor'"
- class="abs holder flex-elem flex-fixed l-flex-row l-time-conductor-holder">
- </mct-representation>
-
-</div>
diff --git a/platform/commonUI/edit/res/templates/library.html b/platform/commonUI/edit/res/templates/library.html
deleted file mode 100644
index e39ca9ff1..000000000
--- a/platform/commonUI/edit/res/templates/library.html
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
- as represented by the Administrator of the National Aeronautics and Space
- Administration. All rights reserved.
-
- Open MCT is licensed under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0.
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Open MCT includes source code licensed under additional open source
- licenses. See the Open Source Licenses file (LICENSES.md) included with
- this source code distribution or the Licensing information page available
- at runtime from the About dialog for additional information.
--->
-<mct-representation key="'tree'"
- mct-object="parameters.domainObject"
- parameters="parameters">
-</mct-representation>
diff --git a/platform/commonUI/edit/res/templates/topbar-edit.html b/platform/commonUI/edit/res/templates/topbar-edit.html
deleted file mode 100644
index 0624e7550..000000000
--- a/platform/commonUI/edit/res/templates/topbar-edit.html
+++ /dev/null
@@ -1,39 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
- as represented by the Administrator of the National Aeronautics and Space
- Administration. All rights reserved.
-
- Open MCT is licensed under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0.
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Open MCT includes source code licensed under additional open source
- licenses. See the Open Source Licenses file (LICENSES.md) included with
- this source code distribution or the Licensing information page available
- at runtime from the About dialog for additional information.
--->
-<div class='top-bar edit abs'>
- <mct-representation key="'object-header'"
- mct-object="domainObject"
- parameters="{ mode: 'Edit' }"
- class="l-flex-row flex-elem grows object-header">
- </mct-representation>
- <div class='buttons-main btn-bar buttons abs'>
- <mct-representation key="'switcher'"
- mct-object="domainObject"
- ng-model="ngModel">
- </mct-representation>
-
- <mct-representation key="'edit-action-buttons'"
- mct-object="domainObject"
- class='conclude-editing'>
- </mct-representation>
- </div>
-</div>
diff --git a/platform/commonUI/edit/src/actions/CancelAction.js b/platform/commonUI/edit/src/actions/CancelAction.js
deleted file mode 100644
index 83b8cf99d..000000000
--- a/platform/commonUI/edit/src/actions/CancelAction.js
+++ /dev/null
@@ -1,90 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- function () {
-
- /**
- * The "Cancel" action; the action triggered by clicking Cancel from
- * Edit Mode. Exits the editing user interface and invokes object
- * capabilities to persist the changes that have been made.
- * @constructor
- * @memberof platform/commonUI/edit
- * @implements {Action}
- */
- function CancelAction(context) {
- this.domainObject = context.domainObject;
- }
-
- /**
- * Cancel editing.
- *
- * @returns {Promise} a promise that will be fulfilled when
- * cancellation has completed
- */
- CancelAction.prototype.perform = function () {
- var domainObject = this.domainObject;
-
- function returnToBrowse() {
- var parent;
-
- //If the object existed already, navigate to refresh view
- // with previous object state.
- if (domainObject.getModel().persisted) {
- return domainObject.getCapability("action").perform("navigate");
- } else {
- //If the object was new, and user has cancelled, then
- //navigate back to parent because nothing to show.
- return domainObject.getCapability("location").getOriginal().then(function (original) {
- parent = original.getCapability("context").getParent();
-
- return parent.getCapability("action").perform("navigate");
- });
- }
- }
-
- function cancel() {
- return domainObject.getCapability("editor").finish();
- }
-
- //Do navigation first in order to trigger unsaved changes dialog
- return returnToBrowse()
- .then(cancel);
- };
-
- /**
- * Check if this action is applicable in a given context.
- * This will ensure that a domain object is present in the context,
- * and that this domain object is in Edit mode.
- * @returns {boolean} true if applicable
- */
- CancelAction.appliesTo = function (context) {
- var domainObject = (context || {}).domainObject;
-
- return domainObject !== undefined
- && domainObject.hasCapability('editor')
- && domainObject.getCapability('editor').isEditContextRoot();
- };
-
- return CancelAction;
- }
-);
diff --git a/platform/commonUI/edit/src/actions/EditAction.js b/platform/commonUI/edit/src/actions/EditAction.js
deleted file mode 100644
index 1a5c62046..000000000
--- a/platform/commonUI/edit/src/actions/EditAction.js
+++ /dev/null
@@ -1,101 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Module defining EditAction. Created by vwoeltje on 11/14/14.
- */
-define(
- [],
- function () {
-
- // A no-op action to return in the event that the action cannot
- // be completed.
- var NULL_ACTION = {
- perform: function () {
- return undefined;
- }
- };
-
- /**
- * The Edit action is performed when the user wishes to enter Edit
- * mode (typically triggered by the Edit button.) This will
- * show the user interface for editing (by way of a change in
- * route)
- * @memberof platform/commonUI/edit
- * @constructor
- * @implements {Action}
- */
- function EditAction($location, navigationService, $log, context) {
- var domainObject = (context || {}).domainObject;
-
- // We cannot enter Edit mode if we have no domain object to
- // edit, so verify that one was defined as part of the
- // context. (This is also verified in appliesTo, so this
- // would indicate abnormal behavior.)
- if (!domainObject) {
- $log.warn([
- "No domain object to edit; ",
- "edit action is not valid."
- ].join(""));
-
- return NULL_ACTION;
- }
-
- this.domainObject = domainObject;
- this.$location = $location;
- this.navigationService = navigationService;
- }
-
- /**
- * Enter edit mode.
- */
- EditAction.prototype.perform = function () {
-
- //If this is not the currently navigated object, then navigate
- // to it.
- if (this.navigationService.getNavigation() !== this.domainObject) {
- this.navigationService.setNavigation(this.domainObject);
- }
-
- this.domainObject.useCapability("editor");
- };
-
- /**
- * Check for applicability; verify that a domain object is present
- * for this action to be performed upon.
- * @param {ActionContext} context the context in which this action
- * will be performed; should contain a `domainObject` property
- */
- EditAction.appliesTo = function (context) {
- var domainObject = (context || {}).domainObject,
- type = domainObject && domainObject.getCapability('type');
-
- // Only allow editing of types that support it and are not already
- // being edited
- return type && type.hasFeature('creation')
- && domainObject.hasCapability('editor')
- && !domainObject.getCapability('editor').isEditContextRoot();
- };
-
- return EditAction;
- }
-);
diff --git a/platform/commonUI/edit/src/actions/EditAndComposeAction.js b/platform/commonUI/edit/src/actions/EditAndComposeAction.js
deleted file mode 100644
index c3ace5ea3..000000000
--- a/platform/commonUI/edit/src/actions/EditAndComposeAction.js
+++ /dev/null
@@ -1,59 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- /**
- * Add one domain object to another's composition.
- * @constructor
- * @memberof platform/commonUI/edit
- * @implements {Action}
- */
- function EditAndComposeAction(context) {
- this.domainObject = (context || {}).domainObject;
- this.selectedObject = (context || {}).selectedObject;
- }
-
- EditAndComposeAction.prototype.perform = function () {
- var self = this,
- editAction = this.domainObject.getCapability('action').getActions("edit")[0];
-
- // Link these objects
- function doLink() {
- var composition = self.domainObject
- && self.domainObject.getCapability('composition');
-
- return composition && composition.add(self.selectedObject);
- }
-
- if (editAction) {
- editAction.perform();
- }
-
- return this.selectedObject && doLink();
- };
-
- return EditAndComposeAction;
- }
-);
diff --git a/platform/commonUI/edit/src/actions/SaveAction.js b/platform/commonUI/edit/src/actions/SaveAction.js
deleted file mode 100644
index 6cfabe2c8..000000000
--- a/platform/commonUI/edit/src/actions/SaveAction.js
+++ /dev/null
@@ -1,98 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ['./SaveInProgressDialog'],
- function (SaveInProgressDialog) {
-
- /**
- * The "Save" action; it invokes object capabilities to persist
- * the changes that have been made.
- * @constructor
- * @implements {Action}
- * @memberof platform/commonUI/edit
- */
- function SaveAction(
- dialogService,
- notificationService,
- context
- ) {
- this.domainObject = (context || {}).domainObject;
- this.dialogService = dialogService;
- this.notificationService = notificationService;
- }
-
- /**
- * Save changes.
- *
- * @returns {Promise} a promise that will be fulfilled when
- * cancellation has completed
- * @memberof platform/commonUI/edit.SaveAction#
- */
- SaveAction.prototype.perform = function () {
- var self = this,
- domainObject = this.domainObject,
- dialog = new SaveInProgressDialog(this.dialogService);
-
- // Invoke any save behavior introduced by the editor capability;
- // this is introduced by EditableDomainObject which is
- // used to insulate underlying objects from changes made
- // during editing.
- function doSave() {
- return domainObject.getCapability("editor").save();
- }
-
- function onSuccess() {
- dialog.hide();
- self.notificationService.info("Save Succeeded");
- }
-
- function onFailure() {
- dialog.hide();
- self.notificationService.error("Save Failed");
- }
-
- dialog.show();
-
- return doSave()
- .then(onSuccess)
- .catch(onFailure);
- };
-
- /**
- * Check if this action is applicable in a given context.
- * This will ensure that a domain object is present in the context,
- * and that this domain object is in Edit mode.
- * @returns true if applicable
- */
- SaveAction.appliesTo = function (context) {
- var domainObject = (context || {}).domainObject;
-
- return domainObject !== undefined
- && domainObject.hasCapability('editor')
- && domainObject.getCapability('editor').isEditContextRoot()
- && domainObject.getModel().persisted !== undefined;
- };
-
- return SaveAction;
- }
-);
diff --git a/platform/commonUI/edit/src/actions/SaveAndStopEditingAction.js b/platform/commonUI/edit/src/actions/SaveAndStopEditingAction.js
deleted file mode 100644
index 32412bcfc..000000000
--- a/platform/commonUI/edit/src/actions/SaveAndStopEditingAction.js
+++ /dev/null
@@ -1,75 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["./SaveAction"],
- function (SaveAction) {
-
- /**
- * The "Save and Stop Editing" action performs a [Save action]{@link SaveAction}
- * on the object under edit followed by exiting the edit user interface.
- * @constructor
- * @implements {Action}
- * @memberof platform/commonUI/edit
- */
- function SaveAndStopEditingAction(
- dialogService,
- notificationService,
- context
- ) {
- this.context = context;
- this.domainObject = (context || {}).domainObject;
- this.dialogService = dialogService;
- this.notificationService = notificationService;
- }
-
- /**
- * Trigger a save operation and exit edit mode.
- *
- * @returns {Promise} a promise that will be fulfilled when
- * cancellation has completed
- * @memberof platform/commonUI/edit.SaveAndStopEditingAction#
- */
- SaveAndStopEditingAction.prototype.perform = function () {
- var domainObject = this.domainObject,
- saveAction = new SaveAction(this.dialogService, this.notificationService, this.context);
-
- function closeEditor() {
- return domainObject.getCapability("editor").finish();
- }
-
- return saveAction.perform()
- .then(closeEditor)
- .catch(closeEditor);
- };
-
- /**
- * Check if this action is applicable in a given context.
- * This will ensure that a domain object is present in the context,
- * and that this domain object is in Edit mode.
- * @returns true if applicable
- */
- SaveAndStopEditingAction.appliesTo = SaveAction.appliesTo;
-
- return SaveAndStopEditingAction;
- }
-);
diff --git a/platform/commonUI/edit/src/actions/SaveInProgressDialog.js b/platform/commonUI/edit/src/actions/SaveInProgressDialog.js
deleted file mode 100644
index ea419e704..000000000
--- a/platform/commonUI/edit/src/actions/SaveInProgressDialog.js
+++ /dev/null
@@ -1,24 +0,0 @@
-define([], function () {
- function SaveInProgressDialog(dialogService) {
- this.dialogService = dialogService;
- this.dialog = undefined;
- }
-
- SaveInProgressDialog.prototype.show = function () {
- this.dialog = this.dialogService.showBlockingMessage({
- title: "Saving",
- hint: "Do not navigate away from this page or close this browser tab while this message is displayed.",
- unknownProgress: true,
- severity: "info",
- delay: true
- });
- };
-
- SaveInProgressDialog.prototype.hide = function () {
- if (this.dialog) {
- this.dialog.dismiss();
- }
- };
-
- return SaveInProgressDialog;
-});
diff --git a/platform/commonUI/edit/src/capabilities/EditorCapability.js b/platform/commonUI/edit/src/capabilities/EditorCapability.js
deleted file mode 100644
index db3970c14..000000000
--- a/platform/commonUI/edit/src/capabilities/EditorCapability.js
+++ /dev/null
@@ -1,64 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- /**
- * A capability that implements an editing 'session' for a domain
- * object. An editing session is initiated via a call to .edit().
- * Once initiated, any persist operations will be queued pending a
- * subsequent call to [.save()](@link #save) or [.finish()](@link
- * #finish).
- * @param domainObject
- * @constructor
- */
- function EditorCapability(
- openmct,
- domainObject
- ) {
- this.openmct = openmct;
- this.domainObject = domainObject;
- }
-
- /**
- * Determines whether this object, or any of its ancestors are
- * currently being edited.
- * @returns boolean
- */
- EditorCapability.prototype.inEditContext = function () {
- return this.openmct.editor.isEditing();
- };
-
- /**
- * Is this the root editing object (ie. the object that the user
- * clicked 'edit' on)?
- * @returns {*}
- */
- EditorCapability.prototype.isEditContextRoot = function () {
- return this.openmct.editor.isEditing();
- };
-
- return EditorCapability;
- }
-);
diff --git a/platform/commonUI/edit/src/controllers/EditActionController.js b/platform/commonUI/edit/src/controllers/EditActionController.js
deleted file mode 100644
index f11579bbf..000000000
--- a/platform/commonUI/edit/src/controllers/EditActionController.js
+++ /dev/null
@@ -1,79 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Module defining EditActionController. Created by vwoeltje on 11/17/14.
- */
-define(
- [],
- function () {
-
- var SAVE_ACTION_CONTEXT = { category: 'save' };
- var OTHERS_ACTION_CONTEXT = { category: 'conclude-editing' };
-
- /**
- * Controller which supplies action instances for Save/Cancel.
- * @memberof platform/commonUI/edit
- * @constructor
- */
- function EditActionController($scope) {
-
- function actionToMenuOption(action) {
- return {
- key: action,
- name: action.getMetadata().name,
- cssClass: action.getMetadata().cssClass
- };
- }
-
- // Maintain all "conclude-editing" and "save" actions in the
- // present context.
- function updateActions() {
- $scope.saveActions = $scope.action
- ? $scope.action.getActions(SAVE_ACTION_CONTEXT)
- : [];
-
- $scope.saveActionsAsMenuOptions = $scope.saveActions.map(actionToMenuOption);
-
- $scope.saveActionMenuClickHandler = function (clickedAction) {
- clickedAction.perform();
- };
-
- $scope.otherEditActions = $scope.action
- ? $scope.action.getActions(OTHERS_ACTION_CONTEXT)
- : [];
-
- // Required because Angular does not allow 'bind'
- // in expressions.
- $scope.actionPerformer = function (action) {
- return action.perform.bind(action);
- };
- }
-
- // Update set of actions whenever the action capability
- // changes or becomes available.
- $scope.$watch("action", updateActions);
- }
-
- return EditActionController;
- }
-);
diff --git a/platform/commonUI/edit/src/controllers/EditObjectController.js b/platform/commonUI/edit/src/controllers/EditObjectController.js
deleted file mode 100644
index 595e008ac..000000000
--- a/platform/commonUI/edit/src/controllers/EditObjectController.js
+++ /dev/null
@@ -1,86 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * This bundle implements Edit mode.
- * @namespace platform/commonUI/edit
- */
-define(
- [],
- function () {
-
- function cancelEditing(domainObject) {
- var navigatedObject = domainObject,
- editorCapability = navigatedObject
- && navigatedObject.getCapability("editor");
-
- return editorCapability
- && editorCapability.finish();
- }
-
- /**
- * Controller which is responsible for populating the scope for
- * Edit mode
- * @memberof platform/commonUI/edit
- * @constructor
- */
- function EditObjectController($scope, $location, navigationService) {
- this.scope = $scope;
- var domainObject = $scope.domainObject;
-
- var removeCheck = navigationService
- .checkBeforeNavigation(function () {
- return "Continuing will cause the loss of any unsaved changes.";
- });
-
- $scope.$on('$destroy', function () {
- removeCheck();
- cancelEditing(domainObject);
- });
-
- function setViewForDomainObject() {
-
- var locationViewKey = $location.search().view;
-
- function selectViewIfMatching(view) {
- if (view.key === locationViewKey) {
- $scope.representation = $scope.representation || {};
- $scope.representation.selected = view;
- }
- }
-
- if (locationViewKey) {
- ((domainObject && domainObject.useCapability('view')) || [])
- .forEach(selectViewIfMatching);
- }
- }
-
- setViewForDomainObject();
-
- $scope.doAction = function (action) {
- return $scope[action] && $scope[action]();
- };
- }
-
- return EditObjectController;
- }
-);
diff --git a/platform/commonUI/edit/src/controllers/EditPanesController.js b/platform/commonUI/edit/src/controllers/EditPanesController.js
deleted file mode 100644
index 9593250a8..000000000
--- a/platform/commonUI/edit/src/controllers/EditPanesController.js
+++ /dev/null
@@ -1,66 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- /**
- * Supports the Library and Elements panes in Edit mode.
- * @memberof platform/commonUI/edit
- * @constructor
- */
- function EditPanesController($scope) {
- var self = this;
-
- // Update root object based on represented object
- function updateRoot(domainObject) {
- var root = self.rootDomainObject,
- context = domainObject
- && domainObject.getCapability('context'),
- newRoot = context && context.getTrueRoot(),
- oldId = root && root.getId(),
- newId = newRoot && newRoot.getId();
-
- // Only update if this has actually changed,
- // to avoid excessive refreshing.
- if (oldId !== newId) {
- self.rootDomainObject = newRoot;
- }
- }
-
- // Update root when represented object changes
- $scope.$watch('domainObject', updateRoot);
- }
-
- /**
- * Get the root-level domain object, as reported by the
- * represented domain object.
- * @returns {DomainObject} the root object
- */
- EditPanesController.prototype.getRoot = function () {
- return this.rootDomainObject;
- };
-
- return EditPanesController;
- }
-);
diff --git a/platform/commonUI/edit/src/policies/EditPersistableObjectsPolicy.js b/platform/commonUI/edit/src/policies/EditPersistableObjectsPolicy.js
deleted file mode 100644
index 5cb7f76df..000000000
--- a/platform/commonUI/edit/src/policies/EditPersistableObjectsPolicy.js
+++ /dev/null
@@ -1,57 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2016, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ['objectUtils'],
- function (objectUtils) {
-
- /**
- * Policy that prevents editing of any object from a provider that does not
- * support persistence (ie. the 'save' operation). Editing is prevented
- * as a subsequent save would fail, causing the loss of a user's changes.
- * @param openmct
- * @constructor
- */
- function EditPersistableObjectsPolicy(openmct) {
- this.openmct = openmct;
- }
-
- EditPersistableObjectsPolicy.prototype.allow = function (action, context) {
- var domainObject = context.domainObject;
- var key = action.getMetadata().key;
- var category = (context || {}).category;
-
- // Use category to selectively block edit from the view. Edit action
- // is also invoked during the create process which should be allowed,
- // because it may be saved elsewhere
- if ((key === 'edit' && category === 'view-control') || key === 'properties') {
- let identifier = this.openmct.objects.parseKeyString(domainObject.getId());
-
- return this.openmct.objects.isPersistable(identifier);
- }
-
- return true;
- };
-
- return EditPersistableObjectsPolicy;
- }
-);
diff --git a/platform/commonUI/edit/src/representers/EditRepresenter.js b/platform/commonUI/edit/src/representers/EditRepresenter.js
deleted file mode 100644
index d39ed0d89..000000000
--- a/platform/commonUI/edit/src/representers/EditRepresenter.js
+++ /dev/null
@@ -1,99 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- /**
- * The EditRepresenter is responsible for implementing
- * representation-level behavior relevant to Edit mode.
- * Specifically, this listens for changes to view configuration
- * or to domain object models, and triggers persistence when
- * these are detected.
- *
- * This is exposed as an extension of category `representers`,
- * which mct-representation will utilize to add additional
- * behavior to each representation.
- *
- * This will be called once per mct-representation directive,
- * and may be reused for different domain objects and/or
- * representations resulting from changes there.
- *
- * @memberof platform/commonUI/edit
- * @implements {Representer}
- * @constructor
- */
- function EditRepresenter($log, $scope) {
- this.$log = $log;
- this.$scope = $scope;
-
- this.$scope.commit = this.commit.bind(this);
- }
-
- /**
- * Commit any changes made to the in-scope model to the domain object.
- * Also commits any changes made to $scope.configuration to the proper
- * configuration value for the current representation.
- *
- * @param {String} message a message to log with the commit message.
- */
- EditRepresenter.prototype.commit = function (message) {
- var model = this.$scope.model,
- configuration = this.$scope.configuration,
- domainObject = this.domainObject;
-
- this.$log.debug([
- "Committing ",
- domainObject && domainObject.getModel().name,
- "(" + (domainObject && domainObject.getId()) + "):",
- message
- ].join(" "));
-
- if (this.domainObject) {
- if (this.key && configuration) {
- model.configuration = model.configuration || {};
- model.configuration[this.key] = configuration;
- }
-
- domainObject.useCapability('mutation', function () {
- return model;
- });
- }
- };
-
- // Handle a specific representation of a specific domain object
- EditRepresenter.prototype.represent = function (representation, representedObject) {
- this.domainObject = representedObject;
- if (representation) {
- this.key = representation.key;
- } else {
- delete this.key;
- }
- };
-
- // Respond to the destruction of the current representation.
- EditRepresenter.prototype.destroy = function () {};
-
- return EditRepresenter;
- }
-);
diff --git a/platform/commonUI/edit/test/actions/CancelActionSpec.js b/platform/commonUI/edit/test/actions/CancelActionSpec.js
deleted file mode 100644
index 54073b603..000000000
--- a/platform/commonUI/edit/test/actions/CancelActionSpec.js
+++ /dev/null
@@ -1,155 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../../src/actions/CancelAction"],
- function (CancelAction) {
-
- describe("The Cancel action", function () {
- var mockDomainObject,
- mockParentObject,
- capabilities = {},
- parentCapabilities = {},
- actionContext,
- action;
-
- function mockPromise(value) {
- return {
- then: function (callback) {
- return mockPromise(callback(value));
- }
- };
- }
-
- beforeEach(function () {
- mockDomainObject = jasmine.createSpyObj(
- "domainObject",
- [
- "getCapability",
- "hasCapability",
- "getModel"
- ]
- );
- mockDomainObject.getModel.and.returnValue({});
-
- mockParentObject = jasmine.createSpyObj(
- "parentObject",
- [
- "getCapability"
- ]
- );
- mockParentObject.getCapability.and.callFake(function (name) {
- return parentCapabilities[name];
- });
-
- capabilities.editor = jasmine.createSpyObj(
- "editor",
- ["save", "finish", "isEditContextRoot"]
- );
- capabilities.action = jasmine.createSpyObj(
- "actionCapability",
- [
- "perform"
- ]
- );
- capabilities.location = jasmine.createSpyObj(
- "locationCapability",
- [
- "getOriginal"
- ]
- );
- capabilities.location.getOriginal.and.returnValue(mockPromise(mockDomainObject));
- capabilities.context = jasmine.createSpyObj(
- "contextCapability",
- [
- "getParent"
- ]
- );
- capabilities.context.getParent.and.returnValue(mockParentObject);
-
- parentCapabilities.action = jasmine.createSpyObj(
- "actionCapability",
- [
- "perform"
- ]
- );
-
- actionContext = {
- domainObject: mockDomainObject
- };
-
- mockDomainObject.getCapability.and.callFake(function (name) {
- return capabilities[name];
- });
-
- mockDomainObject.hasCapability.and.callFake(function (name) {
- return Boolean(capabilities[name]);
- });
-
- capabilities.editor.finish.and.returnValue(mockPromise(true));
-
- action = new CancelAction(actionContext);
-
- });
-
- it("only applies to domain object that is being edited", function () {
- capabilities.editor.isEditContextRoot.and.returnValue(true);
- expect(CancelAction.appliesTo(actionContext)).toBeTruthy();
- expect(mockDomainObject.hasCapability).toHaveBeenCalledWith("editor");
-
- capabilities.editor.isEditContextRoot.and.returnValue(false);
- expect(CancelAction.appliesTo(actionContext)).toBeFalsy();
-
- mockDomainObject.hasCapability.and.returnValue(false);
- expect(CancelAction.appliesTo(actionContext)).toBeFalsy();
- });
-
- it("invokes the editor capability's cancel functionality when"
- + " performed", function () {
- mockDomainObject.getModel.and.returnValue({persisted: 1});
- //Return true from navigate action
- capabilities.action.perform.and.returnValue(mockPromise(true));
- action.perform();
-
- // Should have called finish
- expect(capabilities.editor.finish).toHaveBeenCalled();
-
- // Definitely shouldn't call save!
- expect(capabilities.editor.save).not.toHaveBeenCalled();
- });
-
- it("navigates to object if existing using navigate action", function () {
- mockDomainObject.getModel.and.returnValue({persisted: 1});
- //Return true from navigate action
- capabilities.action.perform.and.returnValue(mockPromise(true));
- action.perform();
- expect(capabilities.action.perform).toHaveBeenCalledWith("navigate");
- });
-
- it("navigates to parent if new using navigate action", function () {
- mockDomainObject.getModel.and.returnValue({persisted: undefined});
- action.perform();
- expect(parentCapabilities.action.perform).toHaveBeenCalledWith("navigate");
- });
- });
- }
-);
diff --git a/platform/commonUI/edit/test/actions/EditActionSpec.js b/platform/commonUI/edit/test/actions/EditActionSpec.js
deleted file mode 100644
index ece393a92..000000000
--- a/platform/commonUI/edit/test/actions/EditActionSpec.js
+++ /dev/null
@@ -1,108 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../../src/actions/EditAction"],
- function (EditAction) {
-
- describe("The Edit action", function () {
- var mockLocation,
- mockNavigationService,
- mockLog,
- mockDomainObject,
- mockType,
- mockEditor,
- actionContext,
- capabilities,
- action;
-
- beforeEach(function () {
- mockLocation = jasmine.createSpyObj(
- "$location",
- ["path"]
- );
- mockNavigationService = jasmine.createSpyObj(
- "navigationService",
- ["setNavigation", "getNavigation", "addListener", "removeListener"]
- );
- mockLog = jasmine.createSpyObj(
- "$log",
- ["error", "warn", "info", "debug"]
- );
- mockDomainObject = jasmine.createSpyObj(
- "domainObject",
- ["getId", "getModel", "getCapability", "hasCapability", "useCapability"]
- );
- mockType = jasmine.createSpyObj(
- "type",
- ["hasFeature"]
- );
- mockEditor = jasmine.createSpyObj(
- "editorCapability",
- ["edit", "isEditContextRoot"]
- );
-
- capabilities = {
- type: mockType,
- editor: mockEditor
- };
-
- mockDomainObject.getCapability.and.callFake(function (name) {
- return capabilities[name];
- });
- mockDomainObject.hasCapability.and.returnValue(true);
- mockType.hasFeature.and.returnValue(true);
-
- actionContext = { domainObject: mockDomainObject };
-
- action = new EditAction(
- mockLocation,
- mockNavigationService,
- mockLog,
- actionContext
- );
- });
-
- it("is only applicable when an editable domain object is present", function () {
- expect(EditAction.appliesTo(actionContext)).toBeTruthy();
- expect(EditAction.appliesTo({})).toBeFalsy();
-
- expect(mockDomainObject.hasCapability).toHaveBeenCalledWith('editor');
- // Should have checked for creatability
- expect(mockType.hasFeature).toHaveBeenCalledWith('creation');
- });
-
- it("is only applicable to objects not already in edit mode", function () {
- mockEditor.isEditContextRoot.and.returnValue(false);
- expect(EditAction.appliesTo(actionContext)).toBe(true);
- mockEditor.isEditContextRoot.and.returnValue(true);
- expect(EditAction.appliesTo(actionContext)).toBe(false);
- });
-
- it ("invokes the Edit capability on the object", function () {
- action.perform();
- expect(mockDomainObject.useCapability).toHaveBeenCalledWith("editor");
- });
-
- });
- }
-);
diff --git a/platform/commonUI/edit/test/actions/EditAndComposeActionSpec.js b/platform/commonUI/edit/test/actions/EditAndComposeActionSpec.js
deleted file mode 100644
index 049e3db26..000000000
--- a/platform/commonUI/edit/test/actions/EditAndComposeActionSpec.js
+++ /dev/null
@@ -1,119 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../../src/actions/EditAndComposeAction"],
- function (EditAndComposeAction) {
-
- describe("The Link action", function () {
- var mockDomainObject,
- mockParent,
- mockContext,
- mockComposition,
- mockActionCapability,
- mockEditAction,
- mockType,
- actionContext,
- model,
- capabilities,
- action;
-
- function mockPromise(value) {
- return {
- then: function (callback) {
- return mockPromise(callback(value));
- }
- };
- }
-
- beforeEach(function () {
- mockDomainObject = jasmine.createSpyObj(
- "domainObject",
- ["getId", "getCapability"]
- );
- mockParent = {
- getModel: function () {
- return model;
- },
- getCapability: function (k) {
- return capabilities[k];
- },
- useCapability: function (k, v) {
- return capabilities[k].invoke(v);
- }
- };
- mockContext = jasmine.createSpyObj("context", ["getParent"]);
- mockComposition = jasmine.createSpyObj("composition", ["invoke", "add"]);
- mockType = jasmine.createSpyObj("type", ["hasFeature", "getKey"]);
- mockActionCapability = jasmine.createSpyObj("actionCapability", ["getActions"]);
- mockEditAction = jasmine.createSpyObj("editAction", ["perform"]);
-
- mockDomainObject.getId.and.returnValue("test");
- mockDomainObject.getCapability.and.returnValue(mockContext);
- mockContext.getParent.and.returnValue(mockParent);
- mockType.hasFeature.and.returnValue(true);
- mockType.getKey.and.returnValue("layout");
- mockComposition.invoke.and.returnValue(mockPromise(true));
- mockComposition.add.and.returnValue(mockPromise(true));
- mockActionCapability.getActions.and.returnValue([]);
-
- capabilities = {
- composition: mockComposition,
- action: mockActionCapability,
- type: mockType
- };
- model = {
- composition: ["a", "b", "c"]
- };
-
- actionContext = {
- domainObject: mockParent,
- selectedObject: mockDomainObject
- };
-
- action = new EditAndComposeAction(actionContext);
- });
-
- it("adds to the parent's composition when performed", function () {
- action.perform();
- expect(mockComposition.add)
- .toHaveBeenCalledWith(mockDomainObject);
- });
-
- it("enables edit mode for objects that have an edit action", function () {
- mockActionCapability.getActions.and.returnValue([mockEditAction]);
- action.perform();
- expect(mockEditAction.perform).toHaveBeenCalled();
- });
-
- it("Does not enable edit mode for objects that do not have an"
- + " edit action", function () {
- mockActionCapability.getActions.and.returnValue([]);
- action.perform();
- expect(mockEditAction.perform).not.toHaveBeenCalled();
- expect(mockComposition.add)
- .toHaveBeenCalledWith(mockDomainObject);
- });
-
- });
- }
-);
diff --git a/platform/commonUI/edit/test/actions/SaveActionSpec.js b/platform/commonUI/edit/test/actions/SaveActionSpec.js
deleted file mode 100644
index 1cd264342..000000000
--- a/platform/commonUI/edit/test/actions/SaveActionSpec.js
+++ /dev/null
@@ -1,159 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../../src/actions/SaveAction"],
- function (SaveAction) {
-
- describe("The Save action", function () {
- var mockDomainObject,
- mockEditorCapability,
- actionContext,
- mockDialogService,
- mockNotificationService,
- mockActionCapability,
- capabilities = {},
- action;
-
- function mockPromise(value) {
- return {
- then: function (callback) {
- return mockPromise(callback(value));
- },
- catch: function (callback) {
- return mockPromise(callback(value));
- }
- };
- }
-
- beforeEach(function () {
- mockDomainObject = jasmine.createSpyObj(
- "domainObject",
- [
- "getCapability",
- "hasCapability",
- "getModel",
- "getOriginalObject"
- ]
- );
- mockEditorCapability = jasmine.createSpyObj(
- "editor",
- ["save", "isEditContextRoot"]
- );
- mockActionCapability = jasmine.createSpyObj(
- "actionCapability",
- ["perform"]
- );
- capabilities.editor = mockEditorCapability;
- capabilities.action = mockActionCapability;
-
- actionContext = {
- domainObject: mockDomainObject
- };
-
- mockDialogService = jasmine.createSpyObj(
- "dialogService",
- ["showBlockingMessage"]
- );
-
- mockNotificationService = jasmine.createSpyObj(
- "notificationService",
- ["info", "error"]
- );
-
- mockDomainObject.hasCapability.and.returnValue(true);
- mockDomainObject.getCapability.and.callFake(function (capability) {
- return capabilities[capability];
- });
- mockDomainObject.getModel.and.returnValue({persisted: 0});
- mockEditorCapability.save.and.returnValue(mockPromise(true));
- mockEditorCapability.isEditContextRoot.and.returnValue(true);
-
- action = new SaveAction(mockDialogService, mockNotificationService, actionContext);
- });
-
- it("only applies to domain object with an editor capability", function () {
- expect(SaveAction.appliesTo(actionContext)).toBe(true);
- expect(mockDomainObject.hasCapability).toHaveBeenCalledWith("editor");
-
- mockDomainObject.hasCapability.and.returnValue(false);
- mockDomainObject.getCapability.and.returnValue(undefined);
- expect(SaveAction.appliesTo(actionContext)).toBe(false);
- });
-
- it("only applies to domain object that has already been persisted",
- function () {
- mockDomainObject.getModel.and.returnValue({persisted: undefined});
- expect(SaveAction.appliesTo(actionContext)).toBe(false);
- });
-
- it("uses the editor capability to save the object",
- function () {
- action.perform();
- expect(mockEditorCapability.save).toHaveBeenCalled();
- });
-
- describe("in order to keep the user in the loop", function () {
- var mockDialogHandle;
-
- beforeEach(function () {
- mockDialogHandle = jasmine.createSpyObj("dialogHandle", ["dismiss"]);
- mockDialogService.showBlockingMessage.and.returnValue(mockDialogHandle);
- });
-
- it("shows a dialog while saving", function () {
- mockEditorCapability.save.and.returnValue(new Promise(function () {
- }));
- action.perform();
- expect(mockDialogService.showBlockingMessage).toHaveBeenCalled();
- expect(mockDialogHandle.dismiss).not.toHaveBeenCalled();
- });
-
- it("hides the dialog when saving is complete", function () {
- action.perform();
- expect(mockDialogService.showBlockingMessage).toHaveBeenCalled();
- expect(mockDialogHandle.dismiss).toHaveBeenCalled();
- });
-
- it("notifies if saving succeeded", function () {
- var mockCallback = jasmine.createSpy("callback");
- mockEditorCapability.save.and.returnValue(Promise.resolve());
-
- return action.perform().then(mockCallback).then(function () {
- expect(mockNotificationService.info).toHaveBeenCalled();
- expect(mockNotificationService.error).not.toHaveBeenCalled();
- });
- });
-
- it("notifies if saving failed", function () {
- var mockCallback = jasmine.createSpy("callback");
- mockEditorCapability.save.and.returnValue(Promise.reject("some failure reason"));
-
- return action.perform().then(mockCallback).then(function () {
- expect(mockNotificationService.error).toHaveBeenCalled();
- expect(mockNotificationService.info).not.toHaveBeenCalled();
- });
- });
- });
- });
- }
-);
diff --git a/platform/commonUI/edit/test/actions/SaveAndStopEditingActionSpec.js b/platform/commonUI/edit/test/actions/SaveAndStopEditingActionSpec.js
deleted file mode 100644
index c4eb1058a..000000000
--- a/platform/commonUI/edit/test/actions/SaveAndStopEditingActionSpec.js
+++ /dev/null
@@ -1,128 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../../src/actions/SaveAndStopEditingAction"],
- function (SaveAndStopEditingAction) {
-
- describe("The Save and Stop Editing action", function () {
-
- // Some mocks appear unused because the
- // underlying SaveAction that this action
- // depends on is not mocked, so we mock some
- // of SaveAction's own dependencies to make
- // it run.
- var mockDomainObject,
- mockEditorCapability,
- actionContext,
- dialogService,
- notificationService,
- mockActionCapability,
- capabilities = {},
- action;
-
- function mockPromise(value) {
- return {
- then: function (callback) {
- return mockPromise(callback(value));
- },
- catch: function (callback) {
- return mockPromise(callback(value));
- }
- };
- }
-
- beforeEach(function () {
- mockDomainObject = jasmine.createSpyObj(
- "domainObject",
- [
- "getCapability",
- "hasCapability",
- "getModel",
- "getOriginalObject"
- ]
- );
- mockEditorCapability = jasmine.createSpyObj(
- "editor",
- ["save", "finish", "isEditContextRoot"]
- );
- mockActionCapability = jasmine.createSpyObj(
- "actionCapability",
- ["perform"]
- );
- capabilities.editor = mockEditorCapability;
- capabilities.action = mockActionCapability;
-
- actionContext = {
- domainObject: mockDomainObject
- };
- dialogService = jasmine.createSpyObj(
- "dialogService",
- ["showBlockingMessage"]
- );
-
- notificationService = jasmine.createSpyObj(
- "notificationService",
- ["info", "error"]
- );
-
- mockDomainObject.hasCapability.and.returnValue(true);
- mockDomainObject.getCapability.and.callFake(function (capability) {
- return capabilities[capability];
- });
- mockDomainObject.getModel.and.returnValue({ persisted: 0 });
- mockEditorCapability.save.and.returnValue(mockPromise(true));
- mockEditorCapability.isEditContextRoot.and.returnValue(true);
-
- action = new SaveAndStopEditingAction(dialogService, notificationService, actionContext);
- });
-
- it("only applies to domain object with an editor capability", function () {
- expect(SaveAndStopEditingAction.appliesTo(actionContext)).toBe(true);
- expect(mockDomainObject.hasCapability).toHaveBeenCalledWith("editor");
-
- mockDomainObject.hasCapability.and.returnValue(false);
- mockDomainObject.getCapability.and.returnValue(undefined);
- expect(SaveAndStopEditingAction.appliesTo(actionContext)).toBe(false);
- });
-
- it("only applies to domain object that has already been persisted", function () {
- mockDomainObject.getModel.and.returnValue({ persisted: undefined });
- expect(SaveAndStopEditingAction.appliesTo(actionContext)).toBe(false);
- });
-
- it("does not close the editor before completing the save", function () {
- mockEditorCapability.save.and.returnValue(new Promise(function () {
- }));
- action.perform();
- expect(mockEditorCapability.save).toHaveBeenCalled();
- expect(mockEditorCapability.finish).not.toHaveBeenCalled();
- });
-
- it("closes the editor after saving", function () {
- action.perform();
- expect(mockEditorCapability.save).toHaveBeenCalled();
- expect(mockEditorCapability.finish).toHaveBeenCalled();
- });
- });
- }
-);
diff --git a/platform/commonUI/edit/test/controllers/EditActionControllerSpec.js b/platform/commonUI/edit/test/controllers/EditActionControllerSpec.js
deleted file mode 100644
index 7156cab24..000000000
--- a/platform/commonUI/edit/test/controllers/EditActionControllerSpec.js
+++ /dev/null
@@ -1,106 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../../src/controllers/EditActionController"],
- function (EditActionController) {
-
- describe("The Edit Action controller", function () {
- var mockSaveActionMetadata = {
- name: "mocked-save-action",
- cssClass: "mocked-save-action-css"
- };
-
- function fakeGetActions(actionContext) {
- if (actionContext.category === "save") {
- var mockedSaveActions = [
- jasmine.createSpyObj("mockSaveAction", ["getMetadata", "perform"]),
- jasmine.createSpyObj("mockSaveAction", ["getMetadata", "perform"])
- ];
- mockedSaveActions.forEach(function (action) {
- action.getMetadata.and.returnValue(mockSaveActionMetadata);
- });
-
- return mockedSaveActions;
- } else if (actionContext.category === "conclude-editing") {
- return ["a", "b", "c"];
- } else {
- throw "EditActionController uses a context that's not covered by tests.";
- }
- }
-
- var mockScope,
- mockActions,
- controller;
-
- beforeEach(function () {
- mockActions = jasmine.createSpyObj("action", ["getActions"]);
- mockActions.getActions.and.callFake(fakeGetActions);
- mockScope = jasmine.createSpyObj("$scope", ["$watch"]);
- mockScope.action = mockActions;
- controller = new EditActionController(mockScope);
- });
-
- function makeControllerUpdateActions() {
- mockScope.$watch.calls.mostRecent().args[1]();
- }
-
- it("watches scope that may change applicable actions", function () {
- // The action capability
- expect(mockScope.$watch).toHaveBeenCalledWith(
- "action",
- jasmine.any(Function)
- );
- });
-
- it("populates the scope with 'save' actions", function () {
- makeControllerUpdateActions();
- expect(mockScope.saveActions.length).toEqual(2);
- });
-
- it("converts 'save' actions to their menu counterparts", function () {
- makeControllerUpdateActions();
- var menuOptions = mockScope.saveActionsAsMenuOptions;
-
- expect(menuOptions.length).toEqual(2);
- expect(menuOptions[0].key).toEqual(mockScope.saveActions[0]);
- expect(menuOptions[1].key).toEqual(mockScope.saveActions[1]);
- menuOptions.forEach(function (option) {
- expect(option.name).toEqual(mockSaveActionMetadata.name);
- expect(option.cssClass).toEqual(mockSaveActionMetadata.cssClass);
- });
- });
-
- it("uses a click handler to perform the clicked action", function () {
- makeControllerUpdateActions();
- var sampleSaveAction = mockScope.saveActions[0];
- mockScope.saveActionMenuClickHandler(sampleSaveAction);
- expect(sampleSaveAction.perform).toHaveBeenCalled();
- });
-
- it("populates the scope with other editing actions", function () {
- makeControllerUpdateActions();
- expect(mockScope.otherEditActions).toEqual(["a", "b", "c"]);
- });
- });
- }
-);
diff --git a/platform/commonUI/edit/test/controllers/EditObjectControllerSpec.js b/platform/commonUI/edit/test/controllers/EditObjectControllerSpec.js
deleted file mode 100644
index e63b8f2db..000000000
--- a/platform/commonUI/edit/test/controllers/EditObjectControllerSpec.js
+++ /dev/null
@@ -1,138 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../../src/controllers/EditObjectController"],
- function (EditObjectController) {
-
- describe("The Edit Object controller", function () {
- var mockScope,
- mockObject,
- testViews,
- mockEditorCapability,
- mockLocation,
- mockNavigationService,
- removeCheck,
- mockStatusCapability,
- mockCapabilities,
- controller;
-
- beforeEach(function () {
- mockScope = jasmine.createSpyObj(
- "$scope",
- ["$on", "$watch"]
- );
- mockObject = jasmine.createSpyObj(
- "domainObject",
- ["getId", "getModel", "getCapability", "hasCapability", "useCapability"]
- );
- mockEditorCapability = jasmine.createSpyObj(
- "mockEditorCapability",
- ["isEditContextRoot", "dirty", "finish"]
- );
- mockStatusCapability = jasmine.createSpyObj('statusCapability',
- ["get"]
- );
-
- mockCapabilities = {
- "editor": mockEditorCapability,
- "status": mockStatusCapability
- };
-
- mockLocation = jasmine.createSpyObj('$location',
- ["search"]
- );
- mockLocation.search.and.returnValue({"view": "fixed"});
- mockNavigationService = jasmine.createSpyObj('navigationService',
- ["checkBeforeNavigation"]
- );
-
- removeCheck = jasmine.createSpy('removeCheck');
- mockNavigationService.checkBeforeNavigation.and.returnValue(removeCheck);
-
- mockObject.getId.and.returnValue("test");
- mockObject.getModel.and.returnValue({ name: "Test object" });
- mockObject.getCapability.and.callFake(function (key) {
- return mockCapabilities[key];
- });
-
- testViews = [
- { key: 'abc' },
- {
- key: 'def',
- someKey: 'some value'
- },
- { key: 'xyz' }
- ];
-
- mockObject.useCapability.and.callFake(function (c) {
- return (c === 'view') && testViews;
- });
- mockLocation.search.and.returnValue({ view: 'def' });
-
- mockScope.domainObject = mockObject;
-
- controller = new EditObjectController(
- mockScope,
- mockLocation,
- mockNavigationService
- );
- });
-
- it("adds a check before navigation", function () {
- expect(mockNavigationService.checkBeforeNavigation)
- .toHaveBeenCalledWith(jasmine.any(Function));
-
- var checkFn = mockNavigationService.checkBeforeNavigation.calls.mostRecent().args[0];
-
- mockEditorCapability.isEditContextRoot.and.returnValue(false);
- mockEditorCapability.dirty.and.returnValue(false);
-
- expect(checkFn()).toBe("Continuing will cause the loss of any unsaved changes.");
-
- mockEditorCapability.isEditContextRoot.and.returnValue(true);
- expect(checkFn()).toBe("Continuing will cause the loss of any unsaved changes.");
-
- mockEditorCapability.dirty.and.returnValue(true);
- expect(checkFn())
- .toBe("Continuing will cause the loss of any unsaved changes.");
-
- });
-
- it("cleans up on destroy", function () {
- expect(mockScope.$on)
- .toHaveBeenCalledWith("$destroy", jasmine.any(Function));
-
- mockScope.$on.calls.mostRecent().args[1]();
-
- expect(mockEditorCapability.finish).toHaveBeenCalled();
- expect(removeCheck).toHaveBeenCalled();
- });
-
- it("sets the active view from query parameters", function () {
- expect(mockScope.representation.selected)
- .toEqual(testViews[1]);
- });
-
- });
- }
-);
diff --git a/platform/commonUI/edit/test/controllers/EditPanesControllerSpec.js b/platform/commonUI/edit/test/controllers/EditPanesControllerSpec.js
deleted file mode 100644
index 76d653265..000000000
--- a/platform/commonUI/edit/test/controllers/EditPanesControllerSpec.js
+++ /dev/null
@@ -1,114 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../../src/controllers/EditPanesController"],
- function (EditPanesController) {
-
- describe("The Edit Panes controller", function () {
- var mockScope,
- mockDomainObject,
- mockContext,
- controller;
-
- beforeEach(function () {
- mockScope = jasmine.createSpyObj("$scope", ["$watch"]);
- mockDomainObject = jasmine.createSpyObj(
- 'domainObject',
- ['getId', 'getCapability']
- );
- mockContext = jasmine.createSpyObj(
- 'context',
- ['getTrueRoot']
- );
-
- mockDomainObject.getId.and.returnValue('test-id');
- mockDomainObject.getCapability.and.returnValue(mockContext);
-
- // Return a new instance of the root object each time
- mockContext.getTrueRoot.and.callFake(function () {
- var mockRoot = jasmine.createSpyObj('root', ['getId']);
- mockRoot.getId.and.returnValue('root-id');
-
- return mockRoot;
- });
-
- controller = new EditPanesController(mockScope);
- });
-
- it("watches for the domain object in view", function () {
- expect(mockScope.$watch).toHaveBeenCalledWith(
- "domainObject",
- jasmine.any(Function)
- );
- });
-
- it("exposes the root object found via the object's context capability", function () {
- mockScope.$watch.calls.mostRecent().args[1](mockDomainObject);
-
- // Verify that the correct capability was used
- expect(mockDomainObject.getCapability)
- .toHaveBeenCalledWith('context');
-
- // Should have exposed the root from getRoot
- expect(controller.getRoot().getId()).toEqual('root-id');
- });
-
- it("preserves the same root instance to avoid excessive refreshing", function () {
- var firstRoot;
- // Expose the domain object
- mockScope.$watch.calls.mostRecent().args[1](mockDomainObject);
- firstRoot = controller.getRoot();
- // Update!
- mockScope.$watch.calls.mostRecent().args[1](mockDomainObject);
- // Should still have the same object instance, to avoid
- // triggering the watch used by the template we're supporting
- expect(controller.getRoot()).toBe(firstRoot);
- });
-
- // Complements the test above; the object pointed to should change
- // when the actual root has changed (detected by identifier)
- it("updates the root when it changes", function () {
- var firstRoot;
- // Expose the domain object
- mockScope.$watch.calls.mostRecent().args[1](mockDomainObject);
- firstRoot = controller.getRoot();
-
- // Change the exposed root
- mockContext.getTrueRoot.and.callFake(function () {
- var mockRoot = jasmine.createSpyObj('root', ['getId']);
- mockRoot.getId.and.returnValue('other-root-id');
-
- return mockRoot;
- });
-
- // Update!
- mockScope.$watch.calls.mostRecent().args[1](mockDomainObject);
-
- // Should still have the same object instance, to avoid
- // triggering the watch used by the template we're supporting
- expect(controller.getRoot()).not.toBe(firstRoot);
- expect(controller.getRoot().getId()).toEqual('other-root-id');
- });
- });
- }
-);
diff --git a/platform/commonUI/edit/test/policies/EditPersistableObjectsSpec.js b/platform/commonUI/edit/test/policies/EditPersistableObjectsSpec.js
deleted file mode 100644
index 8213c6253..000000000
--- a/platform/commonUI/edit/test/policies/EditPersistableObjectsSpec.js
+++ /dev/null
@@ -1,102 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2016, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../../src/policies/EditPersistableObjectsPolicy"],
- function (EditPersistableObjectsPolicy) {
-
- describe("The Edit persistable objects policy", function () {
- var mockDomainObject,
- mockEditAction,
- mockPropertiesAction,
- mockOtherAction,
- mockAPI,
- mockObjectAPI,
- testContext,
- policy;
-
- beforeEach(function () {
- mockDomainObject = jasmine.createSpyObj(
- 'domainObject',
- [
- 'getId'
- ]
- );
-
- mockObjectAPI = jasmine.createSpyObj('objectAPI', [
- 'isPersistable',
- 'parseKeyString'
- ]);
-
- mockAPI = {
- objects: mockObjectAPI
- };
-
- mockEditAction = jasmine.createSpyObj('edit', ['getMetadata']);
- mockPropertiesAction = jasmine.createSpyObj('properties', ['getMetadata']);
- mockOtherAction = jasmine.createSpyObj('other', ['getMetadata']);
-
- mockEditAction.getMetadata.and.returnValue({ key: 'edit' });
- mockPropertiesAction.getMetadata.and.returnValue({ key: 'properties' });
- mockOtherAction.getMetadata.and.returnValue({key: 'other'});
-
- mockDomainObject.getId.and.returnValue('test:testId');
-
- testContext = {
- domainObject: mockDomainObject,
- category: 'view-control'
- };
-
- policy = new EditPersistableObjectsPolicy(mockAPI);
- });
-
- it("Applies to edit action", function () {
- expect(mockObjectAPI.isPersistable).not.toHaveBeenCalled();
-
- policy.allow(mockEditAction, testContext);
- expect(mockObjectAPI.isPersistable).toHaveBeenCalled();
- });
-
- it("Applies to properties action", function () {
- expect(mockObjectAPI.isPersistable).not.toHaveBeenCalled();
-
- policy.allow(mockPropertiesAction, testContext);
- expect(mockObjectAPI.isPersistable).toHaveBeenCalled();
- });
-
- it("does not apply to other actions", function () {
- expect(mockObjectAPI.isPersistable).not.toHaveBeenCalled();
-
- policy.allow(mockOtherAction, testContext);
- expect(mockObjectAPI.isPersistable).not.toHaveBeenCalled();
- });
-
- it("Tests object provider for editability", function () {
- mockObjectAPI.isPersistable.and.returnValue(false);
- expect(policy.allow(mockEditAction, testContext)).toBe(false);
- expect(mockObjectAPI.isPersistable).toHaveBeenCalled();
- mockObjectAPI.isPersistable.and.returnValue(true);
- expect(policy.allow(mockEditAction, testContext)).toBe(true);
- });
- });
- }
-);
diff --git a/platform/commonUI/edit/test/representers/EditRepresenterSpec.js b/platform/commonUI/edit/test/representers/EditRepresenterSpec.js
deleted file mode 100644
index a9d9657a4..000000000
--- a/platform/commonUI/edit/test/representers/EditRepresenterSpec.js
+++ /dev/null
@@ -1,87 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
- '../../src/representers/EditRepresenter'
-], function (
- EditRepresenter
-) {
- describe('EditRepresenter', function () {
- var $log,
- $scope,
- representer;
-
- beforeEach(function () {
- $log = jasmine.createSpyObj('$log', ['debug']);
- $scope = {};
- representer = new EditRepresenter($log, $scope);
- });
-
- it('injects a commit function in scope', function () {
- expect($scope.commit).toEqual(jasmine.any(Function));
- });
-
- describe('representation', function () {
- var domainObject,
- representation;
-
- beforeEach(function () {
- domainObject = jasmine.createSpyObj('domainObject', [
- 'getId',
- 'getModel',
- 'useCapability'
- ]);
-
- domainObject.getId.and.returnValue('anId');
- domainObject.getModel.and.returnValue({name: 'anObject'});
-
- representation = {
- key: 'someRepresentation'
- };
- $scope.model = {name: 'anotherName'};
- $scope.configuration = {some: 'config'};
- representer.represent(representation, domainObject);
- });
-
- it('logs a message when commiting', function () {
- $scope.commit('Test Message');
- expect($log.debug)
- .toHaveBeenCalledWith('Committing anObject (anId): Test Message');
- });
-
- it('mutates the object when committing', function () {
- $scope.commit('Test Message');
-
- expect(domainObject.useCapability)
- .toHaveBeenCalledWith('mutation', jasmine.any(Function));
-
- var mutateValue = domainObject.useCapability.calls.all()[0].args[1]();
-
- expect(mutateValue.configuration.someRepresentation)
- .toEqual({some: 'config'});
- expect(mutateValue.name).toEqual('anotherName');
- });
-
- });
-
- });
-});
diff --git a/platform/commonUI/general/README.md b/platform/commonUI/general/README.md
deleted file mode 100644
index 544baf08b..000000000
--- a/platform/commonUI/general/README.md
+++ /dev/null
@@ -1,8 +0,0 @@
-# Directives
-
-* `mct-scroll-x` is an attribute whose value is an assignable
- Angular expression. This two-way binds that expression to the
- horizontal scroll state of the element on which it is applied.
-* `mct-scroll-y` is an attribute whose value is an assignable
- Angular expression. This two-way binds that expression to the
- vertical scroll state of the element on which it is applied.
diff --git a/platform/commonUI/general/bundle.js b/platform/commonUI/general/bundle.js
deleted file mode 100644
index cee38b124..000000000
--- a/platform/commonUI/general/bundle.js
+++ /dev/null
@@ -1,529 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
- "./src/services/UrlService",
- "./src/services/PopupService",
- "./src/SplashScreenManager",
- "./src/StyleSheetLoader",
- "./src/controllers/TimeRangeController",
- "./src/controllers/DateTimePickerController",
- "./src/controllers/DateTimeFieldController",
- "./src/controllers/TreeNodeController",
- "./src/controllers/ActionGroupController",
- "./src/controllers/ToggleController",
- "./src/controllers/ClickAwayController",
- "./src/controllers/ViewSwitcherController",
- "./src/controllers/GetterSetterController",
- "./src/controllers/SelectorController",
- "./src/controllers/ObjectInspectorController",
- "./src/controllers/BannerController",
- "./src/directives/MCTContainer",
- "./src/directives/MCTDrag",
- "./src/directives/MCTSelectable",
- "./src/directives/MCTClickElsewhere",
- "./src/directives/MCTResize",
- "./src/directives/MCTPopup",
- "./src/directives/MCTScroll",
- "./src/directives/MCTSplitPane",
- "./src/directives/MCTSplitter",
- "./src/directives/MCTTree",
- "./src/directives/MCTIndicators",
- "./src/filters/ReverseFilter",
- "./res/templates/bottombar.html",
- "./res/templates/controls/action-button.html",
- "./res/templates/controls/input-filter.html",
- "./res/templates/angular-indicator.html",
- "./res/templates/message-banner.html",
- "./res/templates/progress-bar.html",
- "./res/templates/controls/time-controller.html",
- "./res/templates/containers/accordion.html",
- "./res/templates/subtree.html",
- "./res/templates/tree.html",
- "./res/templates/tree-node.html",
- "./res/templates/label.html",
- "./res/templates/controls/action-group.html",
- "./res/templates/controls/switcher.html",
- "./res/templates/object-inspector.html",
- "./res/templates/controls/selector.html",
- "./res/templates/controls/datetime-picker.html",
- "./res/templates/controls/datetime-field.html"
-], function (
- UrlService,
- PopupService,
- SplashScreenManager,
- StyleSheetLoader,
- TimeRangeController,
- DateTimePickerController,
- DateTimeFieldController,
- TreeNodeController,
- ActionGroupController,
- ToggleController,
- ClickAwayController,
- ViewSwitcherController,
- GetterSetterController,
- SelectorController,
- ObjectInspectorController,
- BannerController,
- MCTContainer,
- MCTDrag,
- MCTSelectable,
- MCTClickElsewhere,
- MCTResize,
- MCTPopup,
- MCTScroll,
- MCTSplitPane,
- MCTSplitter,
- MCTTree,
- MCTIndicators,
- ReverseFilter,
- bottombarTemplate,
- actionButtonTemplate,
- inputFilterTemplate,
- indicatorTemplate,
- messageBannerTemplate,
- progressBarTemplate,
- timeControllerTemplate,
- accordionTemplate,
- subtreeTemplate,
- treeTemplate,
- treeNodeTemplate,
- labelTemplate,
- actionGroupTemplate,
- switcherTemplate,
- objectInspectorTemplate,
- selectorTemplate,
- datetimePickerTemplate,
- datetimeFieldTemplate
-) {
-
- return {
- name: "platform/commonUI/general",
- definition: {
- "name": "General UI elements",
- "description": "General UI elements, meant to be reused across modes",
- "resources": "res",
- "extensions": {
- "services": [
- {
- "key": "urlService",
- "implementation": UrlService,
- "depends": [
- "$location"
- ]
- },
- {
- "key": "popupService",
- "implementation": PopupService,
- "depends": [
- "$document",
- "$window"
- ]
- }
- ],
- "runs": [
- {
- "implementation": StyleSheetLoader,
- "depends": [
- "stylesheets[]",
- "$document",
- "THEME",
- "ASSETS_PATH"
- ]
- },
- {
- "implementation": SplashScreenManager,
- "depends": [
- "$document"
- ]
- }
- ],
- "filters": [
- {
- "implementation": ReverseFilter,
- "key": "reverse"
- }
- ],
- "templates": [
- {
- "key": "bottombar",
- "template": bottombarTemplate
- },
- {
- "key": "action-button",
- "template": actionButtonTemplate
- },
- {
- "key": "input-filter",
- "template": inputFilterTemplate
- },
- {
- "key": "indicator",
- "template": indicatorTemplate
- },
- {
- "key": "message-banner",
- "template": messageBannerTemplate
- },
- {
- "key": "progress-bar",
- "template": progressBarTemplate
- },
- {
- "key": "time-controller",
- "template": timeControllerTemplate
- }
- ],
- "controllers": [
- {
- "key": "TimeRangeController",
- "implementation": TimeRangeController,
- "depends": [
- "$scope",
- "$timeout",
- "formatService",
- "DEFAULT_TIME_FORMAT",
- "now"
- ]
- },
- {
- "key": "DateTimePickerController",
- "implementation": DateTimePickerController,
- "depends": [
- "$scope",
- "now"
- ]
- },
- {
- "key": "DateTimeFieldController",
- "implementation": DateTimeFieldController,
- "depends": [
- "$scope",
- "formatService",
- "DEFAULT_TIME_FORMAT"
- ]
- },
- {
- "key": "TreeNodeController",
- "implementation": TreeNodeController,
- "depends": [
- "$scope",
- "$timeout",
- "navigationService"
- ]
- },
- {
- "key": "ActionGroupController",
- "implementation": ActionGroupController,
- "depends": [
- "$scope"
- ]
- },
- {
- "key": "ToggleController",
- "implementation": ToggleController
- },
- {
- "key": "ClickAwayController",
- "implementation": ClickAwayController,
- "depends": [
- "$document",
- "$timeout"
- ]
- },
- {
- "key": "ViewSwitcherController",
- "implementation": ViewSwitcherController,
- "depends": [
- "$scope",
- "$timeout"
- ]
- },
- {
- "key": "GetterSetterController",
- "implementation": GetterSetterController,
- "depends": [
- "$scope"
- ]
- },
- {
- "key": "SelectorController",
- "implementation": SelectorController,
- "depends": [
- "objectService",
- "$scope"
- ]
- },
- {
- "key": "ObjectInspectorController",
- "implementation": ObjectInspectorController,
- "depends": [
- "$scope",
- "objectService"
- ]
- },
- {
- "key": "BannerController",
- "implementation": BannerController,
- "depends": [
- "$scope",
- "notificationService",
- "dialogService"
- ]
- }
- ],
- "directives": [
- {
- "key": "mctContainer",
- "implementation": MCTContainer,
- "depends": [
- "containers[]"
- ]
- },
- {
- "key": "mctDrag",
- "implementation": MCTDrag,
- "depends": [
- "$document",
- "agentService"
- ]
- },
- {
- "key": "mctSelectable",
- "implementation": MCTSelectable,
- "depends": [
- "openmct"
- ]
- },
- {
- "key": "mctClickElsewhere",
- "implementation": MCTClickElsewhere,
- "depends": [
- "$document"
- ]
- },
- {
- "key": "mctResize",
- "implementation": MCTResize,
- "depends": [
- "$timeout"
- ]
- },
- {
- "key": "mctPopup",
- "implementation": MCTPopup,
- "depends": [
- "$compile",
- "popupService"
- ]
- },
- {
- "key": "mctScrollX",
- "implementation": MCTScroll,
- "depends": [
- "$parse",
- "MCT_SCROLL_X_PROPERTY",
- "MCT_SCROLL_X_ATTRIBUTE"
- ]
- },
- {
- "key": "mctScrollY",
- "implementation": MCTScroll,
- "depends": [
- "$parse",
- "MCT_SCROLL_Y_PROPERTY",
- "MCT_SCROLL_Y_ATTRIBUTE"
- ]
- },
- {
- "key": "mctSplitPane",
- "implementation": MCTSplitPane,
- "depends": [
- "$parse",
- "$log",
- "$interval",
- "$window"
- ]
- },
- {
- "key": "mctSplitter",
- "implementation": MCTSplitter
- },
- {
- "key": "mctTree",
- "implementation": MCTTree,
- "depends": ['gestureService', 'openmct']
- },
- {
- "key": "mctIndicators",
- "implementation": MCTIndicators,
- "depends": ['openmct']
- }
- ],
- "constants": [
- {
- "key": "MCT_SCROLL_X_PROPERTY",
- "value": "scrollLeft"
- },
- {
- "key": "MCT_SCROLL_X_ATTRIBUTE",
- "value": "mctScrollX"
- },
- {
- "key": "MCT_SCROLL_Y_PROPERTY",
- "value": "scrollTop"
- },
- {
- "key": "MCT_SCROLL_Y_ATTRIBUTE",
- "value": "mctScrollY"
- },
- {
- "key": "THEME",
- "value": "unspecified",
- "priority": "fallback"
- },
- {
- "key": "ASSETS_PATH",
- "value": ".",
- "priority": "fallback"
- }
- ],
- "containers": [
- {
- "key": "accordion",
- "template": accordionTemplate,
- "attributes": [
- "label"
- ]
- }
- ],
- "representations": [
- {
- "key": "tree",
- "template": subtreeTemplate,
- "uses": [
- "composition"
- ],
- "type": "root",
- "priority": "preferred"
- },
- {
- "key": "tree",
- "template": treeTemplate
- },
- {
- "key": "subtree",
- "template": subtreeTemplate,
- "uses": [
- "composition"
- ]
- },
- {
- "key": "tree-node",
- "template": treeNodeTemplate,
- "uses": [
- "action"
- ]
- },
- {
- "key": "label",
- "template": labelTemplate,
- "uses": [
- "type",
- "location"
- ],
- "gestures": [
- "drag",
- "menu",
- "info"
- ]
- },
- {
- "key": "node",
- "template": labelTemplate,
- "uses": [
- "type"
- ],
- "gestures": [
- "drag",
- "menu"
- ]
- },
- {
- "key": "action-group",
- "template": actionGroupTemplate,
- "uses": [
- "action"
- ]
- },
- {
- "key": "switcher",
- "template": switcherTemplate,
- "uses": [
- "view"
- ]
- },
- {
- "key": "object-inspector",
- "template": objectInspectorTemplate
- }
- ],
- "controls": [
- {
- "key": "selector",
- "template": selectorTemplate
- },
- {
- "key": "datetime-picker",
- "template": datetimePickerTemplate
- },
- {
- "key": "datetime-field",
- "template": datetimeFieldTemplate
- }
- ],
- "licenses": [
- {
- "name": "Normalize.css",
- "version": "1.1.2",
- "description": "Browser style normalization",
- "author": "Nicolas Gallagher, Jonathan Neal",
- "website": "http://necolas.github.io/normalize.css/",
- "copyright": "Copyright (c) Nicolas Gallagher and Jonathan Neal",
- "license": "license-mit",
- "link": "https://github.com/necolas/normalize.css/blob/v1.1.2/LICENSE.md"
- },
- {
- "name": "Zepto",
- "version": "1.1.6",
- "description": "DOM manipulation",
- "author": "Thomas Fuchs",
- "website": "http://zeptojs.com/",
- "copyright": "Copyright (c) 2010-2016 Thomas Fuchs",
- "license": "license-mit",
- "link": "https://github.com/madrobby/zepto/blob/master/MIT-LICENSE"
- }
- ]
- }
- }
- };
-});
diff --git a/platform/commonUI/general/res/templates/angular-indicator.html b/platform/commonUI/general/res/templates/angular-indicator.html
deleted file mode 100644
index 8aebc93ae..000000000
--- a/platform/commonUI/general/res/templates/angular-indicator.html
+++ /dev/null
@@ -1,26 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
- as represented by the Administrator of the National Aeronautics and Space
- Administration. All rights reserved.
-
- Open MCT is licensed under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0.
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Open MCT includes source code licensed under additional open source
- licenses. See the Open Source Licenses file (LICENSES.md) included with
- this source code distribution or the Licensing information page available
- at runtime from the About dialog for additional information.
--->
-<!-- DO NOT ADD SPACES BETWEEN THE SPANS - IT ADDS WHITE SPACE!! -->
-<div class="c-indicator {{ngModel.getCssClass()}}"
- ng-show="ngModel.getText().length > 0">
- <span class="label c-indicator__label">{{ngModel.getText()}}</span>
-</div>
diff --git a/platform/commonUI/general/res/templates/bottombar.html b/platform/commonUI/general/res/templates/bottombar.html
deleted file mode 100644
index 5cf4656d1..000000000
--- a/platform/commonUI/general/res/templates/bottombar.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
- as represented by the Administrator of the National Aeronautics and Space
- Administration. All rights reserved.
-
- Open MCT is licensed under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0.
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Open MCT includes source code licensed under additional open source
- licenses. See the Open Source Licenses file (LICENSES.md) included with
- this source code distribution or the Licensing information page available
- at runtime from the About dialog for additional information.
--->
-<div class='abs bottom-bar l-ue-bottom-bar s-ue-bottom-bar mobile-disable-select'>
- <div id='status' class='status-holder'>
- <mct-indicators></mct-indicators>
- </div>
- <mct-include key="'message-banner'"></mct-include>
- <mct-include key="'about-logo'"></mct-include>
-</div>
diff --git a/platform/commonUI/general/res/templates/containers/accordion.html b/platform/commonUI/general/res/templates/containers/accordion.html
deleted file mode 100644
index 40910a5e6..000000000
--- a/platform/commonUI/general/res/templates/containers/accordion.html
+++ /dev/null
@@ -1,34 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
- as represented by the Administrator of the National Aeronautics and Space
- Administration. All rights reserved.
-
- Open MCT is licensed under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0.
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Open MCT includes source code licensed under additional open source
- licenses. See the Open Source Licenses file (LICENSES.md) included with
- this source code distribution or the Licensing information page available
- at runtime from the About dialog for additional information.
--->
-<div
- class="accordion-head"
- ng-click="toggle.toggle()"
- ng-class="{ expanded:!toggle.isActive() }"
- >
- {{container.label}}
-</div>
-<div
- class="accordion-contents"
- ng-show="!toggle.isActive()"
- ng-transclude
- >
-</div>
diff --git a/platform/commonUI/general/res/templates/controls/action-group.html b/platform/commonUI/general/res/templates/controls/action-group.html
deleted file mode 100644
index 15b864b23..000000000
--- a/platform/commonUI/general/res/templates/controls/action-group.html
+++ /dev/null
@@ -1,35 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
- as represented by the Administrator of the National Aeronautics and Space
- Administration. All rights reserved.
-
- Open MCT is licensed under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0.
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Open MCT includes source code licensed under additional open source
- licenses. See the Open Source Licenses file (LICENSES.md) included with
- this source code distribution or the Licensing information page available
- at runtime from the About dialog for additional information.
--->
-<span ng-controller="ActionGroupController">
- <span ng-repeat="action in ungrouped">
- <mct-include key="'action-button'" parameters="{ action: action }"></mct-include>
- </span>
- <span class="l-btn-set" ng-repeat="group in groups">
- <span ng-repeat="action in group">
- <mct-include key="'action-button'"
- parameters="{ action: action }"
- ng-class="{ first:$index == 0, last:$index == group.length - 1 }"
- >
- </mct-include>
- </span>
- </span>
-</span>
diff --git a/platform/commonUI/general/res/templates/controls/breadcrumb.html b/platform/commonUI/general/res/templates/controls/breadcrumb.html
deleted file mode 100644
index ce923da77..000000000
--- a/platform/commonUI/general/res/templates/controls/breadcrumb.html
+++ /dev/null
@@ -1,15 +0,0 @@
-<div ng-init="bcItems=[
- {type: 'folder', glyph: 'F', name: 'My Items'},
- {type: 'layout', glyph: 'L', name: 'Layout with a somewhat long name'},
- {type: 'telemetry-panel', glyph: 't', name: 'Panel, My Panel'},
- {type: 'telemetry-element', glyph: 'T', name: 'ACS-0009'}
- ]"></div>
-
-<div class="l-breadcrumb s-breadcrumb">
- <span
- class="l-breadcrumb-item s-breadcrumb-item"
- ng-repeat="bcItem in bcItems"
- >
- <a><span class="ui-symbol icon {{bcItem.type}}">{{bcItem.glyph}}</span>{{bcItem.name}}</a> <span class="ui-symbol sep">}</span>
- </span>
-</div>
diff --git a/platform/commonUI/general/res/templates/controls/datetime-field.html b/platform/commonUI/general/res/templates/controls/datetime-field.html
deleted file mode 100644
index a319e56cb..000000000
--- a/platform/commonUI/general/res/templates/controls/datetime-field.html
+++ /dev/null
@@ -1,48 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
- as represented by the Administrator of the National Aeronautics and Space
- Administration. All rights reserved.
-
- Open MCT is licensed under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0.
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Open MCT includes source code licensed under additional open source
- licenses. See the Open Source Licenses file (LICENSES.md) included with
- this source code distribution or the Licensing information page available
- at runtime from the About dialog for additional information.
--->
-<span ng-controller="DateTimeFieldController">
- <input type="text" autocorrect="off" spellcheck="false"
- ng-model="textValue"
- ng-blur="restoreTextValue(); ngBlur()"
- ng-mouseup="ngMouseup()"
- ng-disabled="ngDisabled"
- ng-class="{
- error: textInvalid ||
- (structure.validate &&
- !structure.validate(ngModel[field])),
- 'picker-icon': structure.format === 'utc' || !structure.format
- }">
- </input>
- <a class="ui-symbol icon icon-calendar"
- ng-if="!picker.active && (structure.format === 'utc' || !structure.format)"
- ng-click="picker.active = !picker.active"></a>
- <!-- If picker active show icon with no onclick to prevent double registration of clicks -->
- <a class="ui-symbol icon icon-calendar" ng-if="picker.active"></a>
- <mct-popup ng-if="picker.active">
- <div mct-click-elsewhere="picker.active = false">
- <mct-control key="'datetime-picker'"
- ng-model="pickerModel"
- field="'value'">
- </mct-control>
- </div>
- </mct-popup>
-</span>
diff --git a/platform/commonUI/general/res/templates/controls/datetime-picker.html b/platform/commonUI/general/res/templates/controls/datetime-picker.html
deleted file mode 100644
index b16866299..000000000
--- a/platform/commonUI/general/res/templates/controls/datetime-picker.html
+++ /dev/null
@@ -1,66 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
- as represented by the Administrator of the National Aeronautics and Space
- Administration. All rights reserved.
-
- Open MCT is licensed under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0.
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Open MCT includes source code licensed under additional open source
- licenses. See the Open Source Licenses file (LICENSES.md) included with
- this source code distribution or the Licensing information page available
- at runtime from the About dialog for additional information.
--->
-
-<div ng-controller="DateTimePickerController" class="l-datetime-picker s-datetime-picker s-menu">
- <div class="holder">
- <div class="l-month-year-pager">
- <a class="pager prev" ng-click="changeMonth(-1)"></a>
- <span class="val">{{month}} {{year}}</span>
- <a class="pager next" ng-click="changeMonth(1)"></a>
- </div>
- <div class="l-calendar">
- <ul class="l-cal-row l-header">
- <li ng-repeat="day in ['Su','Mo','Tu','We','Th','Fr','Sa']">{{day}}</li>
- </ul>
- <ul class="l-cal-row l-body" ng-repeat="row in table">
- <li ng-repeat="cell in row"
- ng-click="select(cell)"
- ng-class='{ "in-month": isInCurrentMonth(cell), selected: isSelected(cell) }'>
- <div class="prime">{{cell.day}}</div>
- <div class="sub">{{cell.dayOfYear}}</div>
- </li>
- </ul>
- </div>
- </div>
- <div class="l-time-selects complex datetime"
- ng-show="options">
- <div class="field-hints">
- <span class="hint time md"
- ng-repeat="key in ['hours', 'minutes', 'seconds']"
- ng-if="options[key]">
- {{nameFor(key)}}
- </span>
- </div>
- <div>
- <span class="field control time md"
- ng-repeat="key in ['hours', 'minutes', 'seconds']"
- ng-if="options[key]">
- <div class='form-control select'>
- <select size="1"
- ng-model="time[key]"
- ng-options="i for i in optionsFor(key)">
- </select>
- </div>
- </span>
- </div>
- </div>
-</div>
diff --git a/platform/commonUI/general/res/templates/controls/input-filter.html b/platform/commonUI/general/res/templates/controls/input-filter.html
deleted file mode 100644
index 932ee7432..000000000
--- a/platform/commonUI/general/res/templates/controls/input-filter.html
+++ /dev/null
@@ -1,32 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
- as represented by the Administrator of the National Aeronautics and Space
- Administration. All rights reserved.
-
- Open MCT is licensed under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0.
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Open MCT includes source code licensed under additional open source
- licenses. See the Open Source Licenses file (LICENSES.md) included with
- this source code distribution or the Licensing information page available
- at runtime from the About dialog for additional information.
--->
-<!-- look at action-button for example -->
-<span class="t-filter l-filter"
- ng-controller="GetterSetterController">
- <input type="search"
- class="t-filter-input"
- ng-model="getterSetter.value"/>
- <a class="clear-icon icon-x-in-circle"
- ng-class="{show: !(getterSetter.value === '' || getterSetter.value === undefined)}"
- ng-click="getterSetter.value = ''">
- </a>
-</span>
diff --git a/platform/commonUI/general/res/templates/controls/selector.html b/platform/commonUI/general/res/templates/controls/selector.html
deleted file mode 100644
index 28485c8a7..000000000
--- a/platform/commonUI/general/res/templates/controls/selector.html
+++ /dev/null
@@ -1,65 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
- as represented by the Administrator of the National Aeronautics and Space
- Administration. All rights reserved.
-
- Open MCT is licensed under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0.
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Open MCT includes source code licensed under additional open source
- licenses. See the Open Source Licenses file (LICENSES.md) included with
- this source code distribution or the Licensing information page available
- at runtime from the About dialog for additional information.
--->
-<div class='form-control complex channel-selector cols cols-32'
- ng-controller="SelectorController as selector">
- <div class='col col-15'>
- <div class='line field-hints'>Available</div>
- <div class='line treeview' name='available'>
- <mct-representation key="'tree'"
- mct-object="selector.root()"
- ng-model="selector.treeModel">
- </mct-representation>
- </div>
- </div>
- <div class='col col-2'>
- <div class='btn-holder valign-mid btns-add-remove'>
- <a class='s-button major'
- ng-click="selector.select(selector.treeModel.selectedObject)">
- <span class='ui-symbol'>&gt;</span>
- </a>
- <a class='s-button major'
- ng-click="selector.deselect(selector.listModel.selectedObject)">
- <span class='ui-symbol'>&lt;</span>
- </a>
- </div>
- </div>
- <div class='col col-15'>
- <div class='line field-hints'>Selected</div>
- <div class='line treeview l-tree-item-flat-list' name='selected'>
- <ul class="tree">
- <li ng-repeat="selectedObject in selector.selected()">
- <span
- class="tree-item"
- ng-class="{selected: selector.listModel.selectedObject === selectedObject }"
- >
- <mct-representation
- key="'label'"
- mct-object="selectedObject"
- ng-click="selector.listModel.selectedObject = selectedObject"
- >
- </mct-representation>
- </span>
- </li>
- </ul>
- </div>
- </div>
-</div>
diff --git a/platform/commonUI/general/res/templates/controls/switcher.html b/platform/commonUI/general/res/templates/controls/switcher.html
deleted file mode 100644
index 147b0e9ba..000000000
--- a/platform/commonUI/general/res/templates/controls/switcher.html
+++ /dev/null
@@ -1,42 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
- as represented by the Administrator of the National Aeronautics and Space
- Administration. All rights reserved.
-
- Open MCT is licensed under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0.
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Open MCT includes source code licensed under additional open source
- licenses. See the Open Source Licenses file (LICENSES.md) included with
- this source code distribution or the Licensing information page available
- at runtime from the About dialog for additional information.
--->
-<span ng-controller="ViewSwitcherController">
- <div class="view-switcher menu-element s-menu-button {{ngModel.selected.cssClass}}"
- ng-if="view.length > 1"
- ng-controller="ClickAwayController as toggle">
-
- <span class="l-click-area"
- ng-click="toggle.toggle()"
- title="{{ngModel.selected.name}}"></span>
- <span class="title-label">{{ngModel.selected.name}}</span>
-
- <div class="menu" ng-show="toggle.isActive()">
- <ul>
- <li ng-repeat="option in view"
- ng-click="ngModel.selected = option; toggle.setState(false)"
- class="{{option.cssClass}}">
- {{option.name}}
- </li>
- </ul>
- </div>
- </div>
-</span>
diff --git a/platform/commonUI/general/res/templates/controls/time-controller.html b/platform/commonUI/general/res/templates/controls/time-controller.html
deleted file mode 100644
index f11bf3f6c..000000000
--- a/platform/commonUI/general/res/templates/controls/time-controller.html
+++ /dev/null
@@ -1,100 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
- as represented by the Administrator of the National Aeronautics and Space
- Administration. All rights reserved.
-
- Open MCT is licensed under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0.
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Open MCT includes source code licensed under additional open source
- licenses. See the Open Source Licenses file (LICENSES.md) included with
- this source code distribution or the Licensing information page available
- at runtime from the About dialog for additional information.
--->
-<div ng-controller="TimeRangeController as trCtrl" class="l-flex-col">
- <form class="l-time-range-inputs-holder l-flex-row flex-elem"
- ng-submit="trCtrl.updateBoundsFromForm()">
- <span class="l-time-range-inputs-elem flex-elem icon-clock"></span>
- <span class="l-time-range-inputs-elem t-inputs-w l-flex-row flex-elem">
- <span class="l-time-range-input-w flex-elem">
- <mct-control key="'datetime-field'"
- structure="{
- format: parameters.format,
- validate: trCtrl.validateStart
- }"
- ng-model="formModel"
- ng-blur="trCtrl.updateBoundsFromForm()"
- field="'start'"
- class="time-range-start">
- </mct-control>
- </span>
-
- <span class="l-time-range-inputs-elem lbl flex-elem">to</span>
-
- <span class="l-time-range-input-w flex-elem" ng-controller="ToggleController as t2">
- <mct-control key="'datetime-field'"
- structure="{
- format: parameters.format,
- validate: trCtrl.validateEnd
- }"
- ng-model="formModel"
- ng-blur="trCtrl.updateBoundsFromForm()"
- field="'end'"
- class="time-range-end">
- </mct-control>
- </span>
- </span>
- <input type="submit" class="hidden">
- </form>
-
- <div class="l-time-range-slider-holder flex-elem">
- <div class="l-time-range-slider">
- <div class="slider"
- mct-resize="spanWidth = bounds.width">
- <div class="knob knob-l"
- mct-drag-down="trCtrl.startLeftDrag()"
- mct-drag="trCtrl.leftDrag(delta[0])"
- ng-style="{ left: startInnerPct }">
- <div class="range-value">{{startInnerText}}</div>
- </div>
- <div class="knob knob-r"
- mct-drag-down="trCtrl.startRightDrag()"
- mct-drag="trCtrl.rightDrag(delta[0])"
- ng-style="{ right: endInnerPct }">
- <div class="range-value">{{endInnerText}}</div>
- </div>
- <div class="slot range-holder">
- <div class="range"
- mct-drag-down="trCtrl.startMiddleDrag()"
- mct-drag="trCtrl.middleDrag(delta[0])"
- ng-style="{
- left: startInnerPct,
- right: endInnerPct
- }">
- <div class="toi-line"></div>
- </div>
- </div>
- </div>
- </div>
- </div>
-
- <div class="l-time-range-ticks-holder flex-elem">
- <div class="l-time-range-ticks">
- <div
- ng-repeat="tick in ticks track by $index"
- ng-style="{ left: $index * (100 / (ticks.length - 1)) + '%' }"
- class="tick tick-x"
- >
- <span class="l-time-range-tick-label">{{tick}}</span>
- </div>
- </div>
- </div>
-</div>
diff --git a/platform/commonUI/general/res/templates/label.html b/platform/commonUI/general/res/templates/label.html
deleted file mode 100644
index 27983aed1..000000000
--- a/platform/commonUI/general/res/templates/label.html
+++ /dev/null
@@ -1,31 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
- as represented by the Administrator of the National Aeronautics and Space
- Administration. All rights reserved.
-
- Open MCT is licensed under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0.
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Open MCT includes source code licensed under additional open source
- licenses. See the Open Source Licenses file (LICENSES.md) included with
- this source code distribution or the Licensing information page available
- at runtime from the About dialog for additional information.
--->
-<div class="c-object-label"
- ng-class="{ 'is-status--missing': model.status === 'missing' }"
->
- <div class="c-object-label__type-icon {{type.getCssClass()}}"
- ng-class="{ 'l-icon-link':location.isLink() }"
- >
- <span class="is-status__indicator" title="This item is missing or suspect"></span>
- </div>
- <div class='c-object-label__name'>{{model.name}}</div>
-</div>
diff --git a/platform/commonUI/general/res/templates/message-banner.html b/platform/commonUI/general/res/templates/message-banner.html
deleted file mode 100644
index 861db3052..000000000
--- a/platform/commonUI/general/res/templates/message-banner.html
+++ /dev/null
@@ -1,20 +0,0 @@
-<div ng-controller="BannerController" ng-show="active.notification"
- class="c-message-banner {{active.notification.model.severity}}" ng-class="{
- 'minimized': active.notification.model.minimized,
- 'new': !active.notification.model.minimized}"
- ng-click="maximize(active.notification)">
- <span class="c-message-banner__message">
- {{active.notification.model.title}}
- </span>
- <span ng-show="active.notification.model.progress !== undefined || active.notification.model.unknownProgress">
- <mct-include key="'progress-bar'" class="c-message-banner__progress-bar"
- ng-model="active.notification.model">
- </mct-include>
- </span>
- <a ng-hide="active.notification.model.primaryOption === undefined"
- class="banner-elem l-action s-action"
- ng-click="action(active.notification.model.primaryOption.callback, $event)">
- {{active.notification.model.primaryOption.label}}
- </a>
- <button class="c-message-banner__close-button c-click-icon icon-x-in-circle" ng-click="dismiss(active.notification, $event)"></button>
-</div>
diff --git a/platform/commonUI/general/res/templates/object-inspector.html b/platform/commonUI/general/res/templates/object-inspector.html
deleted file mode 100644
index 6abc35e92..000000000
--- a/platform/commonUI/general/res/templates/object-inspector.html
+++ /dev/null
@@ -1,49 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
- as represented by the Administrator of the National Aeronautics and Space
- Administration. All rights reserved.
-
- Open MCT is licensed under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0.
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Open MCT includes source code licensed under additional open source
- licenses. See the Open Source Licenses file (LICENSES.md) included with
- this source code distribution or the Licensing information page available
- at runtime from the About dialog for additional information.
--->
-<span class="l-inspect">
- <div ng-controller="PaneController">
- <mct-split-pane class='abs contents split-layout' anchor='bottom'>
- <div class="split-pane-component pane top">
- <div class="abs holder holder-inspector l-flex-col">
- <div class="pane-header flex-elem">Inspection</div>
- <mct-representation
- key="'inspector-region'"
- mct-object="domainObject"
- ng-model="ngModel"
- class="flex-elem grows vscroll scroll-pad l-flex-col inspector-properties">
- </mct-representation>
- </div>
- </div>
- <mct-splitter class="splitter-inspect-panel mobile-hide"></mct-splitter>
- <div class="split-pane-component pane bottom">
- <div class="abs holder holder-elements l-flex-col">
- <h2>Elements</h2>
- <mct-representation
- key="'edit-elements'"
- mct-object="domainObject"
- class="flex-elem l-flex-col holder grows current-elements">
- </mct-representation>
- </div>
- </div>
- </mct-split-pane>
- </div>
-</span>
diff --git a/platform/commonUI/general/res/templates/progress-bar.html b/platform/commonUI/general/res/templates/progress-bar.html
deleted file mode 100644
index 69883ed7e..000000000
--- a/platform/commonUI/general/res/templates/progress-bar.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<span class="l-progress-bar s-progress-bar"
- ng-class="{ indeterminate:ngModel.progressPerc === 'unknown' }">
- <span class="progress-amt-holder">
- <span class="progress-amt" style="width: {{ngModel.progressPerc === 'unknown' ? 100 : ngModel.progressPerc}}%"></span>
- </span>
-</span>
-<div class="progress-info hint" ng-hide="ngModel.progressText === undefined">
- <span class="progress-amt-text" ng-show="ngModel.progressPerc !== 'unknown' && ngModel.progressPerc > 0">{{ngModel.progressPerc}}% complete. </span>
- {{ngModel.progressText}}
-</div>
diff --git a/platform/commonUI/general/res/templates/subtree.html b/platform/commonUI/general/res/templates/subtree.html
deleted file mode 100644
index 3eb67eb59..000000000
--- a/platform/commonUI/general/res/templates/subtree.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
- as represented by the Administrator of the National Aeronautics and Space
- Administration. All rights reserved.
-
- Open MCT is licensed under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0.
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Open MCT includes source code licensed under additional open source
- licenses. See the Open Source Licenses file (LICENSES.md) included with
- this source code distribution or the Licensing information page available
- at runtime from the About dialog for additional information.
--->
-<mct-tree root-object="domainObject"
- selected-object="ngModel.selectedObject"
- on-selection="ngModel.onSelection"
- allow-selection="ngModel.allowSelection">
-</mct-tree>
-
diff --git a/platform/commonUI/general/res/templates/tree-node.html b/platform/commonUI/general/res/templates/tree-node.html
deleted file mode 100644
index ef0ecc452..000000000
--- a/platform/commonUI/general/res/templates/tree-node.html
+++ /dev/null
@@ -1,50 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
- as represented by the Administrator of the National Aeronautics and Space
- Administration. All rights reserved.
-
- Open MCT is licensed under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0.
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Open MCT includes source code licensed under additional open source
- licenses. See the Open Source Licenses file (LICENSES.md) included with
- this source code distribution or the Licensing information page available
- at runtime from the About dialog for additional information.
--->
-<span ng-controller="ToggleController as toggle">
- <div class="u-contents" ng-controller="TreeNodeController as treeNode">
- <div class="c-tree__item menus-to-left"
- ng-class="{selected: treeNode.isSelected()}">
- <span class='c-disclosure-triangle c-tree__item__view-control'
- ng-class="{ 'is-enabled': model.composition !== undefined, 'c-disclosure-triangle--expanded': toggle.isActive() }"
- ng-click="toggle.toggle(); treeNode.trackExpansion()"
- >
- </span>
- <mct-representation
- class="rep-object-label"
- key="'label'"
- mct-object="domainObject"
- parameters="{suppressMenuOnEdit: true}"
- ng-click="treeNode.select()"
- >
- </mct-representation>
- </div>
- <div class="u-contents"
- ng-show="toggle.isActive()"
- ng-if="model.composition !== undefined">
- <mct-representation key="'subtree'"
- ng-model="ngModel"
- parameters="parameters"
- mct-object="treeNode.hasBeenExpanded() && domainObject">
- </mct-representation>
- </div>
- </div>
-</span>
diff --git a/platform/commonUI/general/res/templates/tree.html b/platform/commonUI/general/res/templates/tree.html
deleted file mode 100644
index db7511b5a..000000000
--- a/platform/commonUI/general/res/templates/tree.html
+++ /dev/null
@@ -1,30 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
- as represented by the Administrator of the National Aeronautics and Space
- Administration. All rights reserved.
-
- Open MCT is licensed under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0.
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Open MCT includes source code licensed under additional open source
- licenses. See the Open Source Licenses file (LICENSES.md) included with
- this source code distribution or the Licensing information page available
- at runtime from the About dialog for additional information.
--->
-<ul class="c-tree">
- <li class="c-tree__item-h">
- <mct-representation key="'tree-node'"
- mct-object="domainObject"
- ng-model="ngModel"
- parameters="parameters">
- </mct-representation>
- </li>
-</ul>
diff --git a/platform/commonUI/general/res/templates/tree/node.html b/platform/commonUI/general/res/templates/tree/node.html
deleted file mode 100644
index 41f3a2b22..000000000
--- a/platform/commonUI/general/res/templates/tree/node.html
+++ /dev/null
@@ -1,2 +0,0 @@
-<span class="c-tree__item js-tree__item"></span>
-<span class="c-tree__item-subtree"></span>
diff --git a/platform/commonUI/general/res/templates/tree/toggle.html b/platform/commonUI/general/res/templates/tree/toggle.html
deleted file mode 100644
index daa78ec9a..000000000
--- a/platform/commonUI/general/res/templates/tree/toggle.html
+++ /dev/null
@@ -1 +0,0 @@
-<span class='c-disclosure-triangle c-tree__item__view-control'></span>
diff --git a/platform/commonUI/general/res/templates/tree/tree-label.html b/platform/commonUI/general/res/templates/tree/tree-label.html
deleted file mode 100644
index da82af233..000000000
--- a/platform/commonUI/general/res/templates/tree/tree-label.html
+++ /dev/null
@@ -1,4 +0,0 @@
-<div class="rep-object-label c-object-label c-tree__item__label">
- <div class="c-object-label__type-icon c-tree__item__type-icon t-item-icon"></div>
- <div class="c-object-label__name c-tree__item__name t-title-label"></div>
-</div>
diff --git a/platform/commonUI/general/res/templates/tree/wait-node.html b/platform/commonUI/general/res/templates/tree/wait-node.html
deleted file mode 100644
index eb54b14d4..000000000
--- a/platform/commonUI/general/res/templates/tree/wait-node.html
+++ /dev/null
@@ -1,24 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
- as represented by the Administrator of the National Aeronautics and Space
- Administration. All rights reserved.
-
- Open MCT is licensed under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0.
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Open MCT includes source code licensed under additional open source
- licenses. See the Open Source Licenses file (LICENSES.md) included with
- this source code distribution or the Licensing information page available
- at runtime from the About dialog for additional information.
--->
-<li class='tree-item t-wait-node loading'>
- <span class="t-title-label">Loading...</span>
-</li>
diff --git a/platform/commonUI/general/src/StyleSheetLoader.js b/platform/commonUI/general/src/StyleSheetLoader.js
deleted file mode 100644
index 1cc0c88ad..000000000
--- a/platform/commonUI/general/src/StyleSheetLoader.js
+++ /dev/null
@@ -1,82 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * This bundle provides various general-purpose UI elements, including
- * platform styling.
- * @namespace platform/commonUI/general
- */
-define(
- [],
- function () {
-
- /**
- * The StyleSheetLoader adds links to style sheets exposed from
- * various bundles as extensions of category `stylesheets`.
- * @memberof platform/commonUI/general
- * @constructor
- * @param {object[]} stylesheets stylesheet extension definitions
- * @param $document Angular's jqLite-wrapped document element
- * @param {string} activeTheme the theme in use
- * @param {string} [assetPath] the directory relative to which
- * stylesheets will be found
- */
- function StyleSheetLoader(stylesheets, $document, activeTheme, assetPath) {
- var head = $document.find('head'),
- document = $document[0];
-
- // Procedure for adding a single stylesheet
- function addStyleSheet(stylesheet) {
- // Create a link element, and construct full path
- var link = document.createElement('link'),
- path = [
- assetPath,
- stylesheet.bundle.path,
- stylesheet.bundle.resources,
- stylesheet.stylesheetUrl
- ].join("/");
-
- // Initialize attributes on the link
- link.setAttribute("rel", "stylesheet");
- link.setAttribute("type", "text/css");
- link.setAttribute("href", path);
-
- // Append the link to the head element
- head.append(link);
- }
-
- // Stylesheets which specify themes should only be applied
- // when that theme has been declared.
- function matchesTheme(stylesheet) {
- return stylesheet.theme === undefined
- || stylesheet.theme === activeTheme;
- }
-
- assetPath = assetPath || ".";
-
- // Add all stylesheets from extensions
- stylesheets.filter(matchesTheme).forEach(addStyleSheet);
- }
-
- return StyleSheetLoader;
- }
-);
diff --git a/platform/commonUI/general/src/controllers/ActionGroupController.js b/platform/commonUI/general/src/controllers/ActionGroupController.js
deleted file mode 100644
index 2566fb328..000000000
--- a/platform/commonUI/general/src/controllers/ActionGroupController.js
+++ /dev/null
@@ -1,104 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Module defining ActionGroupController. Created by vwoeltje on 11/14/14.
- */
-define(
- [],
- function () {
-
- /**
- * Controller which keeps an up-to-date list of actions of
- * a certain category, and additionally bins them into
- * groups as described by their metadata. Used specifically
- * to support button groups.
- *
- * This will maintain two fields in the scope:
- * * `groups`: An array of arrays. Each element in the outer
- * array corresponds to a group; the inner array contains
- * the actions which are in that group.
- * * `ungrouped`: All actions which did not have a defined
- * group.
- *
- * @memberof platform/commonUI/general
- * @constructor
- */
- function ActionGroupController($scope) {
-
- // Separate out the actions that have been retrieved
- // into groups, and populate scope with this.
- function groupActions(actions) {
- var groups = {},
- ungrouped = [];
-
- function assignToGroup(action) {
- var metadata = action.getMetadata(),
- group = metadata.group;
- if (group) {
- groups[group] = groups[group] || [];
- groups[group].push(action);
- } else {
- ungrouped.push(action);
- }
- }
-
- (actions || []).forEach(assignToGroup);
-
- $scope.ungrouped = ungrouped;
- $scope.groups = Object.keys(groups).sort().map(function (k) {
- return groups[k];
- });
- }
-
- // Callback for when state which might influence action groupings
- // changes.
- function updateGroups() {
- var actionCapability = $scope.action,
- params = $scope.parameters || {},
- category = params.category;
-
- if (actionCapability && category) {
- // Get actions by capability, and group them
- groupActions(actionCapability.getActions({
- category: category
- }));
- } else {
- // We don't have enough information to get any actions.
- groupActions([]);
- }
- }
-
- // Changes to the represented object, to its action capability, or
- // to the chosen action category may all require an update.
- $scope.$watch("domainObject", updateGroups);
- $scope.$watch("action", updateGroups);
- $scope.$watch("parameters.category", updateGroups);
-
- // Start with empty arrays.
- $scope.ungrouped = [];
- $scope.groups = [];
- }
-
- return ActionGroupController;
- }
-);
diff --git a/platform/commonUI/general/src/controllers/BannerController.js b/platform/commonUI/general/src/controllers/BannerController.js
deleted file mode 100644
index 93cce6195..000000000
--- a/platform/commonUI/general/src/controllers/BannerController.js
+++ /dev/null
@@ -1,77 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- /**
- * A controller for banner notifications. Banner notifications are a
- * non-blocking way of drawing the user's attention to an event such
- * as system errors, or the progress or successful completion of an
- * ongoing task. This controller provides scoped functions for
- * dismissing and 'maximizing' notifications. See {@link NotificationService}
- * for more details on Notifications.
- *
- * @param $scope
- * @param notificationService
- * @param dialogService
- * @constructor
- */
- function BannerController($scope, notificationService, dialogService) {
- $scope.active = notificationService.active;
-
- $scope.action = function (action, $event) {
- /*
- Prevents default 'maximize' behaviour when clicking on
- notification button
- */
- $event.stopPropagation();
-
- return action();
- };
-
- $scope.dismiss = function (notification, $event) {
- $event.stopPropagation();
- notification.dismiss();
- };
-
- $scope.maximize = function (notification) {
- if (notification.model.severity !== "info") {
- var dialog;
- notification.model.cancel = function () {
- dialog.dismiss();
- };
-
- //If the notification is dismissed by the user, close
- // the dialog.
- notification.on('dismiss', function () {
- dialog.dismiss();
- });
-
- dialog = dialogService.showBlockingMessage(notification.model);
- }
- };
- }
-
- return BannerController;
- });
diff --git a/platform/commonUI/general/src/controllers/ClickAwayController.js b/platform/commonUI/general/src/controllers/ClickAwayController.js
deleted file mode 100644
index 982e2d0aa..000000000
--- a/platform/commonUI/general/src/controllers/ClickAwayController.js
+++ /dev/null
@@ -1,97 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- /**
- * A ClickAwayController is used to toggle things (such as context
- * menus) where clicking elsewhere in the document while the toggle
- * is in an active state is intended to dismiss the toggle.
- *
- * @memberof platform/commonUI/general
- * @constructor
- * @param $scope the scope in which this controller is active
- * @param $document the document element, injected by Angular
- */
- function ClickAwayController($document, $timeout) {
- var self = this;
-
- this.state = false;
- this.$document = $document;
-
- // Callback used by the document listener. Timeout ensures that
- // `clickaway` action occurs after `toggle` if `toggle` is
- // triggered by a click/mouseup.
- this.clickaway = function () {
- $timeout(function () {
- self.deactivate();
- });
- };
- }
-
- // Track state, but also attach and detach a listener for
- // mouseup events on the document.
- ClickAwayController.prototype.deactivate = function () {
- this.state = false;
- this.$document.off("mouseup", this.clickaway);
- };
-
- ClickAwayController.prototype.activate = function () {
- this.state = true;
- this.$document.on("mouseup", this.clickaway);
- };
-
- /**
- * Get the current state of the toggle.
- * @return {boolean} true if active
- */
- ClickAwayController.prototype.isActive = function () {
- return this.state;
- };
-
- /**
- * Set a new state for the toggle.
- * @return {boolean} true to activate
- */
- ClickAwayController.prototype.setState = function (newState) {
- if (this.state !== newState) {
- this.toggle();
- }
- };
-
- /**
- * Toggle the current state; activate if it is inactive,
- * deactivate if it is active.
- */
- ClickAwayController.prototype.toggle = function () {
- if (this.state) {
- this.deactivate();
- } else {
- this.activate();
- }
- };
-
- return ClickAwayController;
- }
-);
diff --git a/platform/commonUI/general/src/controllers/DateTimeFieldController.js b/platform/commonUI/general/src/controllers/DateTimeFieldController.js
deleted file mode 100644
index 4132ded76..000000000
--- a/platform/commonUI/general/src/controllers/DateTimeFieldController.js
+++ /dev/null
@@ -1,112 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- /**
- * Controller to support the date-time entry field.
- *
- * Accepts a `format` property in the `structure` attribute
- * which allows a date/time to be specified via its symbolic
- * key (as will be used to look up said format from the
- * `formatService`.)
- *
- * {@see FormatService}
- * @constructor
- * @memberof platform/commonUI/general
- * @param $scope the Angular scope for this controller
- * @param {FormatService} formatService the service to user to format
- * domain values
- * @param {string} defaultFormat the format to request when no
- * format has been otherwise specified
- */
- function DateTimeFieldController($scope, formatService, defaultFormat) {
- var formatter = formatService.getFormat(defaultFormat);
-
- function updateFromModel(value) {
- // Only reformat if the value is different from user
- // input (to avoid reformatting valid input while typing.)
- if (!formatter.validate($scope.textValue)
- || formatter.parse($scope.textValue) !== value) {
- $scope.textValue = formatter.format(value);
- $scope.textInvalid = false;
- $scope.lastValidValue = $scope.textValue;
- }
-
- $scope.pickerModel = { value: value };
- }
-
- function updateFromView(textValue) {
- $scope.textInvalid = !formatter.validate(textValue);
- if (!$scope.textInvalid) {
- $scope.ngModel[$scope.field] =
- formatter.parse(textValue);
- $scope.lastValidValue = $scope.textValue;
- }
- }
-
- function updateFromPicker(value) {
- if (value !== $scope.ngModel[$scope.field]) {
- $scope.ngModel[$scope.field] = value;
- updateFromModel(value);
- if ($scope.ngBlur) {
- $scope.ngBlur();
- }
-
- // If picker is active, dismiss it when valid value has been selected
- // This 'if' is to avoid unnecessary validation if picker is not active
- if ($scope.picker.active) {
- if ($scope.structure.validate && $scope.structure.validate($scope.ngModel[$scope.field])) {
- $scope.picker.active = false;
- } else if (!$scope.structure.validate) {
- //If picker visible, but no validation function, hide picker
- $scope.picker.active = false;
- }
- }
- }
- }
-
- function setFormat(format) {
- formatter = formatService.getFormat(format || defaultFormat);
- updateFromModel($scope.ngModel[$scope.field]);
- }
-
- function restoreTextValue() {
- $scope.textValue = $scope.lastValidValue;
- updateFromView($scope.textValue);
- }
-
- $scope.restoreTextValue = restoreTextValue;
-
- $scope.picker = { active: false };
-
- $scope.$watch('structure.format', setFormat);
- $scope.$watch('ngModel[field]', updateFromModel);
- $scope.$watch('pickerModel.value', updateFromPicker);
- $scope.$watch('textValue', updateFromView);
- }
-
- return DateTimeFieldController;
- }
-);
diff --git a/platform/commonUI/general/src/controllers/DateTimePickerController.js b/platform/commonUI/general/src/controllers/DateTimePickerController.js
deleted file mode 100644
index 742e4b18b..000000000
--- a/platform/commonUI/general/src/controllers/DateTimePickerController.js
+++ /dev/null
@@ -1,207 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ['moment'],
- function (moment) {
-
- var TIME_NAMES = {
- 'hours': "Hour",
- 'minutes': "Minute",
- 'seconds': "Second"
- },
- MONTHS = moment.months(),
- TIME_OPTIONS = (function makeRanges() {
- var arr = [];
- while (arr.length < 60) {
- arr.push(arr.length);
- }
-
- return {
- hours: arr.slice(0, 24),
- minutes: arr,
- seconds: arr
- };
- }());
-
- /**
- * Controller to support the date-time picker.
- *
- * Adds/uses the following properties in scope:
- * * `year`: Year being displayed in picker
- * * `month`: Month being displayed
- * * `table`: Table being displayed; array of arrays of
- * * `day`: Day of month
- * * `dayOfYear`: Day of year
- * * `month`: Month associated with the day
- * * `year`: Year associated with the day.
- * * `date`: Date chosen
- * * `year`: Year selected
- * * `month`: Month selected (0-indexed)
- * * `day`: Day of month selected
- * * `time`: Chosen time (hours/minutes/seconds)
- * * `hours`: Hours chosen
- * * `minutes`: Minutes chosen
- * * `seconds`: Seconds chosen
- *
- * Months are zero-indexed, day-of-months are one-indexed.
- */
- function DateTimePickerController($scope, now) {
- var year,
- month, // For picker state, not model state
- interacted = false;
-
- function generateTable() {
- var m = moment.utc({
- year: year,
- month: month
- }).day(0),
- table = [],
- row,
- col;
-
- for (row = 0; row < 6; row += 1) {
- table.push([]);
- for (col = 0; col < 7; col += 1) {
- table[row].push({
- year: m.year(),
- month: m.month(),
- day: m.date(),
- dayOfYear: m.dayOfYear()
- });
- m.add(1, 'days'); // Next day!
- }
- }
-
- return table;
- }
-
- function updateScopeForMonth() {
- $scope.month = MONTHS[month];
- $scope.year = year;
- $scope.table = generateTable();
- }
-
- function updateFromModel(ngModel) {
- var m;
-
- m = moment.utc(ngModel);
-
- $scope.date = {
- year: m.year(),
- month: m.month(),
- day: m.date()
- };
- $scope.time = {
- hours: m.hour(),
- minutes: m.minute(),
- seconds: m.second()
- };
-
- //window.alert($scope.date.day + " " + ngModel);
-
- // Zoom to that date in the picker, but
- // only if the user hasn't interacted with it yet.
- if (!interacted) {
- year = m.year();
- month = m.month();
- updateScopeForMonth();
- }
- }
-
- function updateFromView() {
- var m = moment.utc({
- year: $scope.date.year,
- month: $scope.date.month,
- day: $scope.date.day,
- hour: $scope.time.hours,
- minute: $scope.time.minutes,
- second: $scope.time.seconds
- });
- $scope.ngModel[$scope.field] = m.valueOf();
- }
-
- $scope.isInCurrentMonth = function (cell) {
- return cell.month === month;
- };
-
- $scope.isSelected = function (cell) {
- var date = $scope.date || {};
-
- return cell.day === date.day
- && cell.month === date.month
- && cell.year === date.year;
- };
-
- $scope.select = function (cell) {
- $scope.date = $scope.date || {};
- $scope.date.month = cell.month;
- $scope.date.year = cell.year;
- $scope.date.day = cell.day;
- updateFromView();
- };
-
- $scope.dateEquals = function (d1, d2) {
- return d1.year === d2.year
- && d1.month === d2.month
- && d1.day === d2.day;
- };
-
- $scope.changeMonth = function (delta) {
- month += delta;
- if (month > 11) {
- month = 0;
- year += 1;
- }
-
- if (month < 0) {
- month = 11;
- year -= 1;
- }
-
- interacted = true;
- updateScopeForMonth();
- };
-
- $scope.nameFor = function (key) {
- return TIME_NAMES[key];
- };
-
- $scope.optionsFor = function (key) {
- return TIME_OPTIONS[key];
- };
-
- updateScopeForMonth();
-
- // Ensure some useful default
- $scope.ngModel[$scope.field] =
- $scope.ngModel[$scope.field] === undefined
- ? now() : $scope.ngModel[$scope.field];
-
- $scope.$watch('ngModel[field]', updateFromModel);
- $scope.$watchCollection('date', updateFromView);
- $scope.$watchCollection('time', updateFromView);
- }
-
- return DateTimePickerController;
- }
-);
diff --git a/platform/commonUI/general/src/controllers/GetterSetterController.js b/platform/commonUI/general/src/controllers/GetterSetterController.js
deleted file mode 100644
index 029812d93..000000000
--- a/platform/commonUI/general/src/controllers/GetterSetterController.js
+++ /dev/null
@@ -1,89 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- /**
- * This controller acts as an adapter to permit getter-setter
- * functions to be used as ng-model arguments to controls,
- * such as the input-filter. This is supported natively in
- * Angular 1.3+ via `ng-model-options`, so this controller
- * should be made obsolete after any upgrade to Angular 1.3.
- *
- * It expects to find in scope a value `ngModel` which is a
- * function which, when called with no arguments, acts as a
- * getter, and when called with one argument, acts as a setter.
- *
- * It also publishes into the scope a value `getterSetter.value`
- * which is meant to be used as an assignable expression.
- *
- * This controller watches both of these; when one changes,
- * it will update the other's value to match. Because of this,
- * the `ngModel` function should be both stable and computationally
- * inexpensive, as it will be invoked often.
- *
- * Getter-setter style models can be preferable when there
- * is significant indirection between templates; "dotless"
- * expressions in `ng-model` can behave unexpectedly due to the
- * rules of scope, but dots are lost when passed in via `ng-model`
- * (so if a control is internally implemented using regular
- * form elements, it can't transparently pass through the `ng-model`
- * parameter it received.) Getter-setter functions are never the
- * target of a scope assignment and so avoid this problem.
- *
- * @memberof platform/commonUI/general
- * @constructor
- * @param {Scope} $scope the controller's scope
- */
- function GetterSetterController($scope) {
-
- // Update internal assignable state based on changes
- // to the getter-setter function.
- function updateGetterSetter() {
- if (typeof $scope.ngModel === 'function') {
- $scope.getterSetter.value = $scope.ngModel();
- }
- }
-
- // Update the external getter-setter based on changes
- // to the assignable state.
- function updateNgModel() {
- if (typeof $scope.ngModel === 'function') {
- $scope.ngModel($scope.getterSetter.value);
- }
- }
-
- // Watch for changes to both expressions
- $scope.$watch("ngModel()", updateGetterSetter);
- $scope.$watch("getterSetter.value", updateNgModel);
-
- // Publish an assignable field into scope.
- $scope.getterSetter = {};
-
- }
-
- return GetterSetterController;
-
- }
-);
diff --git a/platform/commonUI/general/src/controllers/ObjectInspectorController.js b/platform/commonUI/general/src/controllers/ObjectInspectorController.js
deleted file mode 100644
index e3261e215..000000000
--- a/platform/commonUI/general/src/controllers/ObjectInspectorController.js
+++ /dev/null
@@ -1,119 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Module defining ObjectInspectorController. Created by shale on 08/21/2015.
- */
-define(
- [],
- function () {
-
- /**
- * The ObjectInspectorController gets and formats the data for
- * the inspector display
- *
- * @constructor
- */
- function ObjectInspectorController($scope, objectService) {
- $scope.primaryParents = [];
- $scope.contextutalParents = [];
- //$scope.isLink = false;
-
- // Gets an array of the contextual parents/ancestors of the selected object
- function getContextualPath() {
- var currentObj = $scope.domainObject,
- currentParent,
- parents = [];
-
- currentParent = currentObj
- && currentObj.hasCapability('context')
- && currentObj.getCapability('context').getParent();
-
- while (currentParent && currentParent.getModel().type !== 'root'
- && currentParent.hasCapability('context')) {
- // Record this object
- parents.unshift(currentParent);
-
- // Get the next one up the tree
- currentObj = currentParent;
- currentParent = currentObj.getCapability('context').getParent();
- }
-
- $scope.contextutalParents = parents;
- }
-
- // Gets an array of the parents/ancestors of the selected object's
- // primary location (locational of original non-link)
- function getPrimaryPath(current) {
- var location;
-
- // If this the the initial call of this recursive function
- if (!current) {
- current = $scope.domainObject;
- $scope.primaryParents = [];
- }
-
- location = current.getModel().location;
-
- if (location && location !== 'root') {
- objectService.getObjects([location]).then(function (obj) {
- var next = obj[location];
-
- $scope.primaryParents.unshift(next);
- getPrimaryPath(next);
- });
- }
-
- }
-
- // Gets the metadata for the selected object
- function getMetadata() {
- $scope.metadata = $scope.domainObject
- && $scope.domainObject.hasCapability('metadata')
- && $scope.domainObject.useCapability('metadata');
- }
-
- // Set scope variables when the selected object changes
- $scope.$watch('domainObject', function () {
- $scope.isLink = $scope.domainObject
- && $scope.domainObject.hasCapability('location')
- && $scope.domainObject.getCapability('location').isLink();
-
- if ($scope.isLink) {
- getPrimaryPath();
- getContextualPath();
- } else {
- $scope.primaryParents = [];
- getContextualPath();
- }
-
- getMetadata();
- });
-
- var mutation = $scope.domainObject.getCapability('mutation');
- var unlisten = mutation.listen(getMetadata);
- $scope.$on('$destroy', unlisten);
- }
-
- return ObjectInspectorController;
- }
-);
diff --git a/platform/commonUI/general/src/controllers/SelectorController.js b/platform/commonUI/general/src/controllers/SelectorController.js
deleted file mode 100644
index c8b71cefe..000000000
--- a/platform/commonUI/general/src/controllers/SelectorController.js
+++ /dev/null
@@ -1,164 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- var ROOT_ID = "ROOT";
-
- /**
- * Controller for the domain object selector control.
- * @memberof platform/commonUI/general
- * @constructor
- * @param {ObjectService} objectService service from which to
- * read domain objects
- * @param $scope Angular scope for this controller
- */
- function SelectorController(objectService, $scope) {
- var treeModel = {},
- listModel = {},
- previousSelected,
- self = this;
-
- // For watch; look at the user's selection in the tree
- function getTreeSelection() {
- return treeModel.selectedObject;
- }
-
- // Store root object for subsequent exposure to template
- function storeRoot(objects) {
- self.rootObject = objects[ROOT_ID];
- }
-
- // Check that a selection is of the valid type
- function validateTreeSelection(selectedObject) {
- var type = selectedObject
- && selectedObject.getCapability('type');
-
- // Delegate type-checking to the capability...
- if (!type || !type.instanceOf($scope.structure.type)) {
- treeModel.selectedObject = previousSelected;
- }
-
- // Track current selection to restore it if an invalid
- // selection is made later.
- previousSelected = treeModel.selectedObject;
- }
-
- // Update the right-hand list of currently-selected objects
- function updateList(ids) {
- function updateSelectedObjects(objects) {
- // Look up from the
- function getObject(id) {
- return objects[id];
- }
-
- self.selectedObjects =
- ids.filter(getObject).map(getObject);
- }
-
- // Look up objects by id, then populate right-hand list
- objectService.getObjects(ids).then(updateSelectedObjects);
- }
-
- // Reject attempts to select objects of the wrong type
- $scope.$watch(getTreeSelection, validateTreeSelection);
-
- // Make sure right-hand list matches underlying model
- $scope.$watchCollection(function () {
- return self.getField();
- }, updateList);
-
- // Look up root object, then store it
- objectService.getObjects([ROOT_ID]).then(storeRoot);
-
- this.$scope = $scope;
- this.selectedObjects = [];
-
- // Expose tree/list model for use in template directly
- this.treeModel = treeModel;
- this.listModel = listModel;
- }
-
- // Set the value of the field being edited
- SelectorController.prototype.setField = function (value) {
- this.$scope.ngModel[this.$scope.field] = value;
- };
-
- // Get the value of the field being edited
- SelectorController.prototype.getField = function () {
- return this.$scope.ngModel[this.$scope.field] || [];
- };
-
- /**
- * Get the root object to show in the left-hand tree.
- * @returns {DomainObject} the root object
- */
- SelectorController.prototype.root = function () {
- return this.rootObject;
- };
-
- /**
- * Add a domain object to the list of selected objects.
- * @param {DomainObject} the domain object to select
- */
- SelectorController.prototype.select = function (domainObject) {
- var id = domainObject && domainObject.getId(),
- list = this.getField() || [];
- // Only select if we have a valid id,
- // and it isn't already selected
- if (id && list.indexOf(id) === -1) {
- this.setField(list.concat([id]));
- }
- };
-
- /**
- * Remove a domain object from the list of selected objects.
- * @param {DomainObject} the domain object to select
- */
- SelectorController.prototype.deselect = function (domainObject) {
- var id = domainObject && domainObject.getId(),
- list = this.getField() || [];
- // Only change if this was a valid id,
- // for an object which was already selected
- if (id && list.indexOf(id) !== -1) {
- // Filter it out of the current field
- this.setField(list.filter(function (otherId) {
- return otherId !== id;
- }));
- // Clear the current list selection
- delete this.listModel.selectedObject;
- }
- };
-
- /**
- * Get the currently-selected domain objects.
- * @returns {DomainObject[]} the current selection
- */
- SelectorController.prototype.selected = function () {
- return this.selectedObjects;
- };
-
- return SelectorController;
- }
-);
diff --git a/platform/commonUI/general/src/controllers/TimeRangeController.js b/platform/commonUI/general/src/controllers/TimeRangeController.js
deleted file mode 100644
index 5b4a55a63..000000000
--- a/platform/commonUI/general/src/controllers/TimeRangeController.js
+++ /dev/null
@@ -1,313 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
-
-], function () {
-
- var TICK_SPACING_PX = 150;
-
- /* format number as percent; 0.0-1.0 to "0%"-"100%" */
- function toPercent(p) {
- return (100 * p) + "%";
- }
-
- function clamp(value, low, high) {
- return Math.max(low, Math.min(high, value));
- }
-
- function copyBounds(bounds) {
- return {
- start: bounds.start,
- end: bounds.end
- };
- }
-
- /**
- * Controller used by the `time-controller` template.
- * @memberof platform/commonUI/general
- * @constructor
- * @param $scope the Angular scope for this controller
- * @param {FormatService} formatService the service to user to format
- * domain values
- * @param {string} defaultFormat the format to request when no
- * format has been otherwise specified
- * @param {Function} now a function to return current system time
- */
- function TimeRangeController($scope, $timeout, formatService, defaultFormat, now) {
- this.$scope = $scope;
- this.formatService = formatService;
- this.defaultFormat = defaultFormat;
- this.now = now;
-
- this.tickCount = 2;
- this.innerMinimumSpan = 1000; // 1 second
- this.outerMinimumSpan = 1000; // 1 second
- this.initialDragValue = undefined;
- this.formatter = formatService.getFormat(defaultFormat);
- this.formStartChanged = false;
- this.formEndChanged = false;
- this.$timeout = $timeout;
-
- this.$scope.ticks = [];
-
- this.updateViewFromModel(this.$scope.ngModel);
- this.updateFormModel();
-
- [
- 'updateViewFromModel',
- 'updateSpanWidth',
- 'updateOuterStart',
- 'updateOuterEnd',
- 'updateFormat',
- 'validateStart',
- 'validateEnd',
- 'onFormStartChange',
- 'onFormEndChange'
- ].forEach(function (boundFn) {
- this[boundFn] = this[boundFn].bind(this);
- }, this);
-
- this.$scope.$watchCollection("ngModel", this.updateViewFromModel);
- this.$scope.$watch("spanWidth", this.updateSpanWidth);
- this.$scope.$watch("ngModel.outer.start", this.updateOuterStart);
- this.$scope.$watch("ngModel.outer.end", this.updateOuterEnd);
- this.$scope.$watch("parameters.format", this.updateFormat);
- this.$scope.$watch("formModel.start", this.onFormStartChange);
- this.$scope.$watch("formModel.end", this.onFormEndChange);
- }
-
- TimeRangeController.prototype.formatTimestamp = function (ts) {
- return this.formatter.format(ts);
- };
-
- TimeRangeController.prototype.updateTicks = function () {
- var i, p, ts, start, end, span;
- end = this.$scope.ngModel.outer.end;
- start = this.$scope.ngModel.outer.start;
- span = end - start;
- this.$scope.ticks = [];
- for (i = 0; i < this.tickCount; i += 1) {
- p = i / (this.tickCount - 1);
- ts = p * span + start;
- this.$scope.ticks.push(this.formatTimestamp(ts));
- }
- };
-
- TimeRangeController.prototype.updateSpanWidth = function (w) {
- this.tickCount = Math.max(Math.floor(w / TICK_SPACING_PX), 2);
- this.updateTicks();
- };
-
- TimeRangeController.prototype.updateViewForInnerSpanFromModel = function (
- ngModel
- ) {
- var span = ngModel.outer.end - ngModel.outer.start;
-
- // Expose readable dates for the knobs
- this.$scope.startInnerText = this.formatTimestamp(ngModel.inner.start);
- this.$scope.endInnerText = this.formatTimestamp(ngModel.inner.end);
-
- // And positions for the knobs
- this.$scope.startInnerPct =
- toPercent((ngModel.inner.start - ngModel.outer.start) / span);
- this.$scope.endInnerPct =
- toPercent((ngModel.outer.end - ngModel.inner.end) / span);
- };
-
- TimeRangeController.prototype.defaultBounds = function () {
- var t = this.now();
-
- return {
- start: t - 24 * 3600 * 1000, // One day
- end: t
- };
- };
-
- TimeRangeController.prototype.updateViewFromModel = function (ngModel) {
- ngModel = ngModel || {};
- ngModel.outer = ngModel.outer || this.defaultBounds();
- ngModel.inner = ngModel.inner || copyBounds(ngModel.outer);
-
- // Stick it back is scope (in case we just set defaults)
- this.$scope.ngModel = ngModel;
-
- this.updateViewForInnerSpanFromModel(ngModel);
- this.updateTicks();
- };
-
- TimeRangeController.prototype.startLeftDrag = function () {
- this.initialDragValue = this.$scope.ngModel.inner.start;
- };
-
- TimeRangeController.prototype.startRightDrag = function () {
- this.initialDragValue = this.$scope.ngModel.inner.end;
- };
-
- TimeRangeController.prototype.startMiddleDrag = function () {
- this.initialDragValue = {
- start: this.$scope.ngModel.inner.start,
- end: this.$scope.ngModel.inner.end
- };
- };
-
- TimeRangeController.prototype.toMillis = function (pixels) {
- var span =
- this.$scope.ngModel.outer.end - this.$scope.ngModel.outer.start;
-
- return (pixels / this.$scope.spanWidth) * span;
- };
-
- TimeRangeController.prototype.leftDrag = function (pixels) {
- var delta = this.toMillis(pixels);
- this.$scope.ngModel.inner.start = clamp(
- this.initialDragValue + delta,
- this.$scope.ngModel.outer.start,
- this.$scope.ngModel.inner.end - this.innerMinimumSpan
- );
- this.updateViewFromModel(this.$scope.ngModel);
- };
-
- TimeRangeController.prototype.rightDrag = function (pixels) {
- var delta = this.toMillis(pixels);
- this.$scope.ngModel.inner.end = clamp(
- this.initialDragValue + delta,
- this.$scope.ngModel.inner.start + this.innerMinimumSpan,
- this.$scope.ngModel.outer.end
- );
- this.updateViewFromModel(this.$scope.ngModel);
- };
-
- TimeRangeController.prototype.middleDrag = function (pixels) {
- var delta = this.toMillis(pixels),
- edge = delta < 0 ? 'start' : 'end',
- opposite = delta < 0 ? 'end' : 'start';
-
- // Adjust the position of the edge in the direction of drag
- this.$scope.ngModel.inner[edge] = clamp(
- this.initialDragValue[edge] + delta,
- this.$scope.ngModel.outer.start,
- this.$scope.ngModel.outer.end
- );
- // Adjust opposite knob to maintain span
- this.$scope.ngModel.inner[opposite] =
- this.$scope.ngModel.inner[edge]
- + this.initialDragValue[opposite]
- - this.initialDragValue[edge];
-
- this.updateViewFromModel(this.$scope.ngModel);
- };
-
- TimeRangeController.prototype.updateFormModel = function () {
- this.$scope.formModel = {
- start: ((this.$scope.ngModel || {}).outer || {}).start,
- end: ((this.$scope.ngModel || {}).outer || {}).end
- };
- };
-
- TimeRangeController.prototype.updateOuterStart = function () {
- var ngModel = this.$scope.ngModel;
-
- ngModel.inner.start =
- Math.max(ngModel.outer.start, ngModel.inner.start);
- ngModel.inner.end = Math.max(
- ngModel.inner.start + this.innerMinimumSpan,
- ngModel.inner.end
- );
-
- this.updateFormModel();
- this.updateViewForInnerSpanFromModel(ngModel);
- this.updateTicks();
- };
-
- TimeRangeController.prototype.updateOuterEnd = function () {
- var ngModel = this.$scope.ngModel;
-
- ngModel.inner.end =
- Math.min(ngModel.outer.end, ngModel.inner.end);
- ngModel.inner.start = Math.min(
- ngModel.inner.end - this.innerMinimumSpan,
- ngModel.inner.start
- );
-
- this.updateFormModel();
- this.updateViewForInnerSpanFromModel(ngModel);
- this.updateTicks();
- };
-
- TimeRangeController.prototype.updateFormat = function (key) {
- this.formatter = this.formatService.getFormat(key || this.defaultFormat);
- this.updateViewForInnerSpanFromModel(this.$scope.ngModel);
- this.updateTicks();
- };
-
- TimeRangeController.prototype.updateBoundsFromForm = function () {
- var self = this;
-
- //Allow Angular to trigger watches and determine whether values have changed.
- this.$timeout(function () {
- if (self.formStartChanged) {
- self.$scope.ngModel.outer.start =
- self.$scope.ngModel.inner.start =
- self.$scope.formModel.start;
- self.formStartChanged = false;
- }
-
- if (self.formEndChanged) {
- self.$scope.ngModel.outer.end =
- self.$scope.ngModel.inner.end =
- self.$scope.formModel.end;
- self.formEndChanged = false;
- }
- });
- };
-
- TimeRangeController.prototype.onFormStartChange = function (
- newValue,
- oldValue
- ) {
- if (!this.formStartChanged && newValue !== oldValue) {
- this.formStartChanged = true;
- }
- };
-
- TimeRangeController.prototype.onFormEndChange = function (
- newValue,
- oldValue
- ) {
- if (!this.formEndChanged && newValue !== oldValue) {
- this.formEndChanged = true;
- }
- };
-
- TimeRangeController.prototype.validateStart = function (startValue) {
- return startValue
- <= this.$scope.formModel.end - this.outerMinimumSpan;
- };
-
- TimeRangeController.prototype.validateEnd = function (endValue) {
- return endValue
- >= this.$scope.formModel.start + this.outerMinimumSpan;
- };
-
- return TimeRangeController;
-});
diff --git a/platform/commonUI/general/src/controllers/ToggleController.js b/platform/commonUI/general/src/controllers/ToggleController.js
deleted file mode 100644
index 1b7f15e32..000000000
--- a/platform/commonUI/general/src/controllers/ToggleController.js
+++ /dev/null
@@ -1,66 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- /**
- * A ToggleController is used to activate/deactivate things.
- * A common usage is for "twistie"
- *
- * @memberof platform/commonUI/general
- * @constructor
- */
- function ToggleController() {
- this.state = false;
-
- this.setState = this.setState.bind(this);
- }
-
- /**
- * Get the current state of the toggle.
- * @return {boolean} true if active
- */
- ToggleController.prototype.isActive = function () {
- return this.state;
- };
-
- /**
- * Set a new state for the toggle.
- * @return {boolean} true to activate
- */
- ToggleController.prototype.setState = function (newState) {
- this.state = newState;
- };
-
- /**
- * Toggle the current state; activate if it is inactive,
- * deactivate if it is active.
- */
- ToggleController.prototype.toggle = function () {
- this.state = !this.state;
- };
-
- return ToggleController;
- }
-);
diff --git a/platform/commonUI/general/src/controllers/TreeNodeController.js b/platform/commonUI/general/src/controllers/TreeNodeController.js
deleted file mode 100644
index 7a41c939c..000000000
--- a/platform/commonUI/general/src/controllers/TreeNodeController.js
+++ /dev/null
@@ -1,204 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Module defining TreeNodeController. Created by vwoeltje on 11/10/14.
- */
-define(
- [],
- function () {
-
- /**
- * The TreeNodeController supports the tree node representation;
- * a tree node has a label for the current object as well as a
- * subtree which shows (and is not loaded until) the node is
- * expanded.
- *
- * This controller tracks the following, so that the tree node
- * template may update its state accordingly:
- *
- * * Whether or not the tree node has ever been expanded (this
- * is used to lazily load, exactly once, the subtree)
- * * Whether or not the node is currently the domain object
- * of navigation (this gets highlighted differently to
- * provide the user with visual feedback.)
- *
- * Additionally, this controller will automatically trigger
- * node expansion when this tree node's _subtree_ will contain
- * the navigated object (recursively, this becomes an
- * expand-to-show-navigated-object behavior.)
- *
- * Finally, if a `callback` property is passed in through the
- * `parameters` attribute of the `tree-node`, that callback
- * will be invoked whenever a user clicks in a manner which
- * would result in a selection. This callback is invoked
- * even if the selection does not change (if you are only
- * interested in changes, watch the `selectedObject` property
- * of the object passed in `ng-model` instead.)
- *
- * @memberof platform/commonUI/general
- * @constructor
- */
- function TreeNodeController($scope, $timeout) {
- var self = this,
- selectedObject = ($scope.ngModel || {}).selectedObject;
-
- // Look up the id for a domain object. A convenience
- // for mapping; additionally does some undefined-checking.
- function getId(obj) {
- return obj && obj.getId && obj.getId();
- }
-
- // Verify that id paths are equivalent, staring at
- // index, ending at the end of the node path.
- function checkPath(nodePath, navPath, index) {
- index = index || 0;
-
- // The paths overlap if we have made it past the
- // end of the node's path; otherwise, check the
- // id at the current index for equality and perform
- // a recursive step for subsequent ids in the paths,
- // until we exceed path length or hit a mismatch.
- return (index >= nodePath.length)
- || ((navPath[index] === nodePath[index])
- && checkPath(nodePath, navPath, index + 1));
- }
-
- // Consider the currently-navigated object and update
- // parameters which support display.
- function checkSelection() {
- var nodeObject = $scope.domainObject,
- navObject = selectedObject,
- nodeContext = nodeObject
- && nodeObject.getCapability('context'),
- navContext = navObject
- && navObject.getCapability('context'),
- nodePath,
- navPath;
-
- // Deselect; we will reselect below, iff we are
- // exactly at the end of the path.
- self.isSelectedFlag = false;
-
- // Expand if necessary (if the navigated object will
- // be in this node's subtree)
- if (nodeContext && navContext) {
- // Get the paths as arrays of identifiers
- nodePath = nodeContext.getPath().map(getId);
- navPath = navContext.getPath().map(getId);
-
- // Check to see if the node's path lies entirely
- // within the navigation path; otherwise, navigation
- // has happened in some other subtree.
- if (navPath.length >= nodePath.length
- && checkPath(nodePath, navPath)) {
-
- // nodePath is along the navPath; if it's
- // at the end of the path, highlight;
- // otherwise, expand.
- if (nodePath.length === navPath.length) {
- self.isSelectedFlag = true;
- } else { // node path is shorter: Expand!
- if ($scope.toggle) {
- $scope.toggle.setState(true);
- }
-
- self.trackExpansion();
- }
-
- }
- }
- }
-
- // Callback for the selection updates; track the currently
- // navigated object and update display parameters as needed.
- function setSelection(object) {
- selectedObject = object;
- checkSelection();
- }
-
- this.isSelectedFlag = false;
- this.hasBeenExpandedFlag = false;
- this.$timeout = $timeout;
- this.$scope = $scope;
-
- // Listen for changes which will effect display parameters
- $scope.$watch("ngModel.selectedObject", setSelection);
- $scope.$watch("domainObject", checkSelection);
- }
-
- /**
- * Select the domain object represented by this node in the tree.
- * This will both update the `selectedObject` property in
- * the object passed in via `ng-model`, and will fire any `callback`
- * passed in via `parameters`.
- */
- TreeNodeController.prototype.select = function () {
- if (this.$scope.ngModel) {
- this.$scope.ngModel.selectedObject =
- this.$scope.domainObject;
- }
-
- if ((this.$scope.parameters || {}).callback) {
- this.$scope.parameters.callback(this.$scope.domainObject);
- }
- };
-
- /**
- * This method should be called when a node is expanded
- * to record that this has occurred, to support one-time
- * lazy loading of the node's subtree.
- */
- TreeNodeController.prototype.trackExpansion = function () {
- var self = this;
- if (!self.hasBeenExpanded()) {
- // Run on a timeout; if a lot of expansion needs to
- // occur (e.g. if the selection is several nodes deep) we
- // want this to be spread across multiple digest cycles.
- self.$timeout(function () {
- self.hasBeenExpandedFlag = true;
- }, 0);
- }
- };
-
- /**
- * Check if this not has ever been expanded.
- * @returns true if it has been expanded
- */
- TreeNodeController.prototype.hasBeenExpanded = function () {
- return this.hasBeenExpandedFlag;
- };
-
- /**
- * Check whether or not the domain object represented by
- * this tree node should be highlighted.
- * An object will be highlighted if it matches
- * ngModel.selectedObject
- * @returns true if this should be highlighted
- */
- TreeNodeController.prototype.isSelected = function () {
- return this.isSelectedFlag;
- };
-
- return TreeNodeController;
- }
-);
diff --git a/platform/commonUI/general/src/controllers/ViewSwitcherController.js b/platform/commonUI/general/src/controllers/ViewSwitcherController.js
deleted file mode 100644
index 2e96df153..000000000
--- a/platform/commonUI/general/src/controllers/ViewSwitcherController.js
+++ /dev/null
@@ -1,73 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Module defining ViewSwitcherController. Created by vwoeltje on 11/7/14.
- */
-define(
- [],
- function () {
-
- /**
- * Controller for the view switcher; populates and maintains a list
- * of applicable views for a represented domain object.
- * @memberof platform/commonUI/general
- * @constructor
- */
- function ViewSwitcherController($scope, $timeout) {
- // If the view capability gets refreshed, try to
- // keep the same option chosen.
- function findMatchingOption(options, selected) {
- var i;
-
- if (selected) {
- for (i = 0; i < options.length; i += 1) {
- if (options[i].key === selected.key) {
- return options[i];
- }
- }
- }
-
- return options[0];
- }
-
- // Get list of views, read from capability
- function updateOptions(views) {
- if (Array.isArray(views)) {
- $timeout(function () {
- $scope.ngModel.selected = findMatchingOption(
- views,
- ($scope.ngModel || {}).selected
- );
- }, 0);
- }
- }
-
- // Update view options when the in-scope results of using the
- // view capability change.
- $scope.$watch("view", updateOptions);
- }
-
- return ViewSwitcherController;
- }
-);
-
diff --git a/platform/commonUI/general/src/directives/MCTClickElsewhere.js b/platform/commonUI/general/src/directives/MCTClickElsewhere.js
deleted file mode 100644
index dee34a66e..000000000
--- a/platform/commonUI/general/src/directives/MCTClickElsewhere.js
+++ /dev/null
@@ -1,77 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- /**
- * The `mct-click-elsewhere` directive will evaluate its
- * associated expression whenever a `mousedown` occurs anywhere
- * outside of the element that has the `mct-click-elsewhere`
- * directive attached. This is useful for dismissing popups
- * and the like.
- */
- function MCTClickElsewhere($document) {
-
- // Link; install event handlers.
- function link(scope, element, attrs) {
- // Keep a reference to the body, to attach/detach
- // mouse event handlers; mousedown and mouseup cannot
- // only be attached to the element being linked, as the
- // mouse may leave this element during the drag.
- var body = $document.find('body');
-
- function clickBody(event) {
- var x = event.clientX,
- y = event.clientY,
- rect = element[0].getBoundingClientRect(),
- xMin = rect.left,
- xMax = xMin + rect.width,
- yMin = rect.top,
- yMax = yMin + rect.height;
-
- if (x < xMin || x > xMax || y < yMin || y > yMax) {
- scope.$apply(function () {
- scope.$eval(attrs.mctClickElsewhere);
- });
- }
- }
-
- body.on("mousedown", clickBody);
- scope.$on("$destroy", function () {
- body.off("mousedown", clickBody);
- });
- }
-
- return {
- // mct-drag only makes sense as an attribute
- restrict: "A",
- // Link function, to install event handlers
- link: link
- };
- }
-
- return MCTClickElsewhere;
- }
-);
-
diff --git a/platform/commonUI/general/src/directives/MCTContainer.js b/platform/commonUI/general/src/directives/MCTContainer.js
deleted file mode 100644
index ed66039fe..000000000
--- a/platform/commonUI/general/src/directives/MCTContainer.js
+++ /dev/null
@@ -1,91 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Module defining MCTContainer. Created by vwoeltje on 11/17/14.
- */
-define(
- [],
- function () {
-
- /**
- * The mct-container is similar to the mct-include directive
- * insofar as it allows templates to be referenced by
- * symbolic keys instead of by URL. Unlike mct-include, it
- * supports transclusion.
- *
- * Unlike mct-include, mct-container accepts a key as a
- * plain string attribute, instead of as an Angular
- * expression.
- *
- * @memberof platform/commonUI/general
- * @constructor
- */
- function MCTContainer(containers) {
- var containerMap = {};
-
- // Initialize container map from extensions
- containers.forEach(function (container) {
- containerMap[container.key] = container;
- });
-
- return {
-
- // Allow only at the element level
- restrict: 'E',
-
- // Support transclusion
- transclude: true,
-
- // Create a new (non-isolate) scope
- scope: true,
-
- // Populate initial scope based on attributes requested
- // by the container definition
- link: function (scope, element, attrs) {
- var key = attrs.key,
- container = containerMap[key],
- alias = "container",
- copiedAttributes = {};
-
- if (container) {
- alias = container.alias || alias;
- (container.attributes || []).forEach(function (attr) {
- copiedAttributes[attr] = attrs[attr];
- });
- }
-
- scope[alias] = copiedAttributes;
- },
-
- template: function (element, attrs) {
- var key = attrs.key,
- container = containerMap[key];
-
- return container ? container.template : "";
- }
- };
- }
-
- return MCTContainer;
- }
-);
diff --git a/platform/commonUI/general/src/directives/MCTDrag.js b/platform/commonUI/general/src/directives/MCTDrag.js
deleted file mode 100644
index d6b42ba2a..000000000
--- a/platform/commonUI/general/src/directives/MCTDrag.js
+++ /dev/null
@@ -1,177 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- /**
- * The mct-drag directive allows drag functionality
- * (in the mousedown-mousemove-mouseup sense, as opposed to
- * the drag-and-drop sense) to be attached to specific
- * elements. This takes the form of three attributes:
- *
- * * `mct-drag`: An Angular expression to evaluate during
- * drag movement.
- * * `mct-drag-down`: An Angular expression to evaluate
- * when the drag begins.
- * * `mct-drag-up`: An Angular expression to evaluate when
- * dragging ends.
- *
- * In each case, a variable `delta` will be provided to the
- * expression; this is a two-element array or the horizontal
- * and vertical pixel offset of the current mouse position
- * relative to the mouse position where dragging began.
- *
- * @memberof platform/commonUI/general
- * @constructor
- *
- */
- function MCTDrag($document, agentService) {
-
- // Link; install event handlers.
- function link(scope, element, attrs) {
- // Keep a reference to the body, to attach/detach
- // mouse event handlers; mousedown and mouseup cannot
- // only be attached to the element being linked, as the
- // mouse may leave this element during the drag.
- var body = $document.find('body'),
- isMobile = agentService.isMobile(),
- touchEvents,
- initialPosition,
- $event,
- delta;
-
- if (isMobile) {
- touchEvents = {
- start: 'touchstart',
- end: 'touchend',
- move: 'touchmove'
- };
- } else {
- touchEvents = {
- start: 'mousedown',
- end: "mouseup",
- move: "mousemove"
- };
- }
-
- // Utility function to cause evaluation of mctDrag,
- // mctDragUp, etc
- function fireListener(name) {
- // Evaluate the expression, with current delta
- scope.$eval(attrs[name], {
- delta: delta,
- $event: $event
- });
-
- // Trigger prompt digestion
- scope.$apply();
- }
-
- // Update positions (both actual and relative)
- // based on a new mouse event object.
- function updatePosition(event) {
- // Get the current position, as an array
- var currentPosition = [event.pageX, event.pageY];
-
- // Track the initial position, if one hasn't been observed
- initialPosition = initialPosition || currentPosition;
-
- // Compute relative position
- delta = currentPosition.map(function (v, i) {
- return v - initialPosition[i];
- });
-
- // Also track the plain event for firing listeners
- $event = event;
- }
-
- // Called during a drag, on mousemove
- function continueDrag(event) {
- updatePosition(event);
- fireListener("mctDrag");
-
- // Don't show selection highlights, etc
- event.preventDefault();
-
- return false;
- }
-
- // Called only when the drag ends (on mouseup)
- function endDrag(event) {
- // Detach event handlers
- body.off(touchEvents.end, endDrag);
- body.off(touchEvents.move, continueDrag);
-
- // Also call continueDrag, to fire mctDrag
- // and do its usual position update
- continueDrag(event);
-
- fireListener("mctDragUp");
-
- // Clear out start-of-drag position, target
- initialPosition = undefined;
-
- // Don't show selection highlights, etc
- event.preventDefault();
-
- return false;
- }
-
- // Called on mousedown on the element
- function startDrag(event) {
- // Listen for mouse events at the body level,
- // since the mouse may leave the element during
- // the drag.
- body.on(touchEvents.end, endDrag);
- body.on(touchEvents.move, continueDrag);
-
- // Set an initial position
- updatePosition(event);
-
- // Fire listeners, including mctDrag
- fireListener("mctDragDown");
- fireListener("mctDrag");
-
- // Don't show selection highlights, etc
- event.preventDefault();
-
- return false;
- }
-
- // Listen for start event on the element
- element.on(touchEvents.start, startDrag);
- }
-
- return {
- // mct-drag only makes sense as an attribute
- restrict: "A",
- // Link function, to install event handlers
- link: link
- };
- }
-
- return MCTDrag;
- }
-);
-
diff --git a/platform/commonUI/general/src/directives/MCTIndicators.js b/platform/commonUI/general/src/directives/MCTIndicators.js
deleted file mode 100644
index 6245a8901..000000000
--- a/platform/commonUI/general/src/directives/MCTIndicators.js
+++ /dev/null
@@ -1,40 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
- function MCTIndicators(openmct) {
- return {
- restrict: "E",
- link: function link(scope, element) {
- openmct.indicators.indicatorElements
- .forEach(function (indicatorElement) {
- element.append(indicatorElement);
- });
- }
- };
- }
-
- return MCTIndicators;
- }
-);
diff --git a/platform/commonUI/general/src/directives/MCTPopup.js b/platform/commonUI/general/src/directives/MCTPopup.js
deleted file mode 100644
index 58b145dfe..000000000
--- a/platform/commonUI/general/src/directives/MCTPopup.js
+++ /dev/null
@@ -1,72 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- function () {
-
- var TEMPLATE = "<div></div>";
-
- /**
- * The `mct-popup` directive may be used to display elements
- * which "pop up" over other parts of the page. Typically, this is
- * done in conjunction with an `ng-if` to control the visibility
- * of the popup.
- *
- * Example of usage:
- *
- * <mct-popup ng-if="someExpr">
- * <span>These are the contents of the popup!</span>
- * </mct-popup>
- *
- * @constructor
- * @memberof platform/commonUI/general
- * @param $compile Angular's $compile service
- * @param {platform/commonUI/general.PopupService} popupService
- */
- function MCTPopup($compile, popupService) {
- function link(scope, element, attrs, ctrl, transclude) {
- var div = $compile(TEMPLATE)(scope),
- rect = element.parent()[0].getBoundingClientRect(),
- position = [rect.left, rect.top],
- popup = popupService.display(div, position);
-
- div.addClass('t-popup');
- transclude(function (clone) {
- div.append(clone);
- });
-
- scope.$on('$destroy', function () {
- popup.dismiss();
- });
- }
-
- return {
- restrict: "E",
- transclude: true,
- link: link,
- scope: {}
- };
- }
-
- return MCTPopup;
- }
-);
diff --git a/platform/commonUI/general/src/directives/MCTResize.js b/platform/commonUI/general/src/directives/MCTResize.js
deleted file mode 100644
index 338021e4d..000000000
--- a/platform/commonUI/general/src/directives/MCTResize.js
+++ /dev/null
@@ -1,123 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- // Default resize interval
- var DEFAULT_INTERVAL = 100;
-
- /**
- * The mct-resize directive allows the size of a displayed
- * HTML element to be tracked. This is done by polling,
- * since the DOM API does not currently provide suitable
- * events to watch this reliably.
- *
- * Attributes related to this directive are interpreted as
- * follows:
- *
- * * `mct-resize`: An Angular expression to evaluate when
- * the size changes; the variable `bounds` will be provided
- * with two fields, `width` and `height`, both in pixels.
- * * `mct-resize-interval`: Optional; the interval, in milliseconds,
- * at which to watch for updates. In some cases checking for
- * resize can carry a cost (it forces recalculation of
- * positions within the document) so it may be preferable to watch
- * infrequently. If omitted, a default of 100ms will be used.
- * This is an Angular expression, and it will be re-evaluated after
- * each interval.
- *
- * @memberof platform/commonUI/general
- * @constructor
- *
- */
- function MCTResize($timeout) {
-
- // Link; start listening for changes to an element's size
- function link(scope, element, attrs) {
- var lastBounds,
- linking = true,
- active = true;
-
- // Determine how long to wait before the next update
- function currentInterval() {
- return attrs.mctResizeInterval
- ? scope.$eval(attrs.mctResizeInterval)
- : DEFAULT_INTERVAL;
- }
-
- // Evaluate mct-resize with the current bounds
- function fireEval(bounds) {
- // Only update when bounds actually change
- if (!lastBounds
- || lastBounds.width !== bounds.width
- || lastBounds.height !== bounds.height) {
- scope.$eval(attrs.mctResize, { bounds: bounds });
- if (!linking) { // Avoid apply-in-a-digest
- scope.$apply();
- }
-
- lastBounds = bounds;
- }
- }
-
- // Callback to fire after each timeout;
- // update bounds and schedule another timeout
- function onInterval() {
- if (!active) {
- return;
- }
-
- fireEval({
- width: element[0].offsetWidth,
- height: element[0].offsetHeight
- });
- $timeout(onInterval, currentInterval(), false);
- }
-
- // Stop running in the background
- function deactivate() {
- active = false;
- }
-
- // Unregister once out-of-scope
- scope.$on("$destroy", deactivate);
-
- // Handle the initial callback
- onInterval();
-
- // Trigger scope.$apply on subsequent changes
- linking = false;
- }
-
- return {
- // mct-resize only makes sense as an attribute
- restrict: "A",
- // Link function, to begin watching for changes
- link: link
- };
- }
-
- return MCTResize;
- }
-);
diff --git a/platform/commonUI/general/src/directives/MCTScroll.js b/platform/commonUI/general/src/directives/MCTScroll.js
deleted file mode 100644
index 791ccb817..000000000
--- a/platform/commonUI/general/src/directives/MCTScroll.js
+++ /dev/null
@@ -1,82 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- /**
- * Implements `mct-scroll-x` and `mct-scroll-y` directives. Listens
- * for scroll events and publishes their results into scope; watches
- * scope and updates scroll state to match. This varies for x- and y-
- * directives only by the attribute name chosen to find the expression,
- * and the property (scrollLeft or scrollTop) managed within the
- * element.
- *
- * This is exposed as two directives in `bundle.json`; the difference
- * is handled purely by parameterization.
- *
- * @memberof platform/commonUI/general
- * @constructor
- * @param $parse Angular's $parse
- * @param {string} property property to manage within the HTML element
- * @param {string} attribute attribute to look at for the assignable
- * Angular expression
- */
- function MCTScroll($parse, property, attribute) {
- function link(scope, element, attrs) {
- var expr = attrs[attribute],
- parsed = $parse(expr);
-
- // Set the element's scroll to match the scope's state
- function updateElement(value) {
- element[0][property] = value;
- }
-
- // Handle event; assign to scroll state to scope
- function updateScope() {
- parsed.assign(scope, element[0][property]);
- scope.$apply(expr);
- }
-
- // Initialize state in scope
- parsed.assign(scope, element[0][property]);
-
- // Update element state when value in scope changes
- scope.$watch(expr, updateElement);
-
- // Update state in scope when element is scrolled
- element.on('scroll', updateScope);
- }
-
- return {
- // Restrict to attributes
- restrict: "A",
- // Use this link function
- link: link
- };
- }
-
- return MCTScroll;
-
- }
-);
diff --git a/platform/commonUI/general/src/directives/MCTSelectable.js b/platform/commonUI/general/src/directives/MCTSelectable.js
deleted file mode 100644
index df914f7f6..000000000
--- a/platform/commonUI/general/src/directives/MCTSelectable.js
+++ /dev/null
@@ -1,83 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- /**
- * The mct-selectable directive allows selection functionality
- * (click) to be attached to specific elements.
- *
- * Example of how to use the directive:
- *
- * mct-selectable="{
- * // item is an optional domain object.
- * item: domainObject,
- * // Can define other arbitrary properties.
- * elementProxy: element,
- * controller: fixedController
- * }"
- *
- * @memberof platform/commonUI/general
- * @constructor
- */
- function MCTSelectable(openmct) {
-
- // Link; install event handlers.
- function link(scope, element, attrs) {
- var isDestroyed = false;
- scope.$on("$destroy", function () {
- isDestroyed = true;
- });
-
- openmct.$injector.get('$timeout')(function () {
- if (isDestroyed) {
- return;
- }
-
- var removeSelectable = openmct.selection.selectable(
- element[0],
- scope.$eval(attrs.mctSelectable),
- Object.prototype.hasOwnProperty.call(attrs, 'mctInitSelect')
- && scope.$eval(attrs.mctInitSelect) !== false
- );
-
- scope.$on("$destroy", function () {
- removeSelectable();
- });
- });
-
- }
-
- return {
- // mct-selectable only makes sense as an attribute
- restrict: "A",
- // Link function, to install event handlers
- link: link
- };
-
- }
-
- return MCTSelectable;
- }
-);
diff --git a/platform/commonUI/general/src/directives/MCTSplitPane.js b/platform/commonUI/general/src/directives/MCTSplitPane.js
deleted file mode 100644
index 70b7078d6..000000000
--- a/platform/commonUI/general/src/directives/MCTSplitPane.js
+++ /dev/null
@@ -1,263 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- // Pixel width to allocate for the splitter itself
- var DEFAULT_ANCHOR = 'left',
- POLLING_INTERVAL = 15, // milliseconds
- CHILDREN_WARNING_MESSAGE = [
- "Invalid mct-split-pane contents.",
- "This element should contain exactly three",
- "child elements, where the middle-most element",
- "is an mct-splitter."
- ].join(" "),
- ANCHOR_WARNING_MESSAGE = [
- "Unknown anchor provided to mct-split-pane,",
- "defaulting to",
- DEFAULT_ANCHOR + "."
- ].join(" "),
- ANCHORS = {
- left: {
- edge: "left",
- opposite: "right",
- dimension: "width",
- orientation: "vertical"
- },
- right: {
- edge: "right",
- opposite: "left",
- dimension: "width",
- orientation: "vertical",
- reversed: true
- },
- top: {
- edge: "top",
- opposite: "bottom",
- dimension: "height",
- orientation: "horizontal"
- },
- bottom: {
- edge: "bottom",
- opposite: "top",
- dimension: "height",
- orientation: "horizontal",
- reversed: true
- }
- };
-
- /**
- * Implements `mct-split-pane` directive.
- *
- * This takes the following attributes:
- * * `position`: Two-way bound scope variable which will contain
- * the pixel position of the splitter, offset from the appropriate
- * edge.
- * * `anchor`: Plain string, one of "left", "right", "top",
- * or "bottom".
- *
- * When used, an `mct-split-pane` element should contain exactly
- * three child elements, where the middle is an `mct-splitter`
- * element. These should be included in either left-to-right
- * or top-to-bottom order (depending on anchoring.) If the contents
- * do not match this form, `mct-split-pane` will issue a warning
- * and its behavior will be undefined.
- *
- * This directive works by setting the width of the element
- * nearest the anchor edge, and then positioning the other elements
- * based on its observed width. As such, `min-width`, `max-width`,
- * etc. can be set on that element to control the splitter's
- * allowable positions.
- *
- * @memberof platform/commonUI/general
- * @constructor
- */
- function MCTSplitPane($parse, $log, $interval, $window) {
- function controller($scope, $element, $attrs) {
- var anchorKey = $attrs.anchor || DEFAULT_ANCHOR,
- positionParsed = $parse($attrs.position),
- anchor,
- activeInterval,
- position,
- splitterSize,
-
- alias = $attrs.alias !== undefined
- ? "mctSplitPane-" + $attrs.alias : undefined,
-
- //convert string to number from localStorage
- userWidthPreference = $window.localStorage.getItem(alias) === null
- ? undefined : Number($window.localStorage.getItem(alias));
-
- // Get relevant size (height or width) of DOM element
- function getSize(domElement) {
- return (anchor.orientation === 'vertical'
- ? domElement.offsetWidth : domElement.offsetHeight);
- }
-
- // Apply styles to child elements
- function updateChildren(children) {
- position = userWidthPreference || position;
-
- // Pick out correct elements to update, flowing from
- // selected anchor edge.
- var first = children.eq(anchor.reversed ? 2 : 0),
- splitter = children.eq(1),
- last = children.eq(anchor.reversed ? 0 : 2),
- firstSize;
-
- splitterSize = getSize(splitter[0]);
- first.css(anchor.edge, "0px");
- first.css(anchor.dimension, position + 'px');
-
- // Get actual size (to obey min-width etc.)
- firstSize = getSize(first[0]);
- first.css(anchor.dimension, firstSize + 'px');
- splitter.css(anchor.edge, firstSize + 'px');
- splitter.css(anchor.opposite, "auto");
-
- last.css(anchor.edge, firstSize + splitterSize + 'px');
- last.css(anchor.opposite, '0px');
- position = firstSize;
- }
-
- // Update positioning of contained elements
- function updateElementPositions() {
- var children = $element.children();
-
- // Check to make sure contents are well-formed
- if (children.length !== 3
- || children[1].nodeName.toLowerCase() !== 'mct-splitter') {
- $log.warn(CHILDREN_WARNING_MESSAGE);
-
- return;
- }
-
- updateChildren(children);
- }
-
- // Enforce minimum/maximum positions
- function enforceExtrema() {
- position = Math.max(position, 0);
- position = Math.min(position, getSize($element[0]));
- }
-
- // Getter-setter for the pixel offset of the splitter,
- // relative to the current edge.
- function getSetPosition(value) {
- var prior = position;
- if (typeof value === 'number') {
- position = value;
- enforceExtrema();
- updateElementPositions();
-
- // Pass change up so this state can be shared
- if (positionParsed.assign && position !== prior) {
- positionParsed.assign($scope, position);
- }
- }
-
- return position;
- }
-
- function setUserWidthPreference(value) {
- if (alias) {
- userWidthPreference = value;
- }
- }
-
- function persistToLocalStorage(value) {
- if (alias) {
- $window.localStorage.setItem(alias, value);
- }
- }
-
- // Dynamically apply a CSS class to elements when the user
- // is actively resizing
- function toggleClass(classToToggle) {
- $element.children().toggleClass(classToToggle);
- }
-
- // Make sure anchor parameter is something we know
- if (!ANCHORS[anchorKey]) {
- $log.warn(ANCHOR_WARNING_MESSAGE);
- anchorKey = DEFAULT_ANCHOR;
- }
-
- anchor = ANCHORS[anchorKey];
-
- $scope.$watch($attrs.position, getSetPosition);
-
- $element.addClass("split-layout");
- $element.addClass(anchor.orientation);
-
- // Initialize positions
- getSetPosition(getSize(
- $element.children().eq(anchor.reversed ? 2 : 0)[0]
- ));
-
- // And poll for position changes enforced by styles
- activeInterval = $interval(function () {
- getSetPosition(getSetPosition());
- }, POLLING_INTERVAL, 0, false);
- // ...and stop polling when we're destroyed.
- $scope.$on('$destroy', function () {
- $interval.cancel(activeInterval);
- });
-
- // Interface exposed by controller, for mct-splitter to user
- return {
- anchor: function () {
- return anchor;
- },
- position: function (newPosition) {
- if (arguments.length === 0) {
- return getSetPosition();
- }
-
- setUserWidthPreference(newPosition);
-
- return getSetPosition(newPosition);
- },
- startResizing: function () {
- toggleClass('resizing');
- },
- endResizing: function (finalPosition) {
- persistToLocalStorage(finalPosition);
- toggleClass('resizing');
- }
- };
- }
-
- return {
- // Restrict to attributes
- restrict: "E",
- // Expose its controller
- controller: ['$scope', '$element', '$attrs', controller]
- };
- }
-
- return MCTSplitPane;
-
- }
-);
diff --git a/platform/commonUI/general/src/directives/MCTSplitter.js b/platform/commonUI/general/src/directives/MCTSplitter.js
deleted file mode 100644
index 65c439d75..000000000
--- a/platform/commonUI/general/src/directives/MCTSplitter.js
+++ /dev/null
@@ -1,90 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- // Pixel width to allocate for the splitter itself
- var SPLITTER_TEMPLATE = "<div class='abs'"
- + "mct-drag-down=\"splitter.startMove()\" "
- + "mct-drag=\"splitter.move(delta)\" "
- + "mct-drag-up=\"splitter.endMove()\"></div>";
-
- /**
- * Implements `mct-splitter` directive.
- * @memberof platform/commonUI/general
- * @constructor
- */
- function MCTSplitter() {
- function link(scope, element, attrs, mctSplitPane) {
- var initialPosition,
- newPosition;
-
- element.addClass("splitter");
-
- scope.splitter = {
- // Begin moving this splitter
- startMove: function () {
- mctSplitPane.startResizing();
- initialPosition = mctSplitPane.position();
- },
- // Handle user changes to splitter position
- move: function (delta) {
- var anchor = mctSplitPane.anchor(),
- index = anchor.orientation === "vertical" ? 0 : 1,
- pixelDelta = delta[index]
- * (anchor.reversed ? -1 : 1);
-
- // Update the position of this splitter
- newPosition = initialPosition + pixelDelta;
-
- if (initialPosition !== newPosition) {
- mctSplitPane.position(newPosition);
- }
- },
- // Grab the event when the user is done moving
- // the splitter and pass it on
- endMove: function () {
- mctSplitPane.endResizing(newPosition);
- }
- };
- }
-
- return {
- // Restrict to attributes
- restrict: "E",
- // Utilize the mct-split-pane controller
- require: "^mctSplitPane",
- // Expose its controller
- link: link,
- // Use the template defined above
- template: SPLITTER_TEMPLATE,
- // Create a new scope to put the splitter into
- scope: true
- };
- }
-
- return MCTSplitter;
-
- }
-);
diff --git a/platform/commonUI/general/src/directives/MCTTree.js b/platform/commonUI/general/src/directives/MCTTree.js
deleted file mode 100644
index 1e930acac..000000000
--- a/platform/commonUI/general/src/directives/MCTTree.js
+++ /dev/null
@@ -1,86 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
- 'angular',
- '../ui/TreeView'
-], function (angular, TreeView) {
- function MCTTree(gestureService, openmct) {
- function link(scope, element) {
- if (!scope.allowSelection) {
- scope.allowSelection = function () {
- return true;
- };
- }
-
- if (!scope.onSelection) {
- scope.onSelection = function () {};
- }
-
- var currentSelection = scope.selectedObject;
- var treeView = new TreeView(gestureService, openmct);
-
- function setSelection(domainObject, event) {
- if (currentSelection === domainObject) {
- return;
- }
-
- if (!scope.allowSelection(domainObject)) {
- treeView.value(currentSelection);
-
- return;
- }
-
- currentSelection = domainObject;
- scope.onSelection(domainObject);
- scope.selectedObject = domainObject;
- if (event && event instanceof MouseEvent) {
- scope.$apply();
- }
- }
-
- var unobserve = treeView.observe(setSelection);
-
- element.append(angular.element(treeView.elements()));
-
- scope.$watch('selectedObject', function (object) {
- currentSelection = object;
- treeView.value(object);
- });
- scope.$watch('rootObject', treeView.model.bind(treeView));
- scope.$on('$destroy', unobserve);
- }
-
- return {
- restrict: "E",
- link: link,
- scope: {
- rootObject: "=",
- selectedObject: "=",
- onSelection: "=?",
- allowSelection: "=?"
- }
- };
- }
-
- return MCTTree;
-});
diff --git a/platform/commonUI/general/src/filters/ReverseFilter.js b/platform/commonUI/general/src/filters/ReverseFilter.js
deleted file mode 100644
index dce477d73..000000000
--- a/platform/commonUI/general/src/filters/ReverseFilter.js
+++ /dev/null
@@ -1,42 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(function () {
-
- /**
- * Implements the `reverse` filter, which reverses text strings.
- * Useful in cases where text should be reversed for presentational
- * reasons (e.g. in conjunction with CSS tricks involving text direction),
- * allowing such behavior to be handled independently from the controller
- * layer.
- *
- * @constructor
- * @memberof platform/commonUI/general
- */
- function ReverseFilter() {
- return function reverse(value) {
- return value && value.toString().split('').reverse().join('');
- };
- }
-
- return ReverseFilter;
-});
diff --git a/platform/commonUI/general/src/services/Overlay.js b/platform/commonUI/general/src/services/Overlay.js
deleted file mode 100644
index e1f6d1ebd..000000000
--- a/platform/commonUI/general/src/services/Overlay.js
+++ /dev/null
@@ -1,186 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Module defining OverlayService. Created by deeptailor on 03/29/2018
- */
-
-define(['zepto'], function ($) {
- var OVERLAY_TEMPLATE = ''
-+ ' <div class="abs blocker"></div>'
-+ ' <div class="abs outer-holder">'
-+ ' <a class="close icon-x-in-circle"></a>'
-+ ' <div class="abs inner-holder l-flex-col">'
-+ ' <div class="t-contents flex-elem holder grows"></div>'
-+ ' <div class="bottom-bar flex-elem holder">'
-+ ' <a class="t-done s-button major">Done</a>'
-+ ' </div>'
-+ ' </div>'
-+ ' </div>';
-
- /*
- * An Overlay Service when instantiated creates an overlay dialog.
- * @param {Object} options The options object required to instantiate the overlay service
- * options = {
- * $document: document object,
- * $scope: angular $scope object,
- * element: node to be injected into overlay as a view,
- * overlayWillMount: callback executed before overlay is injected,
- * overlayWillUnmount: callback executed before overlay is removed,
- * overlayDidMount: callback executed after overlay is injected,
- * overlayDidUnmount: callback executed after overlay is removed
- * browseBarButtons: an array of desired buttons to be added to the browse bar of the overlay.
- * the array should consist of button objects containing:
- * a) class - css class to be added to the button div
- * b) title - desired button title
- * c) clickHandler - callback to be added to the click event listener of the button
- * }
- * $document, $scope and element are required
- */
-
- function Overlay(options) {
- this.element = options.$element;
- this.document = options.$document[0];
- this.$scope = options.$scope;
-
- this.overlayWillMount = options.overlayWillMount;
- this.overlayWillUnmount = options.overlayWillUnmount;
-
- this.overlayDidMount = options.overlayDidMount;
- this.overlayDidUnmount = options.overlayDidUnmount;
-
- this.browseBarButtons = options.browseBarButtons || [];
- this.buttons = [];
-
- this.openOverlay = this.openOverlay.bind(this);
- this.closeOverlay = this.closeOverlay.bind(this);
- this.toggleOverlay = this.toggleOverlay.bind(this);
- this.removeButtons = this.removeButtons.bind(this);
-
- this.isOverlayOpen = false;
- }
-
- Overlay.prototype.openOverlay = function () {
-
- if (this.overlayWillMount && typeof this.overlayWillMount === 'function') {
- this.overlayWillMount();
- }
-
- this.overlay = this.document.createElement('div');
- $(this.overlay).addClass('abs overlay l-large-view');
- this.overlay.innerHTML = OVERLAY_TEMPLATE;
-
- this.overlayContainer = this.overlay.querySelector('.t-contents');
-
- this.closeButton = this.overlay.querySelector('a.close');
- this.closeButton.addEventListener('click', this.toggleOverlay);
-
- this.doneButton = this.overlay.querySelector('a.t-done');
- this.doneButton.addEventListener('click', this.toggleOverlay);
-
- this.blocker = this.overlay.querySelector('.abs.blocker');
- this.blocker.addEventListener('click', this.toggleOverlay);
-
- this.document.body.appendChild(this.overlay);
-
- this.overlayContainer.appendChild(this.element);
-
- this.browseBar = this.overlay.querySelector('.object-browse-bar .right');
-
- if (this.browseBarButtons && Array.isArray(this.browseBarButtons)) {
- this.browseBarButtons.forEach(function (buttonObject) {
- var button = newButtonTemplate(buttonObject.class, buttonObject.title);
- this.browseBar.prepend(button);
- button.addEventListener('click', buttonObject.clickHandler);
- this.buttons.push(button);
- }.bind(this));
- }
-
- if (this.overlayDidMount && typeof this.overlayDidMount === 'function') {
- this.overlayDidMount();
- }
- };
-
- Overlay.prototype.closeOverlay = function () {
-
- if (this.overlayWillUnmount && typeof this.overlayWillUnmount === 'function') {
- this.overlayWillUnmount();
- }
-
- this.overlayContainer.removeChild(this.element);
- this.document.body.removeChild(this.overlay);
-
- this.closeButton.removeEventListener('click', this.toggleOverlay);
- this.closeButton = undefined;
-
- this.doneButton.removeEventListener('click', this.toggleOverlay);
- this.doneButton = undefined;
-
- this.blocker.removeEventListener('click', this.toggleOverlay);
- this.blocker = undefined;
-
- this.overlayContainer = undefined;
- this.overlay = undefined;
-
- this.removeButtons();
-
- if (this.overlayDidUnmount && typeof this.overlayDidUnmount === 'function') {
- this.overlayDidUnmount();
- }
- };
-
- Overlay.prototype.toggleOverlay = function (event) {
- if (event) {
- event.stopPropagation();
- }
-
- if (!this.isOverlayOpen) {
- this.openOverlay();
- this.isOverlayOpen = true;
- } else {
- this.closeOverlay();
- this.isOverlayOpen = false;
- }
- };
-
- Overlay.prototype.removeButtons = function () {
- this.buttons.forEach(function (button) {
- button.remove();
- }.bind(this));
-
- this.buttons = [];
- };
-
- function newButtonTemplate(classString, title) {
- var NEW_BUTTON_TEMPLATE = '<a class="s-button labeled' + classString + '">'
- + '<span class="title-label">' + title + '</span>'
- + '</a>';
-
- var button = document.createElement('div');
- $(button).addClass('holder flex-elem');
- button.innerHTML = NEW_BUTTON_TEMPLATE;
-
- return button;
- }
-
- return Overlay;
-});
diff --git a/platform/commonUI/general/src/services/Popup.js b/platform/commonUI/general/src/services/Popup.js
deleted file mode 100644
index ec3a0ffa3..000000000
--- a/platform/commonUI/general/src/services/Popup.js
+++ /dev/null
@@ -1,87 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- function () {
-
- /**
- * A popup is an element that has been displayed at a particular
- * location within the page.
- * @constructor
- * @memberof platform/commonUI/general
- * @param element the jqLite-wrapped element
- * @param {object} styles an object containing key-value pairs
- * of styles used to position the element.
- */
- function Popup(element, styles) {
- this.styles = styles;
- this.element = element;
-
- element.css(styles);
- }
-
- /**
- * Stop showing this popup.
- */
- Popup.prototype.dismiss = function () {
- this.element.remove();
- };
-
- /**
- * Check if this popup is positioned such that it appears to the
- * left of its original location.
- * @returns {boolean} true if the popup goes left
- */
- Popup.prototype.goesLeft = function () {
- return !this.styles.left;
- };
-
- /**
- * Check if this popup is positioned such that it appears to the
- * right of its original location.
- * @returns {boolean} true if the popup goes right
- */
- Popup.prototype.goesRight = function () {
- return !this.styles.right;
- };
-
- /**
- * Check if this popup is positioned such that it appears above
- * its original location.
- * @returns {boolean} true if the popup goes up
- */
- Popup.prototype.goesUp = function () {
- return !this.styles.top;
- };
-
- /**
- * Check if this popup is positioned such that it appears below
- * its original location.
- * @returns {boolean} true if the popup goes down
- */
- Popup.prototype.goesDown = function () {
- return !this.styles.bottom;
- };
-
- return Popup;
- }
-);
diff --git a/platform/commonUI/general/src/services/PopupService.js b/platform/commonUI/general/src/services/PopupService.js
deleted file mode 100644
index b2094a86f..000000000
--- a/platform/commonUI/general/src/services/PopupService.js
+++ /dev/null
@@ -1,124 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ['./Popup'],
- function (Popup) {
-
- /**
- * Displays popup elements at specific positions within the document.
- * @memberof platform/commonUI/general
- * @constructor
- */
- function PopupService($document, $window) {
- this.$document = $document;
- this.$window = $window;
- }
-
- /**
- * Options controlling how the popup is displayed.
- *
- * @typedef PopupOptions
- * @memberof platform/commonUI/general
- * @property {number} [offsetX] the horizontal distance, in pixels,
- * to offset the element in whichever direction it is
- * displayed. Defaults to 0.
- * @property {number} [offsetY] the vertical distance, in pixels,
- * to offset the element in whichever direction it is
- * displayed. Defaults to 0.
- * @property {number} [marginX] the horizontal position, in pixels,
- * after which to prefer to display the element to the left.
- * If negative, this is relative to the right edge of the
- * page. Defaults to half the window's width.
- * @property {number} [marginY] the vertical position, in pixels,
- * after which to prefer to display the element upward.
- * If negative, this is relative to the right edge of the
- * page. Defaults to half the window's height.
- * @property {string} [leftClass] class to apply when shifting to the left
- * @property {string} [rightClass] class to apply when shifting to the right
- * @property {string} [upClass] class to apply when shifting upward
- * @property {string} [downClass] class to apply when shifting downward
- */
-
- /**
- * Display a popup at a particular location. The location chosen will
- * be the corner of the element; the element will be positioned either
- * to the left or the right of this point depending on available
- * horizontal space, and will similarly be shifted upward or downward
- * depending on available vertical space.
- *
- * @param element the jqLite-wrapped DOM element to pop up
- * @param {number[]} position x,y position of the element, in
- * pixel coordinates. Negative values are interpreted as
- * relative to the right or bottom of the window.
- * @param {PopupOptions} [options] additional options to control
- * positioning of the popup
- * @returns {platform/commonUI/general.Popup} the popup
- */
- PopupService.prototype.display = function (element, position, options) {
- var $document = this.$document,
- $window = this.$window,
- body = $document.find('body'),
- winDim = [$window.innerWidth, $window.innerHeight],
- styles = { position: 'absolute' },
- margin,
- offset;
-
- function adjustNegatives(value, index) {
- return value < 0 ? (value + winDim[index]) : value;
- }
-
- // Defaults
- options = options || {};
- offset = [
- options.offsetX !== undefined ? options.offsetX : 0,
- options.offsetY !== undefined ? options.offsetY : 0
- ];
- margin = [options.marginX, options.marginY].map(function (m, i) {
- return m === undefined ? (winDim[i] / 2) : m;
- }).map(adjustNegatives);
-
- position = position.map(adjustNegatives);
-
- if (position[0] > margin[0]) {
- styles.right = (winDim[0] - position[0] + offset[0]) + 'px';
- } else {
- styles.left = (position[0] + offset[0]) + 'px';
- }
-
- if (position[1] > margin[1]) {
- styles.bottom = (winDim[1] - position[1] + offset[1]) + 'px';
- } else {
- styles.top = (position[1] + offset[1]) + 'px';
- }
-
- // Add the menu to the body
- body.append(element);
-
- // Return a function to dismiss the bubble
- return new Popup(element, styles);
- };
-
- return PopupService;
- }
-);
-
diff --git a/platform/commonUI/general/src/services/UrlService.js b/platform/commonUI/general/src/services/UrlService.js
deleted file mode 100644
index 72de4de1c..000000000
--- a/platform/commonUI/general/src/services/UrlService.js
+++ /dev/null
@@ -1,94 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Module defining UrlService.
- */
-define(
- [],
- function () {
-
- /**
- * The url service handles calls for url paths
- * using domain objects.
- * @constructor
- * @memberof platform/commonUI/general
- */
- function UrlService($location) {
- this.$location = $location;
- }
-
- /**
- * Returns the Url path for a specific domain object
- * without the index.html path and the view path
- * @param {string} mode value of browse or edit mode
- * for the path
- * @param {DomainObject} value of the domain object
- * to get the path of
- * @returns {string} URL for the domain object
- */
- UrlService.prototype.urlForLocation = function (mode, domainObject) {
- var context = domainObject
- && domainObject.getCapability('context'),
- objectPath = context ? context.getPath() : [],
- ids = objectPath.map(function (domainObj) {
- return domainObj.getId();
- });
-
- // Parses the path together. Starts with the
- // default index.html file, then the mode passed
- // into the service, followed by ids in the url
- // joined by '/', and lastly the view path from
- // the current location
- return mode + "/" + ids.slice(1).join("/");
- };
-
- /**
- * Returns the Url path for a specific domain object
- * including the index.html path and the view path
- * allowing a new tab to hold the correct characteristics
- * @param {string} mode value of browse or edit mode
- * for the path
- * @param {DomainObject} value of the domain object
- * to get the path of
- * @returns {string} URL for the domain object
- */
- UrlService.prototype.urlForNewTab = function (mode, domainObject) {
- var search = this.$location.search(),
- arr = [];
- for (var key in search) {
- if (Object.prototype.hasOwnProperty.call(search, key)) {
- arr.push(key + '=' + search[key]);
- }
- }
-
- var searchPath = "?" + arr.join('&'),
- newTabPath =
- "#" + this.urlForLocation(mode, domainObject)
- + searchPath;
-
- return newTabPath;
- };
-
- return UrlService;
- }
-);
diff --git a/platform/commonUI/general/src/ui/ToggleView.js b/platform/commonUI/general/src/ui/ToggleView.js
deleted file mode 100644
index 4f53614cc..000000000
--- a/platform/commonUI/general/src/ui/ToggleView.js
+++ /dev/null
@@ -1,65 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
- 'zepto',
- '../../res/templates/tree/toggle.html'
-], function ($, toggleTemplate) {
- function ToggleView(state) {
- this.expanded = Boolean(state);
- this.callbacks = [];
- this.el = $(toggleTemplate);
- this.el.on('click', function () {
- this.value(!this.expanded);
- }.bind(this));
- }
-
- ToggleView.prototype.value = function (state) {
- this.expanded = state;
-
- if (state) {
- this.el.addClass('c-disclosure-triangle--expanded');
- } else {
- this.el.removeClass('c-disclosure-triangle--expanded');
- }
-
- this.callbacks.forEach(function (callback) {
- callback(state);
- });
- };
-
- ToggleView.prototype.observe = function (callback) {
- this.callbacks.push(callback);
-
- return function () {
- this.callbacks = this.callbacks.filter(function (c) {
- return c !== callback;
- });
- }.bind(this);
- };
-
- ToggleView.prototype.elements = function () {
- return this.el;
- };
-
- return ToggleView;
-});
diff --git a/platform/commonUI/general/src/ui/TreeLabelView.js b/platform/commonUI/general/src/ui/TreeLabelView.js
deleted file mode 100644
index 491b219d1..000000000
--- a/platform/commonUI/general/src/ui/TreeLabelView.js
+++ /dev/null
@@ -1,97 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
- 'zepto',
- '../../res/templates/tree/tree-label.html'
-], function ($, labelTemplate) {
-
- function TreeLabelView(gestureService) {
- this.el = $(labelTemplate);
- this.gestureService = gestureService;
- }
-
- function isLink(domainObject) {
- var location = domainObject.getCapability('location');
-
- return location.isLink();
- }
-
- function getClass(domainObject) {
- var type = domainObject.getCapability('type');
-
- return type.getCssClass();
- }
-
- function removePreviousIconClass(el) {
- $(el).removeClass(function (index, className) {
- return (className.match (/\bicon-\S+/g) || []).join(' ');
- });
- }
-
- TreeLabelView.prototype.updateView = function (domainObject) {
- var titleEl = this.el.find('.t-title-label'),
- iconEl = this.el.find('.t-item-icon');
-
- removePreviousIconClass(iconEl);
-
- titleEl.text(domainObject ? domainObject.getModel().name : "");
- iconEl.addClass(domainObject ? getClass(domainObject) : "");
-
- if (domainObject && isLink(domainObject)) {
- iconEl.addClass('l-icon-link');
- } else {
- iconEl.removeClass('l-icon-link');
- }
- };
-
- TreeLabelView.prototype.model = function (domainObject) {
- if (this.unlisten) {
- this.unlisten();
- delete this.unlisten;
- }
-
- if (this.activeGestures) {
- this.activeGestures.destroy();
- delete this.activeGestures;
- }
-
- this.updateView(domainObject);
-
- if (domainObject) {
- this.unlisten = domainObject.getCapability('mutation')
- .listen(this.updateView.bind(this, domainObject));
-
- this.activeGestures = this.gestureService.attachGestures(
- this.elements(),
- domainObject,
- ['info', 'menu', 'drag']
- );
- }
- };
-
- TreeLabelView.prototype.elements = function () {
- return this.el;
- };
-
- return TreeLabelView;
-});
diff --git a/platform/commonUI/general/src/ui/TreeNodeView.js b/platform/commonUI/general/src/ui/TreeNodeView.js
deleted file mode 100644
index e9cb38b26..000000000
--- a/platform/commonUI/general/src/ui/TreeNodeView.js
+++ /dev/null
@@ -1,158 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
- 'zepto',
- '../../res/templates/tree/node.html',
- './ToggleView',
- './TreeLabelView'
-], function ($, nodeTemplate, ToggleView, TreeLabelView) {
-
- function TreeNodeView(gestureService, subtreeFactory, selectFn, openmct) {
- this.li = $('<li class="c-tree__item-h">');
- this.openmct = openmct;
- this.statusClasses = [];
-
- this.toggleView = new ToggleView(false);
- this.toggleView.observe(function (state) {
- if (state) {
- if (!this.subtreeView) {
- this.subtreeView = subtreeFactory();
- this.subtreeView.model(this.activeObject);
- this.li.find('.c-tree__item-subtree').eq(0)
- .append($(this.subtreeView.elements()));
- }
-
- $(this.subtreeView.elements()).removeClass('hidden');
- } else if (this.subtreeView) {
- $(this.subtreeView.elements()).addClass('hidden');
- }
- }.bind(this));
-
- this.labelView = new TreeLabelView(gestureService);
-
- $(this.labelView.elements()).on('click', function (event) {
- selectFn(this.activeObject, event);
- }.bind(this));
-
- this.li.append($(nodeTemplate));
- this.li.find('span').eq(0)
- .append($(this.toggleView.elements()))
- .append($(this.labelView.elements()));
-
- this.model(undefined);
- }
-
- TreeNodeView.prototype.updateStatusClasses = function (statuses) {
- this.statusClasses.forEach(function (statusClass) {
- this.li.removeClass(statusClass);
- }.bind(this));
-
- this.statusClasses = statuses.map(function (status) {
- return 's-status-' + status;
- });
-
- this.statusClasses.forEach(function (statusClass) {
- this.li.addClass(statusClass);
- }.bind(this));
- };
-
- TreeNodeView.prototype.model = function (domainObject) {
- if (this.unlisten) {
- this.unlisten();
- }
-
- this.activeObject = domainObject;
- if (domainObject && domainObject.hasCapability('adapter')) {
- var obj = domainObject.useCapability('adapter');
- var hasComposition = this.openmct.composition.get(obj) !== undefined;
- if (hasComposition) {
- $(this.toggleView.elements()).addClass('is-enabled');
- } else {
- $(this.toggleView.elements()).removeClass('is-enabled');
- }
- }
-
- if (domainObject && domainObject.hasCapability('status')) {
- this.unlisten = domainObject.getCapability('status')
- .listen(this.updateStatusClasses.bind(this));
- this.updateStatusClasses(
- domainObject.getCapability('status').list()
- );
- }
-
- this.labelView.model(domainObject);
- if (this.subtreeView) {
- this.subtreeView.model(domainObject);
- }
- };
-
- function getIdPath(domainObject) {
- var context = domainObject && domainObject.getCapability('context');
-
- function getId(domainObj) {
- return domainObj.getId();
- }
-
- return context ? context.getPath().map(getId) : [];
- }
-
- TreeNodeView.prototype.value = function (domainObject) {
- var activeIdPath = getIdPath(this.activeObject),
- selectedIdPath = getIdPath(domainObject);
-
- if (this.onSelectionPath) {
- this.li.find('.js-tree__item').eq(0).removeClass('is-selected');
- if (this.subtreeView) {
- this.subtreeView.value(undefined);
- }
- }
-
- this.onSelectionPath =
- Boolean(domainObject)
- && Boolean(this.activeObject)
- && (activeIdPath.length <= selectedIdPath.length)
- && activeIdPath.every(function (id, index) {
- return selectedIdPath[index] === id;
- });
-
- if (this.onSelectionPath) {
- if (activeIdPath.length === selectedIdPath.length) {
- this.li.find('.js-tree__item').eq(0).addClass('is-selected');
- } else {
- // Expand to reveal the selection
- this.toggleView.value(true);
- this.subtreeView.value(domainObject);
- }
- }
- };
-
- /**
- *
- * @returns {HTMLElement[]}
- */
- TreeNodeView.prototype.elements = function () {
- return this.li;
- };
-
- return TreeNodeView;
-});
diff --git a/platform/commonUI/general/src/ui/TreeView.js b/platform/commonUI/general/src/ui/TreeView.js
deleted file mode 100644
index a00042ffb..000000000
--- a/platform/commonUI/general/src/ui/TreeView.js
+++ /dev/null
@@ -1,141 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
- 'zepto',
- './TreeNodeView',
- '../../res/templates/tree/wait-node.html'
-], function ($, TreeNodeView, spinnerTemplate) {
-
- function TreeView(gestureService, openmct, selectFn) {
- this.ul = $('<ul class="c-tree"></ul>');
- this.nodeViews = [];
- this.callbacks = [];
- this.selectFn = selectFn || this.value.bind(this);
- this.gestureService = gestureService;
- this.pending = false;
- this.openmct = openmct;
- }
-
- TreeView.prototype.newTreeView = function () {
- return new TreeView(this.gestureService, this.openmct, this.selectFn);
- };
-
- TreeView.prototype.setSize = function (sz) {
- var nodeView;
-
- while (this.nodeViews.length < sz) {
- nodeView = new TreeNodeView(
- this.gestureService,
- this.newTreeView.bind(this),
- this.selectFn,
- this.openmct
- );
- this.nodeViews.push(nodeView);
- this.ul.append($(nodeView.elements()));
- }
-
- while (this.nodeViews.length > sz) {
- nodeView = this.nodeViews.pop();
- $(nodeView.elements()).remove();
- }
- };
-
- TreeView.prototype.loadComposition = function () {
- var self = this,
- domainObject = this.activeObject;
-
- function addNode(domainObj, index) {
- self.nodeViews[index].model(domainObj);
- }
-
- function addNodes(domainObjects) {
- if (self.pending) {
- self.pending = false;
- self.nodeViews = [];
- self.ul.empty();
- }
-
- if (domainObject === self.activeObject) {
- self.setSize(domainObjects.length);
- domainObjects.forEach(addNode);
- self.updateNodeViewSelection();
- }
- }
-
- domainObject.useCapability('composition')
- .then(addNodes);
- };
-
- TreeView.prototype.model = function (domainObject) {
- if (this.unlisten) {
- this.unlisten();
- }
-
- this.activeObject = domainObject;
- this.ul.empty();
-
- if (domainObject && domainObject.hasCapability('composition')) {
- this.pending = true;
- this.ul.append($(spinnerTemplate));
- this.unlisten = domainObject.getCapability('mutation')
- .listen(this.loadComposition.bind(this));
- this.loadComposition(domainObject);
- } else {
- this.setSize(0);
- }
- };
-
- TreeView.prototype.updateNodeViewSelection = function () {
- this.nodeViews.forEach(function (nodeView) {
- nodeView.value(this.selectedObject);
- }.bind(this));
- };
-
- TreeView.prototype.value = function (domainObject, event) {
- this.selectedObject = domainObject;
- this.updateNodeViewSelection();
- this.callbacks.forEach(function (callback) {
- callback(domainObject, event);
- });
- };
-
- TreeView.prototype.observe = function (callback) {
- this.callbacks.push(callback);
-
- return function () {
- this.callbacks = this.callbacks.filter(function (c) {
- return c !== callback;
- });
- }.bind(this);
- };
-
- /**
- *
- * @returns {HTMLElement[]}
- */
- TreeView.prototype.elements = function () {
- return this.ul;
- };
-
- return TreeView;
-});
diff --git a/platform/commonUI/general/test/SplashScreenManagerSpec.js b/platform/commonUI/general/test/SplashScreenManagerSpec.js
deleted file mode 100644
index 3aa1ee7a8..000000000
--- a/platform/commonUI/general/test/SplashScreenManagerSpec.js
+++ /dev/null
@@ -1,90 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
- '../src/SplashScreenManager'
-], function (SplashScreenManager) {
-
- describe('SplashScreenManager', function () {
- var $document,
- splashElement;
-
- beforeEach(function () {
- $document = jasmine.createSpyObj(
- '$document',
- ['querySelectorAll']
- );
-
- splashElement = jasmine.createSpyObj(
- 'splashElement',
- ['addEventListener']
- );
-
- splashElement.parentNode = jasmine.createSpyObj(
- 'splashParent',
- ['removeChild']
- );
-
- splashElement.className = 'some-class-name';
-
- $document.querySelectorAll.and.returnValue([splashElement]);
- });
-
- describe('when element exists', function () {
- beforeEach(function () {
- $document.querySelectorAll.and.returnValue([splashElement]);
-
- return new SplashScreenManager([$document]);
- });
-
- it('adds fade out class', function () {
- expect(splashElement.className).toBe('some-class-name fadeout');
- });
-
- it('removes the element when the transition ends', function () {
- expect(splashElement.addEventListener)
- .toHaveBeenCalledWith(
- 'transitionend',
- jasmine.any(Function)
- );
- expect(splashElement.parentNode.removeChild)
- .not
- .toHaveBeenCalled();
-
- splashElement.addEventListener.calls.mostRecent().args[1]();
- expect(splashElement.parentNode.removeChild)
- .toHaveBeenCalledWith(splashElement);
- });
- });
-
- it('does not error when element doesn\'t exist', function () {
- $document.querySelectorAll.and.returnValue([]);
-
- function run() {
- return new SplashScreenManager([$document]);
- }
-
- expect(run).not.toThrow();
- });
- });
-});
-
diff --git a/platform/commonUI/general/test/StyleSheetLoaderSpec.js b/platform/commonUI/general/test/StyleSheetLoaderSpec.js
deleted file mode 100644
index e9fa8d51f..000000000
--- a/platform/commonUI/general/test/StyleSheetLoaderSpec.js
+++ /dev/null
@@ -1,120 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../src/StyleSheetLoader"],
- function (StyleSheetLoader) {
-
- describe("The style sheet loader", function () {
- var testStyleSheets,
- mockDocument,
- mockPlainDocument,
- mockHead,
- mockElement,
- testBundle,
- loader; // eslint-disable-line
-
- beforeEach(function () {
- testBundle = {
- path: "a/b",
- resources: "c"
- };
-
- testStyleSheets = [
- {
- stylesheetUrl: "d.css",
- bundle: testBundle
- },
- {
- stylesheetUrl: "e.css",
- bundle: testBundle
- },
- {
- stylesheetUrl: "f.css",
- bundle: testBundle
- }
- ];
-
- mockPlainDocument =
- jasmine.createSpyObj("document", ["createElement"]);
- mockDocument = [mockPlainDocument];
- mockDocument.find = jasmine.createSpy("$document.find");
- mockHead = jasmine.createSpyObj("head", ["append"]);
- mockElement = jasmine.createSpyObj("link", ["setAttribute"]);
-
- mockDocument.find.and.returnValue(mockHead);
- mockPlainDocument.createElement.and.returnValue(mockElement);
-
- loader = new StyleSheetLoader(testStyleSheets, mockDocument);
- });
-
- it("appends one link per stylesheet extension", function () {
- expect(mockHead.append.calls.count())
- .toEqual(testStyleSheets.length);
- });
-
- it("appends links to the head", function () {
- expect(mockDocument.find).toHaveBeenCalledWith('head');
- });
-
- it("adjusts link locations", function () {
- expect(mockElement.setAttribute)
- .toHaveBeenCalledWith('href', "./a/b/c/d.css");
- });
-
- describe("for themed stylesheets", function () {
- var testTheme = "test-theme";
-
- beforeEach(function () {
- testStyleSheets = [{
- stylesheetUrl: "themed.css",
- bundle: testBundle,
- theme: testTheme
- }, {
- stylesheetUrl: "bad-theme.css",
- bundle: testBundle,
- theme: 'bad-theme'
- }];
-
- loader = new StyleSheetLoader(
- testStyleSheets,
- mockDocument,
- testTheme
- );
- });
-
- it("includes matching themes", function () {
- expect(mockElement.setAttribute)
- .toHaveBeenCalledWith('href', "./a/b/c/themed.css");
- });
-
- it("excludes mismatching themes", function () {
- expect(mockElement.setAttribute)
- .not
- .toHaveBeenCalledWith('href', "./a/b/c/bad-theme.css");
- });
- });
-
- });
- }
-);
-
diff --git a/platform/commonUI/general/test/controllers/ActionGroupControllerSpec.js b/platform/commonUI/general/test/controllers/ActionGroupControllerSpec.js
deleted file mode 100644
index 4b10e500f..000000000
--- a/platform/commonUI/general/test/controllers/ActionGroupControllerSpec.js
+++ /dev/null
@@ -1,114 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../../src/controllers/ActionGroupController"],
- function (ActionGroupController) {
-
- describe("The action group controller", function () {
- var mockScope,
- mockActions,
- controller;
-
- function mockAction(metadata, index) {
- var action = jasmine.createSpyObj(
- "action" + index,
- ["perform", "getMetadata"]
- );
- action.getMetadata.and.returnValue(metadata);
-
- return action;
- }
-
- beforeEach(function () {
- mockActions = jasmine.createSpyObj("action", ["getActions"]);
- mockScope = jasmine.createSpyObj("$scope", ["$watch"]);
- controller = new ActionGroupController(mockScope);
- });
-
- it("watches scope that may change applicable actions", function () {
- // The action capability
- expect(mockScope.$watch).toHaveBeenCalledWith(
- "action",
- jasmine.any(Function)
- );
- // The category of action to load
- expect(mockScope.$watch).toHaveBeenCalledWith(
- "parameters.category",
- jasmine.any(Function)
- );
- });
-
- it("populates the scope with grouped and ungrouped actions", function () {
- mockScope.action = mockActions;
- mockScope.parameters = { category: "test" };
-
- mockActions.getActions.and.returnValue([
- {
- group: "a",
- someKey: 0
- },
- {
- group: "a",
- someKey: 1
- },
- {
- group: "b",
- someKey: 2
- },
- {
- group: "a",
- someKey: 3
- },
- {
- group: "b",
- someKey: 4
- },
- { someKey: 5 },
- { someKey: 6 },
- {
- group: "a",
- someKey: 7
- },
- { someKey: 8 }
- ].map(mockAction));
-
- // Call the watch
- mockScope.$watch.calls.mostRecent().args[1]();
-
- // Should have grouped and ungrouped actions in scope now
- expect(mockScope.groups.length).toEqual(2);
- expect(mockScope.groups[0].length).toEqual(4); // a
- expect(mockScope.groups[1].length).toEqual(2); // b
- expect(mockScope.ungrouped.length).toEqual(3); // ungrouped
- });
-
- it("provides empty arrays when no action capability is available", function () {
- // Call the watch
- mockScope.$watch.calls.mostRecent().args[1]();
-
- expect(mockScope.groups.length).toEqual(0);
- expect(mockScope.ungrouped.length).toEqual(0);
- });
- });
- }
-);
diff --git a/platform/commonUI/general/test/controllers/ClickAwayControllerSpec.js b/platform/commonUI/general/test/controllers/ClickAwayControllerSpec.js
deleted file mode 100644
index 9eeefdea6..000000000
--- a/platform/commonUI/general/test/controllers/ClickAwayControllerSpec.js
+++ /dev/null
@@ -1,92 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../../src/controllers/ClickAwayController"],
- function (ClickAwayController) {
-
- describe("The click-away controller", function () {
- var mockDocument,
- mockTimeout,
- controller;
-
- beforeEach(function () {
- mockDocument = jasmine.createSpyObj(
- "$document",
- ["on", "off"]
- );
- mockTimeout = jasmine.createSpy('timeout');
- controller = new ClickAwayController(
- mockDocument,
- mockTimeout
- );
- });
-
- it("is initially inactive", function () {
- expect(controller.isActive()).toBe(false);
- });
-
- it("does not listen to the document before being toggled", function () {
- expect(mockDocument.on).not.toHaveBeenCalled();
- });
-
- it("tracks enabled/disabled state when toggled", function () {
- controller.toggle();
- expect(controller.isActive()).toBe(true);
- controller.toggle();
- expect(controller.isActive()).toBe(false);
- controller.toggle();
- expect(controller.isActive()).toBe(true);
- controller.toggle();
- expect(controller.isActive()).toBe(false);
- });
-
- it("allows active state to be explicitly specified", function () {
- controller.setState(true);
- expect(controller.isActive()).toBe(true);
- controller.setState(true);
- expect(controller.isActive()).toBe(true);
- controller.setState(false);
- expect(controller.isActive()).toBe(false);
- controller.setState(false);
- expect(controller.isActive()).toBe(false);
- });
-
- it("registers a mouse listener when activated", function () {
- controller.setState(true);
- expect(mockDocument.on).toHaveBeenCalled();
- });
-
- it("deactivates and detaches listener on document click", function () {
- var callback, timeout;
- controller.setState(true);
- callback = mockDocument.on.calls.mostRecent().args[1];
- callback();
- timeout = mockTimeout.calls.mostRecent().args[0];
- timeout();
- expect(controller.isActive()).toEqual(false);
- expect(mockDocument.off).toHaveBeenCalledWith("mouseup", callback);
- });
-
- });
- }
-);
diff --git a/platform/commonUI/general/test/controllers/DateTimeFieldControllerSpec.js b/platform/commonUI/general/test/controllers/DateTimeFieldControllerSpec.js
deleted file mode 100644
index b2a54017a..000000000
--- a/platform/commonUI/general/test/controllers/DateTimeFieldControllerSpec.js
+++ /dev/null
@@ -1,221 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../../src/controllers/DateTimeFieldController", "moment"],
- function (DateTimeFieldController, moment) {
-
- var TEST_FORMAT = "YYYY-MM-DD HH:mm:ss";
-
- describe("The DateTimeFieldController", function () {
- var mockScope,
- mockFormatService,
- mockFormat,
- controller;
-
- function fireWatch(expr, value) {
- mockScope.$watch.calls.all().forEach(function (call) {
- if (call.args[0] === expr) {
- call.args[1](value);
- }
- });
- }
-
- beforeEach(function () {
- mockScope = jasmine.createSpyObj('$scope', ['$watch']);
- mockFormatService =
- jasmine.createSpyObj('formatService', ['getFormat']);
- mockFormat = jasmine.createSpyObj('format', [
- 'parse',
- 'validate',
- 'format'
- ]);
-
- mockFormatService.getFormat.and.returnValue(mockFormat);
-
- mockFormat.validate.and.callFake(function (text) {
- return moment.utc(text, TEST_FORMAT).isValid();
- });
- mockFormat.parse.and.callFake(function (text) {
- return moment.utc(text, TEST_FORMAT).valueOf();
- });
- mockFormat.format.and.callFake(function (value) {
- return moment.utc(value).format(TEST_FORMAT);
- });
-
- mockScope.ngModel = { testField: 12321 };
- mockScope.field = "testField";
- mockScope.structure = { format: "someFormat" };
- mockScope.ngBlur = jasmine.createSpy('blur');
-
- controller = new DateTimeFieldController(
- mockScope,
- mockFormatService
- );
- fireWatch("ngModel[field]", mockScope.ngModel.testField);
- });
-
- it("updates text from model values", function () {
- var testTime = mockFormat.parse("1977-05-25 17:30:00");
- mockScope.ngModel.testField = testTime;
- fireWatch("ngModel[field]", testTime);
- expect(mockScope.textValue).toEqual("1977-05-25 17:30:00");
- });
-
- describe("when valid text is entered", function () {
- var newText;
-
- beforeEach(function () {
- newText = "1977-05-25 17:30:00";
- mockScope.textValue = newText;
- fireWatch("textValue", newText);
- });
-
- it("updates models from user-entered text", function () {
- expect(mockScope.ngModel.testField)
- .toEqual(mockFormat.parse(newText));
- expect(mockScope.textInvalid).toBeFalsy();
- });
-
- it("does not indicate a blur event", function () {
- expect(mockScope.ngBlur).not.toHaveBeenCalled();
- });
- });
-
- describe("when a date is chosen via the date picker", function () {
- var newValue;
-
- beforeEach(function () {
- newValue = 12345654321;
- mockScope.pickerModel.value = newValue;
- fireWatch("pickerModel.value", newValue);
- });
-
- it("updates models", function () {
- expect(mockScope.ngModel.testField).toEqual(newValue);
- });
-
- it("fires a blur event", function () {
- expect(mockScope.ngBlur).toHaveBeenCalled();
- });
- });
-
- it("exposes toggle state for date-time picker", function () {
- expect(mockScope.picker.active).toBe(false);
- });
-
- describe("when user input is invalid", function () {
- var newText, oldText, oldValue;
-
- beforeEach(function () {
- newText = "Not a date";
- oldValue = mockScope.ngModel.testField;
- oldText = mockScope.textValue;
- mockScope.textValue = newText;
- fireWatch("textValue", newText);
- });
-
- it("displays error state", function () {
- expect(mockScope.textInvalid).toBeTruthy();
- });
-
- it("does not modify model state", function () {
- expect(mockScope.ngModel.testField).toEqual(oldValue);
- });
-
- it("does not modify user input", function () {
- expect(mockScope.textValue).toEqual(newText);
- });
-
- it("restores valid text values on request", function () {
- mockScope.restoreTextValue();
- expect(mockScope.textValue).toEqual(oldText);
- });
- });
-
- it("does not modify valid but irregular user input", function () {
- // Don't want the controller "fixing" bad or
- // irregularly-formatted input out from under
- // the user's fingertips.
- var newText = "2015-3-3 01:02:04",
- oldValue = mockScope.ngModel.testField;
-
- mockFormat.validate.and.returnValue(true);
- mockFormat.parse.and.returnValue(42);
- mockScope.textValue = newText;
- fireWatch("textValue", newText);
-
- expect(mockScope.textValue).toEqual(newText);
- expect(mockScope.ngModel.testField).toEqual(42);
- expect(mockScope.ngModel.testField).not.toEqual(oldValue);
- });
-
- it("obtains a format from the format service", function () {
- fireWatch('structure.format', mockScope.structure.format);
- expect(mockFormatService.getFormat)
- .toHaveBeenCalledWith(mockScope.structure.format);
- });
-
- it("throws an error for unknown formats", function () {
- mockFormatService.getFormat.and.returnValue(undefined);
- expect(function () {
- fireWatch("structure.format", "some-format");
- }).toThrow();
- });
-
- describe("using the obtained format", function () {
- var testValue = 1234321,
- testText = "some text";
-
- beforeEach(function () {
- mockFormat.validate.and.returnValue(true);
- mockFormat.parse.and.returnValue(testValue);
- mockFormat.format.and.returnValue(testText);
- });
-
- it("parses user input", function () {
- var newText = "some other new text";
- mockScope.textValue = newText;
- fireWatch("textValue", newText);
- expect(mockFormat.parse).toHaveBeenCalledWith(newText);
- expect(mockScope.ngModel.testField).toEqual(testValue);
- });
-
- it("validates user input", function () {
- var newText = "some other new text";
- mockScope.textValue = newText;
- fireWatch("textValue", newText);
- expect(mockFormat.validate).toHaveBeenCalledWith(newText);
- });
-
- it("formats model data for display", function () {
- var newValue = 42;
- mockScope.ngModel.testField = newValue;
- fireWatch("ngModel[field]", newValue);
- expect(mockFormat.format).toHaveBeenCalledWith(newValue);
- expect(mockScope.textValue).toEqual(testText);
- });
- });
-
- });
- }
-);
diff --git a/platform/commonUI/general/test/controllers/DateTimePickerControllerSpec.js b/platform/commonUI/general/test/controllers/DateTimePickerControllerSpec.js
deleted file mode 100644
index 24c771d7a..000000000
--- a/platform/commonUI/general/test/controllers/DateTimePickerControllerSpec.js
+++ /dev/null
@@ -1,193 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../../src/controllers/DateTimePickerController", "moment"],
- function (DateTimePickerController, moment) {
-
- describe("The DateTimePickerController", function () {
- var mockScope,
- mockNow,
- controller;
-
- function fireWatch(expr, value) {
- mockScope.$watch.calls.all().forEach(function (call) {
- if (call.args[0] === expr) {
- call.args[1](value);
- }
- });
- }
-
- function fireWatchCollection(expr, value) {
- mockScope.$watchCollection.calls.all().forEach(function (call) {
- if (call.args[0] === expr) {
- call.args[1](value);
- }
- });
- }
-
- beforeEach(function () {
- mockScope = jasmine.createSpyObj(
- "$scope",
- ["$apply", "$watch", "$watchCollection"]
- );
- mockScope.ngModel = {};
- mockScope.field = "testField";
- mockNow = jasmine.createSpy('now');
- controller = new DateTimePickerController(mockScope, mockNow);
- });
-
- it("watches the model that was passed in", function () {
- expect(mockScope.$watch).toHaveBeenCalledWith(
- "ngModel[field]",
- jasmine.any(Function)
- );
- });
-
- it("updates value in model when values in scope change", function () {
- mockScope.date = {
- year: 1998,
- month: 0,
- day: 6
- };
- mockScope.time = {
- hours: 12,
- minutes: 34,
- seconds: 56
- };
- fireWatchCollection("date", mockScope.date);
- expect(mockScope.ngModel[mockScope.field])
- .toEqual(moment.utc("1998-01-06 12:34:56").valueOf());
- });
-
- describe("once initialized with model state", function () {
- var testTime = moment.utc("1998-01-06 12:34:56").valueOf();
-
- beforeEach(function () {
- fireWatch("ngModel[field]", testTime);
- });
-
- it("exposes date/time values in scope", function () {
- expect(mockScope.date.year).toEqual(1998);
- expect(mockScope.date.month).toEqual(0); // Months are zero-indexed
- expect(mockScope.date.day).toEqual(6);
- expect(mockScope.time.hours).toEqual(12);
- expect(mockScope.time.minutes).toEqual(34);
- expect(mockScope.time.seconds).toEqual(56);
- });
-
- it("provides names for time properties", function () {
- Object.keys(mockScope.time).forEach(function (key) {
- expect(mockScope.nameFor(key))
- .toEqual(jasmine.any(String));
- });
- });
-
- it("provides options for time properties", function () {
- Object.keys(mockScope.time).forEach(function (key) {
- expect(mockScope.optionsFor(key))
- .toEqual(jasmine.any(Array));
- });
- });
-
- it("exposes times to populate calendar as a table", function () {
- // Verify that data structure is as expected by template
- expect(mockScope.table).toEqual(jasmine.any(Array));
- expect(mockScope.table[0]).toEqual(jasmine.any(Array));
- expect(mockScope.table[0][0]).toEqual({
- year: jasmine.any(Number),
- month: jasmine.any(Number),
- day: jasmine.any(Number),
- dayOfYear: jasmine.any(Number)
- });
- });
-
- it("contains the current date in its initial table", function () {
- var matchingCell;
- // Should be able to find the selected date
- mockScope.table.forEach(function (row) {
- row.forEach(function (cell) {
- if (cell.dayOfYear === 6) {
- matchingCell = cell;
- }
- });
- });
- expect(matchingCell).toEqual({
- year: 1998,
- month: 0,
- day: 6,
- dayOfYear: 6
- });
- });
-
- it("allows the displayed month to be advanced", function () {
- // Around the edges of the displayed calendar we
- // may be in previous or subsequent month, so
- // test around the middle.
- var i, originalMonth = mockScope.table[2][0].month;
-
- function mod12(month) {
- return ((month % 12) + 12) % 12;
- }
-
- for (i = 1; i <= 12; i += 1) {
- mockScope.changeMonth(1);
- expect(mockScope.table[2][0].month)
- .toEqual(mod12(originalMonth + i));
- }
-
- for (i = 11; i >= -12; i -= 1) {
- mockScope.changeMonth(-1);
- expect(mockScope.table[2][0].month)
- .toEqual(mod12(originalMonth + i));
- }
- });
-
- it("allows checking if a cell is in the current month", function () {
- expect(mockScope.isInCurrentMonth(mockScope.table[2][0]))
- .toBe(true);
- });
-
- it("allows cells to be selected", function () {
- mockScope.select(mockScope.table[2][0]);
- expect(mockScope.isSelected(mockScope.table[2][0]))
- .toBe(true);
- mockScope.select(mockScope.table[2][1]);
- expect(mockScope.isSelected(mockScope.table[2][0]))
- .toBe(false);
- expect(mockScope.isSelected(mockScope.table[2][1]))
- .toBe(true);
- });
-
- it("allows cells to be compared", function () {
- var table = mockScope.table;
- expect(mockScope.dateEquals(table[2][0], table[2][1]))
- .toBe(false);
- expect(mockScope.dateEquals(table[2][1], table[2][1]))
- .toBe(true);
- });
-
- });
-
- });
- }
-);
diff --git a/platform/commonUI/general/test/controllers/GetterSetterControllerSpec.js b/platform/commonUI/general/test/controllers/GetterSetterControllerSpec.js
deleted file mode 100644
index bf6ea3891..000000000
--- a/platform/commonUI/general/test/controllers/GetterSetterControllerSpec.js
+++ /dev/null
@@ -1,83 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../../src/controllers/GetterSetterController"],
- function (GetterSetterController) {
-
- describe("The getter-setter controller", function () {
- var mockScope,
- mockModel,
- controller;
-
- beforeEach(function () {
- mockScope = jasmine.createSpyObj("$scope", ["$watch"]);
- mockModel = jasmine.createSpy("ngModel");
- mockScope.ngModel = mockModel;
- controller = new GetterSetterController(mockScope);
- });
-
- it("watches for changes to external and internal mode", function () {
- expect(mockScope.$watch).toHaveBeenCalledWith(
- "ngModel()",
- jasmine.any(Function)
- );
- expect(mockScope.$watch).toHaveBeenCalledWith(
- "getterSetter.value",
- jasmine.any(Function)
- );
- });
-
- it("updates an external function when changes are detected", function () {
- mockScope.getterSetter.value = "some new value";
- // Verify precondition
- expect(mockScope.ngModel)
- .not.toHaveBeenCalledWith("some new value");
- // Fire the matching watcher
- mockScope.$watch.calls.all().forEach(function (call) {
- if (call.args[0] === "getterSetter.value") {
- call.args[1](mockScope.getterSetter.value);
- }
- });
- // Verify getter-setter was notified
- expect(mockScope.ngModel)
- .toHaveBeenCalledWith("some new value");
- });
-
- it("updates internal state when external changes are detected", function () {
- mockScope.ngModel.and.returnValue("some other new value");
- // Verify precondition
- expect(mockScope.getterSetter.value).toBeUndefined();
- // Fire the matching watcher
- mockScope.$watch.calls.all().forEach(function (call) {
- if (call.args[0] === "ngModel()") {
- call.args[1]("some other new value");
- }
- });
- // Verify state in scope was updated
- expect(mockScope.getterSetter.value)
- .toEqual("some other new value");
- });
-
- });
- }
-);
diff --git a/platform/commonUI/general/test/controllers/ObjectInspectorControllerSpec.js b/platform/commonUI/general/test/controllers/ObjectInspectorControllerSpec.js
deleted file mode 100644
index 5934ee650..000000000
--- a/platform/commonUI/general/test/controllers/ObjectInspectorControllerSpec.js
+++ /dev/null
@@ -1,113 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Created by shale on 08/24/2015.
- */
-define(
- ["../../src/controllers/ObjectInspectorController"],
- function (ObjectInspectorController) {
-
- describe("The object inspector controller ", function () {
- var mockScope,
- mockObjectService,
- mockPromise,
- mockDomainObject,
- mockContextCapability,
- mockLocationCapability,
- controller;
-
- beforeEach(function () {
- mockScope = jasmine.createSpyObj(
- "$scope",
- ["$watch", "$on"]
- );
-
- mockObjectService = jasmine.createSpyObj(
- "objectService",
- ["getObjects"]
- );
- mockPromise = jasmine.createSpyObj(
- "promise",
- ["then"]
- );
- mockObjectService.getObjects.and.returnValue(mockPromise);
-
- mockDomainObject = jasmine.createSpyObj(
- "selectedObject",
- ["hasCapability", "getCapability", "useCapability", "getModel"]
- );
- mockDomainObject.getModel.and.returnValue({location: 'somewhere'});
- mockDomainObject.hasCapability.and.returnValue(true);
-
- mockContextCapability = jasmine.createSpyObj(
- "context capability",
- ["getParent"]
- );
- mockLocationCapability = jasmine.createSpyObj(
- "location capability",
- ["isLink"]
- );
-
- mockDomainObject.getCapability.and.callFake(function (param) {
- if (param === 'location') {
- return mockLocationCapability;
- } else if (param === 'context') {
- return mockContextCapability;
- } else if (param === 'mutation') {
- return {
- listen: function () {
- return true;
- }
- };
- }
- });
-
- mockScope.domainObject = mockDomainObject;
- controller = new ObjectInspectorController(mockScope, mockObjectService);
- });
-
- it("watches for changes to the selected object", function () {
- expect(mockScope.$watch).toHaveBeenCalledWith('domainObject', jasmine.any(Function));
- });
-
- it("looks for contextual parent objects", function () {
- mockScope.$watch.calls.mostRecent().args[1]();
- expect(mockContextCapability.getParent).toHaveBeenCalled();
- });
-
- it("if link, looks for primary parent objects", function () {
- mockLocationCapability.isLink.and.returnValue(true);
-
- mockScope.$watch.calls.mostRecent().args[1]();
- expect(mockDomainObject.getModel).toHaveBeenCalled();
- expect(mockObjectService.getObjects).toHaveBeenCalled();
- mockPromise.then.calls.mostRecent().args[0]({'somewhere': mockDomainObject});
- });
-
- it("gets metadata", function () {
- mockScope.$watch.calls.mostRecent().args[1]();
- expect(mockDomainObject.useCapability).toHaveBeenCalledWith('metadata');
- });
- });
- }
-);
diff --git a/platform/commonUI/general/test/controllers/SelectorControllerSpec.js b/platform/commonUI/general/test/controllers/SelectorControllerSpec.js
deleted file mode 100644
index b2b239770..000000000
--- a/platform/commonUI/general/test/controllers/SelectorControllerSpec.js
+++ /dev/null
@@ -1,186 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../../src/controllers/SelectorController"],
- function (SelectorController) {
-
- describe("The controller for the 'selector' control", function () {
- var mockObjectService,
- mockScope,
- mockDomainObject,
- mockType,
- mockDomainObjects,
- controller;
-
- function promiseOf(v) {
- return (v || {}).then ? v : {
- then: function (callback) {
- return promiseOf(callback(v));
- }
- };
- }
-
- function makeMockObject(id) {
- var mockObject = jasmine.createSpyObj(
- 'object-' + id,
- ['getId']
- );
- mockObject.getId.and.returnValue(id);
-
- return mockObject;
- }
-
- beforeEach(function () {
- mockObjectService = jasmine.createSpyObj(
- 'objectService',
- ['getObjects']
- );
- mockScope = jasmine.createSpyObj(
- '$scope',
- ['$watch', '$watchCollection']
- );
- mockDomainObject = jasmine.createSpyObj(
- 'domainObject',
- ['getCapability', 'hasCapability']
- );
- mockType = jasmine.createSpyObj(
- 'type',
- ['instanceOf']
- );
- mockDomainObjects = {};
-
- ["ROOT", "abc", "def", "xyz"].forEach(function (id) {
- mockDomainObjects[id] = makeMockObject(id);
- });
-
- mockDomainObject.getCapability.and.returnValue(mockType);
- mockObjectService.getObjects.and.returnValue(promiseOf(mockDomainObjects));
- mockScope.field = "testField";
- mockScope.ngModel = {};
-
- controller = new SelectorController(
- mockObjectService,
- mockScope
- );
- });
-
- it("loads the root object", function () {
- expect(mockObjectService.getObjects)
- .toHaveBeenCalledWith(["ROOT"]);
- });
-
- it("watches for changes in selection in left-hand tree", function () {
- var testObject = {
- a: 123,
- b: 456
- };
- // This test is sensitive to ordering of watch calls
- expect(mockScope.$watch.calls.count()).toEqual(1);
- // Make sure we're watching the correct object
- controller.treeModel.selectedObject = testObject;
- expect(mockScope.$watch.calls.all()[0].args[0]()).toBe(testObject);
- });
-
- it("watches for changes in controlled property", function () {
- var testValue = ["a", "b", 1, 2];
- // This test is sensitive to ordering of watch calls
- expect(mockScope.$watchCollection.calls.count()).toEqual(1);
- // Make sure we're watching the correct object
- mockScope.ngModel = { testField: testValue };
- expect(mockScope.$watchCollection.calls.all()[0].args[0]()).toBe(testValue);
- });
-
- it("rejects selection of incorrect types", function () {
- mockScope.structure = { type: "someType" };
- mockType.instanceOf.and.returnValue(false);
- controller.treeModel.selectedObject = mockDomainObject;
- // Fire the watch
- mockScope.$watch.calls.all()[0].args[1](mockDomainObject);
- // Should have cleared the selection
- expect(controller.treeModel.selectedObject).toBeUndefined();
- // Verify interaction (that instanceOf got a useful argument)
- expect(mockType.instanceOf).toHaveBeenCalledWith("someType");
- });
-
- it("permits selection of matching types", function () {
- mockScope.structure = { type: "someType" };
- mockType.instanceOf.and.returnValue(true);
- controller.treeModel.selectedObject = mockDomainObject;
- // Fire the watch
- mockScope.$watch.calls.all()[0].args[1](mockDomainObject);
- // Should have preserved the selection
- expect(controller.treeModel.selectedObject).toEqual(mockDomainObject);
- // Verify interaction (that instanceOf got a useful argument)
- expect(mockType.instanceOf).toHaveBeenCalledWith("someType");
- });
-
- it("loads objects when the underlying list changes", function () {
- var testIds = ["abc", "def", "xyz"];
- // This test is sensitive to ordering of watch calls
- expect(mockScope.$watchCollection.calls.count()).toEqual(1);
- // Make sure we're watching the correct object
- mockScope.ngModel = { testField: testIds };
- // Fire the watch
- mockScope.$watchCollection.calls.all()[0].args[1](testIds);
- // Should have loaded the corresponding objects
- expect(mockObjectService.getObjects).toHaveBeenCalledWith(testIds);
- });
-
- it("exposes the root object to populate the left-hand tree", function () {
- expect(controller.root()).toEqual(mockDomainObjects.ROOT);
- });
-
- it("adds objects to the underlying model", function () {
- expect(mockScope.ngModel.testField).toBeUndefined();
- controller.select(mockDomainObjects.def);
- expect(mockScope.ngModel.testField).toEqual(["def"]);
- controller.select(mockDomainObjects.abc);
- expect(mockScope.ngModel.testField).toEqual(["def", "abc"]);
- });
-
- it("removes objects to the underlying model", function () {
- controller.select(mockDomainObjects.def);
- controller.select(mockDomainObjects.abc);
- expect(mockScope.ngModel.testField).toEqual(["def", "abc"]);
- controller.deselect(mockDomainObjects.def);
- expect(mockScope.ngModel.testField).toEqual(["abc"]);
- });
-
- it("provides a list of currently-selected objects", function () {
- // Verify precondition
- expect(controller.selected()).toEqual([]);
- // Select some objects
- controller.select(mockDomainObjects.def);
- controller.select(mockDomainObjects.abc);
- // Fire the watch for the id changes...
- mockScope.$watchCollection.calls.all()[0].args[1](
- mockScope.$watchCollection.calls.all()[0].args[0]()
- );
- // Should have loaded and exposed those objects
- expect(controller.selected()).toEqual(
- [mockDomainObjects.def, mockDomainObjects.abc]
- );
- });
- });
- }
-);
diff --git a/platform/commonUI/general/test/controllers/TimeRangeControllerSpec.js b/platform/commonUI/general/test/controllers/TimeRangeControllerSpec.js
deleted file mode 100644
index 3b59d82fd..000000000
--- a/platform/commonUI/general/test/controllers/TimeRangeControllerSpec.js
+++ /dev/null
@@ -1,317 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../../src/controllers/TimeRangeController", "moment"],
- function (TimeRangeController, moment) {
-
- var SEC = 1000,
- MIN = 60 * SEC,
- HOUR = 60 * MIN,
- DAY = 24 * HOUR;
-
- describe("The TimeRangeController", function () {
- var mockScope,
- mockFormatService,
- testDefaultFormat,
- mockTimeout,
- mockNow,
- mockFormat,
- controller;
-
- function fireWatch(expr, value) {
- mockScope.$watch.calls.all().forEach(function (call) {
- if (call.args[0] === expr) {
- call.args[1](value);
- }
- });
- }
-
- function fireWatchCollection(expr, value) {
- mockScope.$watchCollection.calls.all().forEach(function (call) {
- if (call.args[0] === expr) {
- call.args[1](value);
- }
- });
- }
-
- beforeEach(function () {
- mockTimeout = function (fn) {
- return fn();
- };
-
- mockScope = jasmine.createSpyObj(
- "$scope",
- ["$apply", "$watch", "$watchCollection"]
- );
- mockFormatService = jasmine.createSpyObj(
- "formatService",
- ["getFormat"]
- );
- testDefaultFormat = 'utc';
- mockFormat = jasmine.createSpyObj(
- "format",
- ["validate", "format", "parse"]
- );
-
- mockFormatService.getFormat.and.returnValue(mockFormat);
-
- mockFormat.format.and.callFake(function (value) {
- return moment.utc(value).format("YYYY-MM-DD HH:mm:ss");
- });
-
- mockNow = jasmine.createSpy('now');
-
- controller = new TimeRangeController(
- mockScope,
- mockTimeout,
- mockFormatService,
- testDefaultFormat,
- mockNow
- );
- });
-
- it("watches the model that was passed in", function () {
- expect(mockScope.$watchCollection)
- .toHaveBeenCalledWith("ngModel", jasmine.any(Function));
- });
-
- it("exposes start time validator", function () {
- var testValue = 42000000;
- mockScope.formModel = { end: testValue };
- expect(controller.validateStart(testValue + 1))
- .toBe(false);
- expect(controller.validateStart(testValue - 60 * 60 * 1000 - 1))
- .toBe(true);
- });
-
- it("exposes end time validator", function () {
- var testValue = 42000000;
- mockScope.formModel = { start: testValue };
- expect(controller.validateEnd(testValue - 1))
- .toBe(false);
- expect(controller.validateEnd(testValue + 60 * 60 * 1000 + 1))
- .toBe(true);
- });
-
- describe("when changes are made via form entry", function () {
- beforeEach(function () {
- mockScope.ngModel = {
- outer: {
- start: DAY * 2,
- end: DAY * 3
- },
- inner: {
- start: DAY * 2.25,
- end: DAY * 2.75
- }
- };
- mockScope.formModel = {
- start: DAY * 10000,
- end: DAY * 11000
- };
- });
-
- it('updates all changed bounds when requested', function () {
- fireWatchCollection("formModel", mockScope.formModel);
- fireWatch("formModel.start", mockScope.formModel.start);
- fireWatch("formModel.end", mockScope.formModel.end);
-
- expect(mockScope.ngModel.outer.start)
- .not.toEqual(mockScope.formModel.start);
- expect(mockScope.ngModel.inner.start)
- .not.toEqual(mockScope.formModel.start);
-
- expect(mockScope.ngModel.outer.end)
- .not.toEqual(mockScope.formModel.end);
- expect(mockScope.ngModel.inner.end)
- .not.toEqual(mockScope.formModel.end);
-
- controller.updateBoundsFromForm();
-
- expect(mockScope.ngModel.outer.start)
- .toEqual(mockScope.formModel.start);
- expect(mockScope.ngModel.inner.start)
- .toEqual(mockScope.formModel.start);
-
- expect(mockScope.ngModel.outer.end)
- .toEqual(mockScope.formModel.end);
- expect(mockScope.ngModel.inner.end)
- .toEqual(mockScope.formModel.end);
- });
-
- it('updates changed start bound when requested', function () {
- fireWatchCollection("formModel", mockScope.formModel);
- fireWatch("formModel.start", mockScope.formModel.start);
-
- expect(mockScope.ngModel.outer.start)
- .not.toEqual(mockScope.formModel.start);
- expect(mockScope.ngModel.inner.start)
- .not.toEqual(mockScope.formModel.start);
-
- expect(mockScope.ngModel.outer.end)
- .not.toEqual(mockScope.formModel.end);
- expect(mockScope.ngModel.inner.end)
- .not.toEqual(mockScope.formModel.end);
-
- controller.updateBoundsFromForm();
-
- expect(mockScope.ngModel.outer.start)
- .toEqual(mockScope.formModel.start);
- expect(mockScope.ngModel.inner.start)
- .toEqual(mockScope.formModel.start);
-
- expect(mockScope.ngModel.outer.end)
- .not.toEqual(mockScope.formModel.end);
- expect(mockScope.ngModel.inner.end)
- .not.toEqual(mockScope.formModel.end);
- });
-
- it('updates changed end bound when requested', function () {
- fireWatchCollection("formModel", mockScope.formModel);
- fireWatch("formModel.end", mockScope.formModel.end);
-
- expect(mockScope.ngModel.outer.start)
- .not.toEqual(mockScope.formModel.start);
- expect(mockScope.ngModel.inner.start)
- .not.toEqual(mockScope.formModel.start);
-
- expect(mockScope.ngModel.outer.end)
- .not.toEqual(mockScope.formModel.end);
- expect(mockScope.ngModel.inner.end)
- .not.toEqual(mockScope.formModel.end);
-
- controller.updateBoundsFromForm();
-
- expect(mockScope.ngModel.outer.start)
- .not.toEqual(mockScope.formModel.start);
- expect(mockScope.ngModel.inner.start)
- .not.toEqual(mockScope.formModel.start);
-
- expect(mockScope.ngModel.outer.end)
- .toEqual(mockScope.formModel.end);
- expect(mockScope.ngModel.inner.end)
- .toEqual(mockScope.formModel.end);
- });
- });
-
- describe("when dragged", function () {
- beforeEach(function () {
- mockScope.ngModel = {
- outer: {
- start: DAY * 1000,
- end: DAY * 1001
- },
- inner: {
- start: DAY * 1000 + HOUR * 3,
- end: DAY * 1001 - HOUR * 3
- }
- };
- mockScope.spanWidth = 1000;
- fireWatch("spanWidth", mockScope.spanWidth);
- fireWatchCollection("ngModel", mockScope.ngModel);
- });
-
- it("updates the start time for left drags", function () {
- controller.startLeftDrag();
- controller.leftDrag(250);
- expect(mockScope.ngModel.inner.start)
- .toEqual(DAY * 1000 + HOUR * 9);
- });
-
- it("updates the end time for right drags", function () {
- controller.startRightDrag();
- controller.rightDrag(-250);
- expect(mockScope.ngModel.inner.end)
- .toEqual(DAY * 1000 + HOUR * 15);
- });
-
- it("updates both start and end for middle drags", function () {
- controller.startMiddleDrag();
- controller.middleDrag(-125);
- expect(mockScope.ngModel.inner).toEqual({
- start: DAY * 1000,
- end: DAY * 1000 + HOUR * 18
- });
- controller.middleDrag(250);
- expect(mockScope.ngModel.inner).toEqual({
- start: DAY * 1000 + HOUR * 6,
- end: DAY * 1001
- });
- });
-
- it("enforces a minimum inner span", function () {
- controller.startRightDrag();
- controller.rightDrag(-9999999);
- expect(mockScope.ngModel.inner.end)
- .toBeGreaterThan(mockScope.ngModel.inner.start);
- });
- });
-
- describe("when outer bounds are changed", function () {
- beforeEach(function () {
- mockScope.ngModel = {
- outer: {
- start: DAY * 1000,
- end: DAY * 1001
- },
- inner: {
- start: DAY * 1000 + HOUR * 3,
- end: DAY * 1001 - HOUR * 3
- }
- };
- mockScope.spanWidth = 1000;
- fireWatch("spanWidth", mockScope.spanWidth);
- fireWatchCollection("ngModel", mockScope.ngModel);
- });
-
- it("enforces a minimum inner span when outer span changes", function () {
- mockScope.ngModel.outer.end =
- mockScope.ngModel.outer.start - DAY * 100;
- fireWatch(
- "ngModel.outer.end",
- mockScope.ngModel.outer.end
- );
- expect(mockScope.ngModel.inner.end)
- .toBeGreaterThan(mockScope.ngModel.inner.start);
- });
-
- });
-
- it("watches for changes in format selection", function () {
- expect(mockFormatService.getFormat)
- .not.toHaveBeenCalledWith('test-format');
- fireWatch("parameters.format", 'test-format');
- expect(mockFormatService.getFormat)
- .toHaveBeenCalledWith('test-format');
- });
-
- it("throws an error for unknown formats", function () {
- mockFormatService.getFormat.and.returnValue(undefined);
- expect(function () {
- fireWatch("parameters.format", "some-format");
- }).toThrow();
- });
-
- });
- }
-);
diff --git a/platform/commonUI/general/test/controllers/ToggleControllerSpec.js b/platform/commonUI/general/test/controllers/ToggleControllerSpec.js
deleted file mode 100644
index 9d7c8c4e0..000000000
--- a/platform/commonUI/general/test/controllers/ToggleControllerSpec.js
+++ /dev/null
@@ -1,62 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../../src/controllers/ToggleController"],
- function (ToggleController) {
-
- describe("The toggle controller", function () {
- var controller;
-
- beforeEach(function () {
- controller = new ToggleController();
- });
-
- it("is initially inactive", function () {
- expect(controller.isActive()).toBe(false);
- });
-
- it("tracks enabled/disabled state when toggled", function () {
- controller.toggle();
- expect(controller.isActive()).toBe(true);
- controller.toggle();
- expect(controller.isActive()).toBe(false);
- controller.toggle();
- expect(controller.isActive()).toBe(true);
- controller.toggle();
- expect(controller.isActive()).toBe(false);
- });
-
- it("allows active state to be explicitly specified", function () {
- controller.setState(true);
- expect(controller.isActive()).toBe(true);
- controller.setState(true);
- expect(controller.isActive()).toBe(true);
- controller.setState(false);
- expect(controller.isActive()).toBe(false);
- controller.setState(false);
- expect(controller.isActive()).toBe(false);
- });
-
- });
- }
-);
diff --git a/platform/commonUI/general/test/controllers/TreeNodeControllerSpec.js b/platform/commonUI/general/test/controllers/TreeNodeControllerSpec.js
deleted file mode 100644
index 2793a1799..000000000
--- a/platform/commonUI/general/test/controllers/TreeNodeControllerSpec.js
+++ /dev/null
@@ -1,209 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../../src/controllers/TreeNodeController"],
- function (TreeNodeController) {
-
- describe("The tree node controller", function () {
- var mockScope,
- mockTimeout,
- mockDomainObject,
- controller;
-
- function TestObject(id, context) {
- return {
- getId: function () {
- return id;
- },
- getCapability: function (key) {
- return key === 'context' ? context : undefined;
- }
- };
- }
-
- beforeEach(function () {
- mockScope = jasmine.createSpyObj("$scope", ["$watch", "$on", "$emit"]);
- mockTimeout = jasmine.createSpy("$timeout");
- mockDomainObject = jasmine.createSpyObj(
- "domainObject",
- ["getId", "getCapability", "getModel", "useCapability"]
- );
-
- controller = new TreeNodeController(mockScope, mockTimeout);
- });
-
- it("allows tracking of expansion state", function () {
- // The tree node tracks whether or not it has ever
- // been expanded in order to lazily load the expanded
- // portion of the tree.
- expect(controller.hasBeenExpanded()).toBeFalsy();
- controller.trackExpansion();
-
- // Expansion is tracked on a timeout, because too
- // much expansion can result in an unstable digest.
- expect(mockTimeout).toHaveBeenCalled();
- mockTimeout.calls.mostRecent().args[0]();
-
- expect(controller.hasBeenExpanded()).toBeTruthy();
- controller.trackExpansion();
- expect(controller.hasBeenExpanded()).toBeTruthy();
- });
-
- it("tracks whether or not the represented object is currently navigated-to", function () {
- // This is needed to highlight the current selection
- var mockContext = jasmine.createSpyObj(
- "context",
- ["getParent", "getPath", "getRoot"]
- ),
- obj = new TestObject("test-object", mockContext);
-
- mockContext.getPath.and.returnValue([obj]);
-
- // Verify precondition
- expect(controller.isSelected()).toBeFalsy();
-
- // Change the represented domain object
- mockScope.domainObject = obj;
-
- // Invoke the watch with the new selection
- mockScope.$watch.calls.all()[0].args[1](obj);
-
- expect(controller.isSelected()).toBeTruthy();
- });
-
- it("expands a node if it is on the navigation path", function () {
- var mockParentContext = jasmine.createSpyObj(
- "parentContext",
- ["getParent", "getPath", "getRoot"]
- ),
- mockChildContext = jasmine.createSpyObj(
- "childContext",
- ["getParent", "getPath", "getRoot"]
- ),
- parent = new TestObject("parent", mockParentContext),
- child = new TestObject("child", mockChildContext);
-
- mockChildContext.getParent.and.returnValue(parent);
- mockChildContext.getPath.and.returnValue([parent, child]);
- mockParentContext.getPath.and.returnValue([parent]);
-
- // Set up such that we are on, but not at the end of, a path
- mockScope.ngModel = { selectedObject: child };
- mockScope.domainObject = parent;
- mockScope.toggle = jasmine.createSpyObj("toggle", ["setState"]);
-
- // Invoke the watch with the new selection
- mockScope.$watch.calls.all()[0].args[1](child);
-
- // Expansion is tracked on a timeout, because too
- // much expansion can result in an unstable digest.
- // Trigger that timeout.
- expect(mockTimeout).toHaveBeenCalled();
- mockTimeout.calls.mostRecent().args[0]();
-
- expect(mockScope.toggle.setState).toHaveBeenCalledWith(true);
- expect(controller.hasBeenExpanded()).toBeTruthy();
- expect(controller.isSelected()).toBeFalsy();
-
- });
-
- it("does not expand a node if it is not on the navigation path", function () {
- var mockParentContext = jasmine.createSpyObj(
- "parentContext",
- ["getParent", "getPath", "getRoot"]
- ),
- mockChildContext = jasmine.createSpyObj(
- "childContext",
- ["getParent", "getPath", "getRoot"]
- ),
- parent = new TestObject("parent", mockParentContext),
- child = new TestObject("child", mockChildContext);
-
- mockChildContext.getParent.and.returnValue(parent);
- mockChildContext.getPath.and.returnValue([child, child]);
- mockParentContext.getPath.and.returnValue([parent]);
-
- // Set up such that we are on, but not at the end of, a path
- mockScope.ngModel = { selectedObject: child };
- mockScope.domainObject = parent;
- mockScope.toggle = jasmine.createSpyObj("toggle", ["setState"]);
-
- // Invoke the watch with the new selection
- mockScope.$watch.calls.all()[0].args[1](child);
-
- // Expansion is tracked on a timeout, because too
- // much expansion can result in an unstable digest.
- // We want to make sure no timeouts are pending here.
- expect(mockTimeout).not.toHaveBeenCalled();
- expect(controller.hasBeenExpanded()).toBeFalsy();
- expect(controller.isSelected()).toBeFalsy();
- });
-
- it("does not expand a node if no context is available", function () {
- var mockParentContext = jasmine.createSpyObj(
- "parentContext",
- ["getParent", "getPath", "getRoot"]
- ),
- mockChildContext = jasmine.createSpyObj(
- "childContext",
- ["getParent", "getPath", "getRoot"]
- ),
- parent = new TestObject("parent", mockParentContext),
- child = new TestObject("child", undefined);
-
- mockChildContext.getParent.and.returnValue(parent);
- mockChildContext.getPath.and.returnValue([parent, child]);
- mockParentContext.getPath.and.returnValue([parent]);
-
- // Set up such that we are on, but not at the end of, a path
- mockScope.ngModel = { selectedObject: child };
- mockScope.domainObject = parent;
- mockScope.toggle = jasmine.createSpyObj("toggle", ["setState"]);
-
- // Invoke the watch with the new selection
- mockScope.$watch.calls.all()[0].args[1](child);
-
- expect(mockScope.toggle.setState).not.toHaveBeenCalled();
- expect(controller.hasBeenExpanded()).toBeFalsy();
- expect(controller.isSelected()).toBeFalsy();
-
- });
-
- it("exposes selected objects in scope", function () {
- mockScope.domainObject = mockDomainObject;
- mockScope.ngModel = {};
- controller.select();
- expect(mockScope.ngModel.selectedObject)
- .toEqual(mockDomainObject);
- });
-
- it("invokes optional callbacks upon selection", function () {
- mockScope.parameters =
- { callback: jasmine.createSpy('callback') };
- controller.select();
- expect(mockScope.parameters.callback).toHaveBeenCalled();
- });
-
- });
- }
-);
diff --git a/platform/commonUI/general/test/controllers/ViewSwitcherControllerSpec.js b/platform/commonUI/general/test/controllers/ViewSwitcherControllerSpec.js
deleted file mode 100644
index a6b104068..000000000
--- a/platform/commonUI/general/test/controllers/ViewSwitcherControllerSpec.js
+++ /dev/null
@@ -1,155 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * MCTRepresentationSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/controllers/ViewSwitcherController"],
- function (ViewSwitcherController) {
-
- describe("The view switcher controller", function () {
- var mockScope,
- mockTimeout,
- controller;
-
- beforeEach(function () {
- mockScope = jasmine.createSpyObj("$scope", ["$watch"]);
- mockTimeout = jasmine.createSpy("$timeout");
- mockTimeout.and.callFake(function (cb) {
- cb();
- });
- mockScope.ngModel = {};
- controller = new ViewSwitcherController(mockScope, mockTimeout);
- });
-
- it("watches for changes in applicable views", function () {
- // The view capability is used by associated
- // representations, so "view" in scope should always
- // be the list of applicable views. The view switcher
- // controller should be watching this.
- expect(mockScope.$watch).toHaveBeenCalledWith(
- "view",
- jasmine.any(Function)
- );
- });
-
- it("maintains the current selection when views change", function () {
- var views = [
- {
- key: "a",
- name: "View A"
- },
- {
- key: "b",
- name: "View B"
- },
- {
- key: "c",
- name: "View C"
- },
- {
- key: "d",
- name: "View D"
- }
- ];
- mockScope.$watch.calls.mostRecent().args[1](views);
- mockScope.ngModel.selected = views[1];
-
- // Change the set of applicable views
- mockScope.$watch.calls.mostRecent().args[1]([
- {
- key: "a",
- name: "View A"
- },
- {
- key: "b",
- name: "View B"
- },
- {
- key: "x",
- name: "View X"
- }
- ]);
-
- // "b" is still in there, should remain selected
- expect(mockScope.ngModel.selected).toEqual(views[1]);
- });
-
- it("chooses a default if a selected view becomes inapplicable", function () {
- var views = [
- {
- key: "a",
- name: "View A"
- },
- {
- key: "b",
- name: "View B"
- },
- {
- key: "c",
- name: "View C"
- },
- {
- key: "d",
- name: "View D"
- }
- ];
- mockScope.$watch.calls.mostRecent().args[1](views);
- mockScope.ngModel.selected = views[1];
-
- // Change the set of applicable views
- mockScope.$watch.calls.mostRecent().args[1]([
- {
- key: "a",
- name: "View A"
- },
- {
- key: "c",
- name: "View C"
- },
- {
- key: "x",
- name: "View X"
- }
- ]);
-
- // "b" is still in there, should remain selected
- expect(mockScope.ngModel.selected).not.toEqual(views[1]);
- });
-
- // Use of a timeout avoids infinite digest problems when deeply
- // nesting switcher-driven views (e.g. in a layout.) See WTD-689
- it("updates initial selection on a timeout", function () {
- // Verify precondition
- expect(mockTimeout).not.toHaveBeenCalled();
-
- // Invoke the watch for set of views
- mockScope.$watch.calls.mostRecent().args[1]([]);
-
- // Should have run on a timeout
- expect(mockTimeout).toHaveBeenCalled();
- });
-
- });
- }
-);
diff --git a/platform/commonUI/general/test/directives/MCTClickElsewhereSpec.js b/platform/commonUI/general/test/directives/MCTClickElsewhereSpec.js
deleted file mode 100644
index 3c1c7bdad..000000000
--- a/platform/commonUI/general/test/directives/MCTClickElsewhereSpec.js
+++ /dev/null
@@ -1,129 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../../src/directives/MCTClickElsewhere"],
- function (MCTClickElsewhere) {
-
- var JQLITE_METHODS = ["on", "off", "find", "parent"];
-
- describe("The mct-click-elsewhere directive", function () {
- var mockDocument,
- mockScope,
- mockElement,
- testAttrs,
- mockBody,
- mockPlainEl,
- testRect,
- mctClickElsewhere;
-
- function testEvent(x, y) {
- return {
- clientX: x,
- clientY: y,
- preventDefault: jasmine.createSpy("preventDefault")
- };
- }
-
- beforeEach(function () {
- mockDocument =
- jasmine.createSpyObj("$document", JQLITE_METHODS);
- mockScope =
- jasmine.createSpyObj("$scope", ["$eval", "$apply", "$on"]);
- mockElement =
- jasmine.createSpyObj("element", JQLITE_METHODS);
- mockBody =
- jasmine.createSpyObj("body", JQLITE_METHODS);
- mockPlainEl =
- jasmine.createSpyObj("htmlElement", ["getBoundingClientRect"]);
-
- testAttrs = {
- mctClickElsewhere: "some Angular expression"
- };
- testRect = {
- left: 20,
- top: 42,
- width: 60,
- height: 75
- };
- mockElement[0] = mockPlainEl;
- mockPlainEl.getBoundingClientRect.and.returnValue(testRect);
-
- mockDocument.find.and.returnValue(mockBody);
-
- mctClickElsewhere = new MCTClickElsewhere(mockDocument);
- mctClickElsewhere.link(mockScope, mockElement, testAttrs);
- });
-
- it("is valid as an attribute", function () {
- expect(mctClickElsewhere.restrict).toEqual("A");
- });
-
- it("detaches listeners when destroyed", function () {
- expect(mockBody.off).not.toHaveBeenCalled();
- mockScope.$on.calls.all().forEach(function (call) {
- if (call.args[0] === '$destroy') {
- call.args[1]();
- }
- });
- expect(mockBody.off).toHaveBeenCalled();
- expect(mockBody.off.calls.mostRecent().args)
- .toEqual(mockBody.on.calls.mostRecent().args);
- });
-
- it("listens for mousedown on the document's body", function () {
- expect(mockBody.on)
- .toHaveBeenCalledWith('mousedown', jasmine.any(Function));
- });
-
- describe("when a click occurs outside the element's bounds", function () {
- beforeEach(function () {
- mockBody.on.calls.mostRecent().args[1](testEvent(
- testRect.left + testRect.width + 10,
- testRect.top + testRect.height + 10
- ));
- });
-
- it("triggers an evaluation of its related Angular expression", function () {
- expect(mockScope.$apply).toHaveBeenCalled();
- mockScope.$apply.calls.mostRecent().args[0]();
- expect(mockScope.$eval)
- .toHaveBeenCalledWith(testAttrs.mctClickElsewhere);
- });
- });
-
- describe("when a click occurs within the element's bounds", function () {
- beforeEach(function () {
- mockBody.on.calls.mostRecent().args[1](testEvent(
- testRect.left + testRect.width / 2,
- testRect.top + testRect.height / 2
- ));
- });
-
- it("triggers no evaluation", function () {
- expect(mockScope.$eval).not.toHaveBeenCalled();
- });
- });
-
- });
- }
-);
diff --git a/platform/commonUI/general/test/directives/MCTContainerSpec.js b/platform/commonUI/general/test/directives/MCTContainerSpec.js
deleted file mode 100644
index 4ba6b016a..000000000
--- a/platform/commonUI/general/test/directives/MCTContainerSpec.js
+++ /dev/null
@@ -1,94 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../../src/directives/MCTContainer"],
- function (MCTContainer) {
-
- describe("The mct-container directive", function () {
- var testContainers = [
- {
- bundle: {
- path: "a",
- resources: "b"
- },
- template: "<div>foo</div>",
- key: "abc"
- },
- {
- bundle: {
- path: "x",
- resources: "y"
- },
- template: "<span>bar</span>",
- key: "xyz",
- attributes: ["someAttr", "someOtherAttr"]
- }
- ],
- mctContainer;
-
- beforeEach(function () {
- mctContainer = new MCTContainer(testContainers);
- });
-
- it("is applicable to elements", function () {
- expect(mctContainer.restrict).toEqual("E");
- });
-
- it("creates a new (non-isolate) scope", function () {
- expect(mctContainer.scope).toBe(true);
- });
-
- it("chooses a template based on key", function () {
- expect(mctContainer.template(
- undefined,
- { key: "abc" }
- )).toEqual(testContainers[0].template);
-
- expect(mctContainer.template(
- undefined,
- { key: "xyz" }
- )).toEqual(testContainers[1].template);
- });
-
- it("copies attributes needed by the container", function () {
- var scope = {};
-
- mctContainer.link(
- scope,
- undefined,
- {
- key: "xyz",
- someAttr: "some value",
- someOtherAttr: "some other value",
- someExtraAttr: "should not be present"
- }
- );
-
- expect(scope.container.someAttr).toEqual("some value");
- expect(scope.container.someOtherAttr).toEqual("some other value");
- expect(scope.container.someExtraAttr).toBeUndefined();
- });
-
- });
- }
-);
diff --git a/platform/commonUI/general/test/directives/MCTDragSpec.js b/platform/commonUI/general/test/directives/MCTDragSpec.js
deleted file mode 100644
index 2691691c6..000000000
--- a/platform/commonUI/general/test/directives/MCTDragSpec.js
+++ /dev/null
@@ -1,303 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../../src/directives/MCTDrag"],
- function (MCTDrag) {
-
- var JQLITE_METHODS = ["on", "off", "find"];
-
- describe("The mct-drag directive in Mobile", function () {
- var mockDocument,
- mockAgentService,
- mockScope,
- mockElement,
- testAttrs,
- mockBody,
- mctDrag;
-
- function testEvent(x, y) {
- return {
- pageX: x,
- pageY: y,
- preventDefault: jasmine.createSpy("preventDefault")
- };
- }
-
- beforeEach(function () {
- mockDocument =
- jasmine.createSpyObj("$document", JQLITE_METHODS);
- mockAgentService =
- jasmine.createSpyObj("agentService", ["isMobile"]);
- mockScope =
- jasmine.createSpyObj("$scope", ["$eval", "$apply"]);
- mockElement =
- jasmine.createSpyObj("element", JQLITE_METHODS);
- mockBody =
- jasmine.createSpyObj("body", JQLITE_METHODS);
-
- testAttrs = {
- mctDragDown: "starting a drag",
- mctDrag: "continuing a drag",
- mctDragUp: "ending a drag"
- };
-
- mockDocument.find.and.returnValue(mockBody);
- mockAgentService.isMobile.and.returnValue(true);
-
- mctDrag = new MCTDrag(mockDocument, mockAgentService);
- mctDrag.link(mockScope, mockElement, testAttrs);
- });
-
- it("is valid as an attribute", function () {
- expect(mctDrag.restrict).toEqual("A");
- });
-
- it("listens for touchstart on its element", function () {
- expect(mockElement.on).toHaveBeenCalledWith(
- "touchstart",
- jasmine.any(Function)
- );
-
- // Verify no interactions with body as well
- expect(mockBody.on).not.toHaveBeenCalled();
- });
-
- it("invokes mctDragDown when dragging begins", function () {
- var event = testEvent(42, 60);
- mockElement.on.calls.mostRecent().args[1](event);
- expect(mockScope.$eval).toHaveBeenCalledWith(
- testAttrs.mctDragDown,
- {
- delta: [0, 0],
- $event: event
- }
- );
- });
-
- it("listens for touchmove after dragging begins", function () {
- mockElement.on.calls.mostRecent().args[1](testEvent(42, 60));
- expect(mockBody.on).toHaveBeenCalledWith(
- "touchmove",
- jasmine.any(Function)
- );
- expect(mockBody.on).toHaveBeenCalledWith(
- "touchend",
- jasmine.any(Function)
- );
- });
-
- it("invokes mctDrag expression during drag", function () {
- var event;
-
- mockElement.on.calls.mostRecent().args[1](testEvent(42, 60));
-
- // Find and invoke the touchmove listener
- mockBody.on.calls.all().forEach(function (call) {
- if (call.args[0] === 'touchmove') {
- call.args[1](event = testEvent(52, 200));
- }
- });
-
- // Should have passed that delta to mct-drag expression
- expect(mockScope.$eval).toHaveBeenCalledWith(
- testAttrs.mctDrag,
- {
- delta: [10, 140],
- $event: event
- }
- );
- });
-
- it("invokes mctDragUp expression after drag", function () {
- var event;
-
- mockElement.on.calls.mostRecent().args[1](testEvent(42, 60));
-
- // Find and invoke the touchmove listener
- mockBody.on.calls.all().forEach(function (call) {
- if (call.args[0] === 'touchmove') {
- call.args[1](testEvent(52, 200));
- }
- });
- // Find and invoke the touchmove listener
- mockBody.on.calls.all().forEach(function (call) {
- if (call.args[0] === 'touchend') {
- call.args[1](event = testEvent(40, 71));
- }
- });
-
- // Should have passed that delta to mct-drag-up expression
- // and that delta should have been relative to the
- // initial position
- expect(mockScope.$eval).toHaveBeenCalledWith(
- testAttrs.mctDragUp,
- {
- delta: [-2, 11],
- $event: event
- }
- );
-
- // Should also have unregistered listeners
- expect(mockBody.off).toHaveBeenCalled();
- });
-
- });
-
- describe("The mct-drag directive in Desktop", function () {
- var mockDocument,
- mockAgentService,
- mockScope,
- mockElement,
- testAttrs,
- mockBody,
- mctDrag;
-
- function testEvent(x, y) {
- return {
- pageX: x,
- pageY: y,
- preventDefault: jasmine.createSpy("preventDefault")
- };
- }
-
- beforeEach(function () {
- mockDocument =
- jasmine.createSpyObj("$document", JQLITE_METHODS);
- mockAgentService =
- jasmine.createSpyObj("agentService", ["isMobile"]);
- mockScope =
- jasmine.createSpyObj("$scope", ["$eval", "$apply"]);
- mockElement =
- jasmine.createSpyObj("element", JQLITE_METHODS);
- mockBody =
- jasmine.createSpyObj("body", JQLITE_METHODS);
-
- testAttrs = {
- mctDragDown: "starting a drag",
- mctDrag: "continuing a drag",
- mctDragUp: "ending a drag"
- };
-
- mockDocument.find.and.returnValue(mockBody);
- mockAgentService.isMobile.and.returnValue(false);
-
- mctDrag = new MCTDrag(mockDocument, mockAgentService);
- mctDrag.link(mockScope, mockElement, testAttrs);
- });
-
- it("is valid as an attribute", function () {
- expect(mctDrag.restrict).toEqual("A");
- });
-
- it("listens for mousedown on its element", function () {
- expect(mockElement.on).toHaveBeenCalledWith(
- "mousedown",
- jasmine.any(Function)
- );
-
- // Verify no interactions with body as well
- expect(mockBody.on).not.toHaveBeenCalled();
- });
-
- it("invokes mctDragDown when dragging begins", function () {
- var event = testEvent(42, 60);
- mockElement.on.calls.mostRecent().args[1](event);
- expect(mockScope.$eval).toHaveBeenCalledWith(
- testAttrs.mctDragDown,
- {
- delta: [0, 0],
- $event: event
- }
- );
- });
-
- it("listens for mousemove after dragging begins", function () {
- mockElement.on.calls.mostRecent().args[1](testEvent(42, 60));
- expect(mockBody.on).toHaveBeenCalledWith(
- "mousemove",
- jasmine.any(Function)
- );
- expect(mockBody.on).toHaveBeenCalledWith(
- "mouseup",
- jasmine.any(Function)
- );
- });
-
- it("invokes mctDrag expression during drag", function () {
- var event;
-
- mockElement.on.calls.mostRecent().args[1](testEvent(42, 60));
-
- // Find and invoke the mousemove listener
- mockBody.on.calls.all().forEach(function (call) {
- if (call.args[0] === 'mousemove') {
- call.args[1](event = testEvent(52, 200));
- }
- });
-
- // Should have passed that delta to mct-drag expression
- expect(mockScope.$eval).toHaveBeenCalledWith(
- testAttrs.mctDrag,
- {
- delta: [10, 140],
- $event: event
- }
- );
- });
-
- it("invokes mctDragUp expression after drag", function () {
- var event;
-
- mockElement.on.calls.mostRecent().args[1](testEvent(42, 60));
-
- // Find and invoke the mousemove listener
- mockBody.on.calls.all().forEach(function (call) {
- if (call.args[0] === 'mousemove') {
- call.args[1](testEvent(52, 200));
- }
- });
- // Find and invoke the mousemove listener
- mockBody.on.calls.all().forEach(function (call) {
- if (call.args[0] === 'mouseup') {
- call.args[1](event = testEvent(40, 71));
- }
- });
-
- // Should have passed that delta to mct-drag-up expression
- // and that delta should have been relative to the
- // initial position
- expect(mockScope.$eval).toHaveBeenCalledWith(
- testAttrs.mctDragUp,
- {
- delta: [-2, 11],
- $event: event
- }
- );
-
- // Should also have unregistered listeners
- expect(mockBody.off).toHaveBeenCalled();
- });
-
- });
- }
-);
diff --git a/platform/commonUI/general/test/directives/MCTPopupSpec.js b/platform/commonUI/general/test/directives/MCTPopupSpec.js
deleted file mode 100644
index d2a50e53d..000000000
--- a/platform/commonUI/general/test/directives/MCTPopupSpec.js
+++ /dev/null
@@ -1,132 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../../src/directives/MCTPopup"],
- function (MCTPopup) {
-
- var JQLITE_METHODS = [
- "on",
- "off",
- "find",
- "parent",
- "css",
- "addClass",
- "append"
- ];
-
- describe("The mct-popup directive", function () {
- var mockCompile,
- mockPopupService,
- mockPopup,
- mockScope,
- mockElement,
- testAttrs,
- mockTransclude,
- mockParentEl,
- mockNewElement,
- testRect,
- mctPopup;
-
- beforeEach(function () {
- mockCompile =
- jasmine.createSpy("$compile");
- mockPopupService =
- jasmine.createSpyObj("popupService", ["display"]);
- mockPopup =
- jasmine.createSpyObj("popup", ["dismiss"]);
- mockScope =
- jasmine.createSpyObj("$scope", ["$eval", "$apply", "$on"]);
- mockElement =
- jasmine.createSpyObj("element", JQLITE_METHODS);
- mockTransclude =
- jasmine.createSpy("transclude");
- mockParentEl =
- jasmine.createSpyObj("parent", ["getBoundingClientRect"]);
- mockNewElement =
- jasmine.createSpyObj("newElement", JQLITE_METHODS);
-
- testAttrs = {
- mctClickElsewhere: "some Angular expression"
- };
- testRect = {
- left: 20,
- top: 42,
- width: 60,
- height: 75
- };
-
- mockCompile.and.callFake(function () {
- var mockFn = jasmine.createSpy();
- mockFn.and.returnValue(mockNewElement);
-
- return mockFn;
- });
- mockElement.parent.and.returnValue([mockParentEl]);
- mockParentEl.getBoundingClientRect.and.returnValue(testRect);
- mockPopupService.display.and.returnValue(mockPopup);
-
- mctPopup = new MCTPopup(mockCompile, mockPopupService);
-
- mctPopup.link(
- mockScope,
- mockElement,
- testAttrs,
- null,
- mockTransclude
- );
- });
-
- it("is valid as an element", function () {
- expect(mctPopup.restrict).toEqual("E");
- });
-
- describe("creates an element which", function () {
- it("displays as a popup", function () {
- expect(mockPopupService.display).toHaveBeenCalledWith(
- mockNewElement,
- [testRect.left, testRect.top]
- );
- });
-
- it("displays transcluded content", function () {
- var mockClone =
- jasmine.createSpyObj('clone', JQLITE_METHODS);
- mockTransclude.calls.mostRecent().args[0](mockClone);
- expect(mockNewElement.append)
- .toHaveBeenCalledWith(mockClone);
- });
-
- it("is removed when its containing scope is destroyed", function () {
- expect(mockPopup.dismiss).not.toHaveBeenCalled();
- mockScope.$on.calls.all().forEach(function (call) {
- if (call.args[0] === '$destroy') {
- call.args[1]();
- }
- });
- expect(mockPopup.dismiss).toHaveBeenCalled();
- });
- });
-
- });
- }
-);
diff --git a/platform/commonUI/general/test/directives/MCTResizeSpec.js b/platform/commonUI/general/test/directives/MCTResizeSpec.js
deleted file mode 100644
index 492736049..000000000
--- a/platform/commonUI/general/test/directives/MCTResizeSpec.js
+++ /dev/null
@@ -1,166 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../../src/directives/MCTResize"],
- function (MCTResize) {
-
- describe("The mct-resize directive", function () {
- var mockTimeout,
- mockScope,
- testElement,
- testAttrs,
- mctResize;
-
- beforeEach(function () {
- mockTimeout = jasmine.createSpy("$timeout");
- mockScope = jasmine.createSpyObj("$scope", ["$eval", "$on", "$apply"]);
-
- testElement = {
- offsetWidth: 100,
- offsetHeight: 200
- };
- testAttrs = { mctResize: "some-expr" };
-
- mctResize = new MCTResize(mockTimeout);
- });
-
- it("is applicable as an attribute only", function () {
- expect(mctResize.restrict).toEqual("A");
- });
-
- it("starts tracking size changes upon link", function () {
- expect(mockTimeout).not.toHaveBeenCalled();
- mctResize.link(mockScope, [testElement], testAttrs);
- expect(mockTimeout).toHaveBeenCalledWith(
- jasmine.any(Function),
- jasmine.any(Number),
- false
- );
- expect(mockScope.$eval).toHaveBeenCalledWith(
- testAttrs.mctResize,
- {
- bounds: {
- width: 100,
- height: 200
- }
- }
- );
- });
-
- it("reports size changes on a timeout", function () {
- mctResize.link(mockScope, [testElement], testAttrs);
-
- // Change the element's apparent size
- testElement.offsetWidth = 300;
- testElement.offsetHeight = 350;
-
- // Shouldn't know about this yet...
- expect(mockScope.$eval).not.toHaveBeenCalledWith(
- testAttrs.mctResize,
- {
- bounds: {
- width: 300,
- height: 350
- }
- }
- );
-
- // Fire the timeout
- mockTimeout.calls.mostRecent().args[0]();
-
- // Should have triggered an evaluation of mctResize
- // with the new width & height
- expect(mockScope.$eval).toHaveBeenCalledWith(
- testAttrs.mctResize,
- {
- bounds: {
- width: 300,
- height: 350
- }
- }
- );
- });
-
- it("stops size checking for size changes after destroy", function () {
- mctResize.link(mockScope, [testElement], testAttrs);
-
- // First, make sure there's a $destroy observer
- expect(mockScope.$on)
- .toHaveBeenCalledWith("$destroy", jasmine.any(Function));
-
- // Should have scheduled the first timeout
- expect(mockTimeout.calls.count()).toEqual(1);
-
- // Fire the timeout
- mockTimeout.calls.mostRecent().args[0]();
-
- // Should have scheduled another timeout
- expect(mockTimeout.calls.count()).toEqual(2);
-
- // Broadcast a destroy event
- mockScope.$on.calls.mostRecent().args[1]();
-
- testElement.offsetWidth = 300;
- testElement.offsetHeight = 350;
- mockScope.$eval.calls.reset();
-
- // Fire the timeout
- mockTimeout.calls.mostRecent().args[0]();
-
- // Should NOT have scheduled another timeout
- expect(mockTimeout.calls.count()).toEqual(2);
- expect(mockScope.$eval).not.toHaveBeenCalled();
- });
-
- it("triggers a digest cycle when size changes", function () {
- var applyCount;
- mctResize.link(mockScope, [testElement], testAttrs);
- applyCount = mockScope.$apply.calls.count();
-
- // Change the element's apparent size
- testElement.offsetWidth = 300;
- testElement.offsetHeight = 350;
-
- // Fire the timeout
- mockTimeout.calls.mostRecent().args[0]();
-
- // No more apply calls
- expect(mockScope.$apply.calls.count())
- .toBeGreaterThan(applyCount);
- });
-
- it("does not trigger a digest cycle when size does not change", function () {
- var applyCount;
- mctResize.link(mockScope, [testElement], testAttrs);
- applyCount = mockScope.$apply.calls.count();
-
- // Fire the timeout
- mockTimeout.calls.mostRecent().args[0]();
-
- // No more apply calls
- expect(mockScope.$apply.calls.count()).toEqual(applyCount);
- });
-
- });
- }
-);
diff --git a/platform/commonUI/general/test/directives/MCTScrollSpec.js b/platform/commonUI/general/test/directives/MCTScrollSpec.js
deleted file mode 100644
index cd0f3d165..000000000
--- a/platform/commonUI/general/test/directives/MCTScrollSpec.js
+++ /dev/null
@@ -1,114 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-define(
- ['../../src/directives/MCTScroll'],
- function (MCTScroll) {
-
- var EVENT_PROPERTY = "testProperty",
- ATTRIBUTE = "testAttribute",
- EXPRESSION = "some.expression";
-
- // MCTScroll is the commonality between mct-scroll-x and
- // mct-scroll-y; it gets the event property to watch and
- // the attribute which contains the associated assignable
- // expression.
- describe("An mct-scroll-* directive", function () {
- var mockParse,
- mockParsed,
- mockScope,
- mockElement,
- testAttrs,
- mctScroll;
-
- beforeEach(function () {
- mockParse = jasmine.createSpy('$parse');
- mockParsed = jasmine.createSpy('parsed');
- mockParsed.assign = jasmine.createSpy('assign');
-
- mockScope = jasmine.createSpyObj('$scope', ['$watch', '$apply']);
- mockElement = [{ testProperty: 42 }];
- mockElement.on = jasmine.createSpy('on');
-
- mockParse.and.returnValue(mockParsed);
-
- testAttrs = {};
- testAttrs[ATTRIBUTE] = EXPRESSION;
-
- mctScroll = new MCTScroll(
- mockParse,
- EVENT_PROPERTY,
- ATTRIBUTE
- );
- mctScroll.link(mockScope, mockElement, testAttrs);
- });
-
- it("is available for attributes", function () {
- expect(mctScroll.restrict).toEqual('A');
- });
-
- it("does not create an isolate scope", function () {
- expect(mctScroll.scope).toBeUndefined();
- });
-
- it("watches for changes in observed expression", function () {
- expect(mockScope.$watch).toHaveBeenCalledWith(
- EXPRESSION,
- jasmine.any(Function)
- );
- // Should have been only watch (other tests need this to be true)
- expect(mockScope.$watch.calls.count()).toEqual(1);
- });
-
- it("listens for scroll events", function () {
- expect(mockElement.on).toHaveBeenCalledWith(
- 'scroll',
- jasmine.any(Function)
- );
- // Should have been only listener (other tests need this to be true)
- expect(mockElement.on.calls.count()).toEqual(1);
- });
-
- it("publishes initial scroll state", function () {
- expect(mockParse).toHaveBeenCalledWith(EXPRESSION);
- expect(mockParsed.assign).toHaveBeenCalledWith(mockScope, 42);
- });
-
- it("updates scroll state when scope changes", function () {
- mockScope.$watch.calls.mostRecent().args[1](64);
- expect(mockElement[0].testProperty).toEqual(64);
- });
-
- it("updates scope when scroll state changes", function () {
- mockElement[0].testProperty = 12321;
- mockElement.on.calls.mostRecent().args[1]({ target: mockElement[0] });
- expect(mockParsed.assign).toHaveBeenCalledWith(mockScope, 12321);
- expect(mockScope.$apply).toHaveBeenCalledWith(EXPRESSION);
- });
-
- // This would trigger an infinite digest exception
- it("does not call $apply during construction", function () {
- expect(mockScope.$apply).not.toHaveBeenCalled();
- });
-
- });
- }
-);
diff --git a/platform/commonUI/general/test/directives/MCTSplitPaneSpec.js b/platform/commonUI/general/test/directives/MCTSplitPaneSpec.js
deleted file mode 100644
index a72539ede..000000000
--- a/platform/commonUI/general/test/directives/MCTSplitPaneSpec.js
+++ /dev/null
@@ -1,233 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../../src/directives/MCTSplitPane"],
- function (MCTSplitPane) {
-
- var JQLITE_METHODS = [
- 'on',
- 'addClass',
- 'children',
- 'eq',
- 'toggleClass',
- 'css'
- ];
-
- describe("The mct-split-pane directive", function () {
- var mockParse,
- mockLog,
- mockInterval,
- mockParsed,
- mctSplitPane,
- mockWindow = {};
-
- beforeEach(function () {
- mockParse = jasmine.createSpy('$parse');
- mockLog =
- jasmine.createSpyObj('$log', ['warn', 'info', 'debug']);
- mockInterval = jasmine.createSpy('$interval');
- mockInterval.cancel = jasmine.createSpy('mockCancel');
- mockParsed = jasmine.createSpy('parsed');
- mockParsed.assign = jasmine.createSpy('assign');
- mockParse.and.returnValue(mockParsed);
-
- mockWindow.localStorage = {
- store: {},
- setItem: function (key, value) {
- this.store[key] = value;
- },
- getItem: function (key) {
- return this.store[key];
- }
- };
-
- mctSplitPane = new MCTSplitPane(
- mockParse,
- mockLog,
- mockInterval,
- mockWindow
- );
- });
-
- it("is only applicable as an element", function () {
- expect(mctSplitPane.restrict).toEqual("E");
- });
-
- describe("when its controller is applied", function () {
- var mockScope,
- mockElement,
- testAttrs,
- mockChildren,
- mockFirstPane,
- mockSplitter,
- mockSecondPane,
- controller;
-
- function fireOn(eventType) {
- mockScope.$on.calls.all().forEach(function (call) {
- if (call.args[0] === eventType) {
- call.args[1]();
- }
- });
- }
-
- beforeEach(function () {
- mockScope =
- jasmine.createSpyObj('$scope', ['$apply', '$watch', '$on']);
- mockElement =
- jasmine.createSpyObj('element', JQLITE_METHODS);
- testAttrs = {alias: 'rightSide'};
- mockChildren =
- jasmine.createSpyObj('children', JQLITE_METHODS);
- mockFirstPane =
- jasmine.createSpyObj('firstPane', JQLITE_METHODS);
- mockSplitter =
- jasmine.createSpyObj('splitter', JQLITE_METHODS);
- mockSecondPane =
- jasmine.createSpyObj('secondPane', JQLITE_METHODS);
-
- mockElement.children.and.returnValue(mockChildren);
- mockElement[0] = {
- offsetWidth: 12321,
- offsetHeight: 45654
- };
- mockChildren.eq.and.callFake(function (i) {
- return [mockFirstPane, mockSplitter, mockSecondPane][i];
- });
- mockFirstPane[0] = {
- offsetWidth: 123,
- offsetHeight: 456
- };
- mockSplitter[0] = {
- nodeName: 'mct-splitter',
- offsetWidth: 10,
- offsetHeight: 456
- };
- mockSecondPane[0] = {
- offsetWidth: 10,
- offsetHeight: 456
- };
-
- mockChildren[0] = mockFirstPane[0];
- mockChildren[1] = mockSplitter[0];
- mockChildren[3] = mockSecondPane[0];
- mockChildren.length = 3;
-
- controller = mctSplitPane.controller[3](
- mockScope,
- mockElement,
- testAttrs
- );
- });
-
- it("sets an interval which does not trigger digests", function () {
- expect(mockInterval.calls.mostRecent().args[3]).toBe(false);
- });
-
- it("exposes its splitter's initial position", function () {
- expect(controller.position()).toEqual(
- mockFirstPane[0].offsetWidth
- );
- });
-
- it("exposes the current anchoring mode", function () {
- expect(controller.anchor()).toEqual({
- edge: 'left',
- opposite: 'right',
- dimension: 'width',
- orientation: 'vertical'
- });
- });
-
- it("applies resizing class to children when resizing", function () {
- controller.startResizing();
- expect(mockChildren.toggleClass).toHaveBeenCalledWith('resizing');
- });
-
- it("removes resizing class from children when resizing action ends", function () {
- controller.endResizing(0);
- expect(mockChildren.toggleClass).toHaveBeenCalledWith('resizing');
- });
-
- it("allows positions to be set", function () {
- var testValue = mockChildren[0].offsetWidth + 50;
- controller.position(testValue);
- expect(mockFirstPane.css).toHaveBeenCalledWith(
- 'width',
- (testValue) + 'px'
- );
- });
-
- it("issues no warnings under nominal usage", function () {
- expect(mockLog.warn).not.toHaveBeenCalled();
- });
-
- it("warns if no mct-splitter is present", function () {
- mockSplitter[0].nodeName = "not-mct-splitter";
- controller = mctSplitPane.controller[3](
- mockScope,
- mockElement,
- testAttrs
- );
- expect(mockLog.warn).toHaveBeenCalled();
- });
-
- it("warns if an unknown anchor key is given", function () {
- testAttrs.anchor = "middle";
- controller = mctSplitPane.controller[3](
- mockScope,
- mockElement,
- testAttrs
- );
- expect(mockLog.warn).toHaveBeenCalled();
- });
-
- it("updates positions on a timer", function () {
- mockFirstPane[0].offsetWidth += 100;
- // Should not reflect the change yet
- expect(controller.position()).not.toEqual(
- mockFirstPane[0].offsetWidth
- );
- mockInterval.calls.mostRecent().args[0]();
- expect(controller.position()).toEqual(
- mockFirstPane[0].offsetWidth
- );
- });
-
- it("cancels the active interval when scope is destroyed", function () {
- expect(mockInterval.cancel).not.toHaveBeenCalled();
- fireOn('$destroy');
- expect(mockInterval.cancel).toHaveBeenCalled();
- });
-
- it("saves user preference to localStorage when user is done resizing", function () {
- controller.endResizing(100);
- expect(Number(mockWindow.localStorage.getItem('mctSplitPane-rightSide'))).toEqual(100);
- });
-
- });
-
- });
-
- }
-);
diff --git a/platform/commonUI/general/test/directives/MCTSplitterSpec.js b/platform/commonUI/general/test/directives/MCTSplitterSpec.js
deleted file mode 100644
index 73ab9a2e2..000000000
--- a/platform/commonUI/general/test/directives/MCTSplitterSpec.js
+++ /dev/null
@@ -1,110 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../../src/directives/MCTSplitter"],
- function (MCTSplitter) {
-
- describe("The mct-splitter directive", function () {
- var mctSplitter;
-
- beforeEach(function () {
- mctSplitter = new MCTSplitter();
- });
-
- it("is applicable to elements", function () {
- expect(mctSplitter.restrict).toEqual("E");
- });
-
- it("depends on the mct-split-pane controller", function () {
- expect(mctSplitter.require).toEqual("^mctSplitPane");
- });
-
- describe("when linked", function () {
- var mockScope,
- mockElement,
- testAttrs,
- mockSplitPane;
-
- beforeEach(function () {
- mockScope = jasmine.createSpyObj(
- '$scope',
- ['$on', '$watch']
- );
- mockElement = jasmine.createSpyObj(
- 'element',
- ['addClass']
- );
- testAttrs = {};
- mockSplitPane = jasmine.createSpyObj(
- 'mctSplitPane',
- ['position', 'startResizing', 'endResizing', 'anchor']
- );
-
- mctSplitter.link(
- mockScope,
- mockElement,
- testAttrs,
- mockSplitPane
- );
- });
-
- it("adds a splitter class", function () {
- expect(mockElement.addClass)
- .toHaveBeenCalledWith('splitter');
- });
-
- describe("and then manipulated", function () {
- var testPosition;
-
- beforeEach(function () {
- testPosition = 12321;
- mockSplitPane.position.and.returnValue(testPosition);
- mockSplitPane.anchor.and.returnValue({
- orientation: 'vertical',
- reversed: false
- });
- mockScope.splitter.startMove();
- });
-
- it("tell's the splitter when it is resizing", function () {
- expect(mockSplitPane.startResizing)
- .toHaveBeenCalled();
- });
-
- it("repositions during drag", function () {
- mockScope.splitter.move([10, 0]);
- expect(mockSplitPane.position)
- .toHaveBeenCalledWith(testPosition + 10);
- });
-
- it("tell's the splitter when it is done resizing", function () {
- mockScope.splitter.move([10, 0]);
- mockScope.splitter.endMove();
- expect(mockSplitPane.endResizing).toHaveBeenCalledWith(testPosition + 10);
- });
-
- });
- });
- });
- }
-);
diff --git a/platform/commonUI/general/test/directives/MCTTreeSpec.js b/platform/commonUI/general/test/directives/MCTTreeSpec.js
deleted file mode 100644
index 7b7b71b80..000000000
--- a/platform/commonUI/general/test/directives/MCTTreeSpec.js
+++ /dev/null
@@ -1,146 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
- '../../src/directives/MCTTree',
- '../../src/ui/TreeView'
-], function (MCTTree, TreeView) {
- describe("The mct-tree directive", function () {
- var mockParse,
- mockGestureService,
- mockExpr,
- mctTree;
-
- function makeMockDomainObject(id) {
- var mockDomainObject = jasmine.createSpyObj('domainObject-' + id, [
- 'getId',
- 'getModel',
- 'getCapability',
- 'hasCapability'
- ]);
- mockDomainObject.getId.and.returnValue(id);
- mockDomainObject.getModel.and.returnValue({});
-
- return mockDomainObject;
- }
-
- beforeEach(function () {
- mockGestureService = jasmine.createSpyObj(
- 'gestureService',
- ['attachGestures']
- );
- mockParse = jasmine.createSpy('$parse');
- mockExpr = jasmine.createSpy('expr');
- mockExpr.assign = jasmine.createSpy('assign');
- mockParse.and.returnValue(mockExpr);
- spyOn(TreeView.prototype, 'observe').and.callThrough();
-
- mctTree = new MCTTree(mockParse, mockGestureService);
- });
-
- it("is applicable as an element", function () {
- expect(mctTree.restrict).toEqual("E");
- });
-
- it("two-way binds", function () {
- expect(mctTree.scope).toEqual({
- rootObject: "=",
- selectedObject: "=",
- allowSelection: "=?",
- onSelection: "=?"
- });
- });
-
- describe("link", function () {
- var mockScope,
- mockElement,
- testAttrs;
-
- beforeEach(function () {
- mockScope =
- jasmine.createSpyObj('$scope', ['$watch', '$on', '$apply']);
- mockElement = jasmine.createSpyObj('element', ['append']);
- testAttrs = { mctModel: "some-expression" };
- mockScope.$parent =
- jasmine.createSpyObj('$scope', ['$watch', '$on']);
- mctTree.link(mockScope, mockElement, testAttrs);
- });
-
- it("populates the mct-tree element", function () {
- expect(mockElement.append).toHaveBeenCalled();
- });
-
- it("watches for selected-object expression in the parent", function () {
- expect(mockScope.$watch).toHaveBeenCalledWith(
- "selectedObject",
- jasmine.any(Function)
- );
- });
-
- it("watches for changes to root-object", function () {
- expect(mockScope.$watch).toHaveBeenCalledWith(
- "rootObject",
- jasmine.any(Function)
- );
- });
-
- it("listens for the $destroy event", function () {
- expect(mockScope.$on).toHaveBeenCalledWith(
- "$destroy",
- jasmine.any(Function)
- );
- });
-
- it("watches for changes in tree view", function () {
-
- });
-
- // https://github.com/nasa/openmct/issues/1114
- it("does not trigger $apply during $watches", function () {
- mockScope.mctObject = makeMockDomainObject('root');
- mockScope.mctMode = makeMockDomainObject('selection');
- mockScope.$watch.calls.all().forEach(function (call) {
- call.args[1](mockScope[call.args[0]]);
- });
- expect(mockScope.$apply).not.toHaveBeenCalled();
- });
- it("does trigger $apply from tree manipulation", function () {
- if (/PhantomJS/g.test(window.navigator.userAgent)) {
- console.log('Unable to run test in PhantomJS due to lack of support for event constructors');
-
- return;
- }
-
- // White-boxy; we know this is the setter for the tree's value
- var treeValueFn = TreeView.prototype.observe.calls.all()[0].args[0];
-
- mockScope.mctObject = makeMockDomainObject('root');
- mockScope.mctMode = makeMockDomainObject('selection');
-
- treeValueFn(makeMockDomainObject('other'), new MouseEvent("click"));
-
- expect(mockScope.$apply).toHaveBeenCalled();
- });
- });
- });
-
-});
diff --git a/platform/commonUI/general/test/services/PopupServiceSpec.js b/platform/commonUI/general/test/services/PopupServiceSpec.js
deleted file mode 100644
index 45d1d2edc..000000000
--- a/platform/commonUI/general/test/services/PopupServiceSpec.js
+++ /dev/null
@@ -1,98 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../../src/services/PopupService"],
- function (PopupService) {
-
- describe("PopupService", function () {
- var mockDocument,
- testWindow,
- mockBody,
- mockElement,
- popupService;
-
- beforeEach(function () {
- mockDocument = jasmine.createSpyObj('$document', ['find']);
- testWindow = {
- innerWidth: 1000,
- innerHeight: 800
- };
- mockBody = jasmine.createSpyObj('body', ['append']);
- mockElement = jasmine.createSpyObj('element', [
- 'css',
- 'remove'
- ]);
-
- mockDocument.find.and.callFake(function (query) {
- return query === 'body' && mockBody;
- });
-
- popupService = new PopupService(mockDocument, testWindow);
- });
-
- it("adds elements to the body of the document", function () {
- popupService.display(mockElement, [0, 0]);
- expect(mockBody.append).toHaveBeenCalledWith(mockElement);
- });
-
- describe("when positioned in appropriate quadrants", function () {
- it("orients elements relative to the top-left", function () {
- popupService.display(mockElement, [25, 50]);
- expect(mockElement.css).toHaveBeenCalledWith({
- position: 'absolute',
- left: '25px',
- top: '50px'
- });
- });
-
- it("orients elements relative to the top-right", function () {
- popupService.display(mockElement, [800, 50]);
- expect(mockElement.css).toHaveBeenCalledWith({
- position: 'absolute',
- right: '200px',
- top: '50px'
- });
- });
-
- it("orients elements relative to the bottom-right", function () {
- popupService.display(mockElement, [800, 650]);
- expect(mockElement.css).toHaveBeenCalledWith({
- position: 'absolute',
- right: '200px',
- bottom: '150px'
- });
- });
-
- it("orients elements relative to the bottom-left", function () {
- popupService.display(mockElement, [120, 650]);
- expect(mockElement.css).toHaveBeenCalledWith({
- position: 'absolute',
- left: '120px',
- bottom: '150px'
- });
- });
- });
-
- });
- }
-);
diff --git a/platform/commonUI/general/test/services/PopupSpec.js b/platform/commonUI/general/test/services/PopupSpec.js
deleted file mode 100644
index 1f2b6b98f..000000000
--- a/platform/commonUI/general/test/services/PopupSpec.js
+++ /dev/null
@@ -1,74 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../../src/services/Popup"],
- function (Popup) {
-
- describe("Popup", function () {
- var mockElement,
- testStyles,
- popup;
-
- beforeEach(function () {
- mockElement =
- jasmine.createSpyObj('element', ['css', 'remove']);
- testStyles = {
- left: '12px',
- top: '14px'
- };
- popup = new Popup(mockElement, testStyles);
- });
-
- it("applies CSS styles when instantiated", function () {
- expect(mockElement.css)
- .toHaveBeenCalledWith(testStyles);
- });
-
- it("reports the orientation of the popup", function () {
- var otherStyles = {
- right: '12px',
- bottom: '14px'
- },
- otherPopup = new Popup(mockElement, otherStyles);
-
- expect(popup.goesLeft()).toBeFalsy();
- expect(popup.goesRight()).toBeTruthy();
- expect(popup.goesUp()).toBeFalsy();
- expect(popup.goesDown()).toBeTruthy();
-
- expect(otherPopup.goesLeft()).toBeTruthy();
- expect(otherPopup.goesRight()).toBeFalsy();
- expect(otherPopup.goesUp()).toBeTruthy();
- expect(otherPopup.goesDown()).toBeFalsy();
- });
-
- it("removes elements when dismissed", function () {
- expect(mockElement.remove).not.toHaveBeenCalled();
- popup.dismiss();
- expect(mockElement.remove).toHaveBeenCalled();
- });
-
- });
-
- }
-);
diff --git a/platform/commonUI/general/test/services/UrlServiceSpec.js b/platform/commonUI/general/test/services/UrlServiceSpec.js
deleted file mode 100644
index 1086084c2..000000000
--- a/platform/commonUI/general/test/services/UrlServiceSpec.js
+++ /dev/null
@@ -1,98 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * MCTRepresentationSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/services/UrlService"],
- function (UrlService) {
-
- describe("The url service", function () {
- var urlService,
- mockLocation,
- mockDomainObject,
- mockContext,
- mockMode,
- testViews;
-
- beforeEach(function () {
- // Creates a mockLocation, used to
- // do the view search
- mockLocation = jasmine.createSpyObj(
- "$location",
- ["path", "search"]
- );
-
- // The mockDomainObject is initialized as a
- // spy object to ultimately be passed into the
- // urlService urlFor function
- mockDomainObject = jasmine.createSpyObj(
- "domainObject",
- ["getId", "getCapability", "getModel", "useCapability"]
- );
- mockContext = jasmine.createSpyObj('context', ['getPath']);
- testViews = [
- { key: 'abc' },
- {
- key: 'def',
- someKey: 'some value'
- },
- { key: 'xyz' }
- ];
- mockMode = "browse";
-
- // The mockContext is set a path
- // for the mockDomainObject
- mockContext.getPath.and.returnValue(
- [mockDomainObject]
- );
-
- // view capability used with the testviews made
- mockDomainObject.useCapability.and.callFake(function (c) {
- return (c === 'view') && testViews;
- });
-
- // context capability used with the mockContext created
- // so the variables including context in the urlFor are
- // initialized and reached
- mockDomainObject.getCapability.and.callFake(function (c) {
- return c === 'context' && mockContext;
- });
-
- // Uses the mockLocation to get the current
- // "mock" website's view
- mockLocation.search.and.returnValue({ view: 'def' });
-
- urlService = new UrlService(mockLocation);
- });
-
- it("get url for a location using domainObject and mode", function () {
- urlService.urlForLocation(mockMode, mockDomainObject);
- });
-
- it("get url for a new tab using domainObject and mode", function () {
- urlService.urlForNewTab(mockMode, mockDomainObject);
- });
- });
- }
-);
diff --git a/platform/commonUI/general/test/suite.json b/platform/commonUI/general/test/suite.json
deleted file mode 100644
index 09d0bfd09..000000000
--- a/platform/commonUI/general/test/suite.json
+++ /dev/null
@@ -1,29 +0,0 @@
-[
- "controllers/ActionGroupController",
- "controllers/BottomBarController",
- "controllers/ClickAwayController",
- "controllers/ContextMenuController",
- "controllers/DateTimeFieldController",
- "controllers/DateTimePickerController",
- "controllers/GetterSetterController",
- "controllers/ObjectInspectorController",
- "controllers/SelectorController",
- "controllers/TimeRangeController",
- "controllers/ToggleController",
- "controllers/TreeNodeController",
- "controllers/ViewSwitcherController",
- "directives/MCTClickElsewhere",
- "directives/MCTContainer",
- "directives/MCTDrag",
- "directives/MCTPopup",
- "directives/MCTResize",
- "directives/MCTScroll",
- "directives/MCTSplitPane",
- "directives/MCTSplitter",
- "filters/ReverseFilter",
- "services/Popup",
- "services/PopupService",
- "services/UrlService",
- "StyleSheetLoader",
- "UnsupportedBrowserWarning"
-]
diff --git a/platform/commonUI/general/test/ui/TreeViewSpec.js b/platform/commonUI/general/test/ui/TreeViewSpec.js
deleted file mode 100644
index b99d20661..000000000
--- a/platform/commonUI/general/test/ui/TreeViewSpec.js
+++ /dev/null
@@ -1,290 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
- '../../src/ui/TreeView',
- 'zepto'
-], function (TreeView, $) {
-
- xdescribe("TreeView", function () {
- var mockGestureService,
- mockGestureHandle,
- mockDomainObject,
- mockMutation,
- mockUnlisten,
- testCapabilities,
- treeView;
-
- function makeMockDomainObject(id, model, capabilities) {
- var mockDomainObj = jasmine.createSpyObj(
- 'domainObject-' + id,
- [
- 'getId',
- 'getModel',
- 'getCapability',
- 'hasCapability',
- 'useCapability'
- ]
- );
- mockDomainObj.getId.and.returnValue(id);
- mockDomainObj.getModel.and.returnValue(model);
- mockDomainObj.hasCapability.and.callFake(function (c) {
- return Boolean(capabilities[c]);
- });
- mockDomainObj.getCapability.and.callFake(function (c) {
- return capabilities[c];
- });
- mockDomainObj.useCapability.and.callFake(function (c) {
- return capabilities[c] && capabilities[c].invoke();
- });
-
- return mockDomainObj;
- }
-
- beforeEach(function () {
- mockGestureService = jasmine.createSpyObj(
- 'gestureService',
- ['attachGestures']
- );
-
- mockGestureHandle = jasmine.createSpyObj('gestures', ['destroy']);
-
- mockGestureService.attachGestures.and.returnValue(mockGestureHandle);
-
- mockMutation = jasmine.createSpyObj('mutation', ['listen']);
- mockUnlisten = jasmine.createSpy('unlisten');
- mockMutation.listen.and.returnValue(mockUnlisten);
-
- testCapabilities = { mutation: mockMutation };
-
- mockDomainObject =
- makeMockDomainObject('parent', {}, testCapabilities);
-
- treeView = new TreeView(mockGestureService);
- });
-
- describe("elements", function () {
- var elements;
-
- beforeEach(function () {
- elements = treeView.elements();
- });
-
- it("is an unordered list", function () {
- expect(elements[0].tagName.toLowerCase())
- .toEqual('ul');
- });
- });
-
- describe("model", function () {
- var mockComposition;
-
- function makeGenericCapabilities() {
- var mockStatus =
- jasmine.createSpyObj('status', ['listen', 'list']);
-
- mockStatus.list.and.returnValue([]);
-
- return {
- context: jasmine.createSpyObj('context', ['getPath']),
- type: jasmine.createSpyObj('type', ['getCssClass']),
- location: jasmine.createSpyObj('location', ['isLink']),
- mutation: jasmine.createSpyObj('mutation', ['listen']),
- status: mockStatus
- };
- }
-
- beforeEach(function () {
- mockComposition = ['a', 'b', 'c'].map(function (id) {
- var testCaps = makeGenericCapabilities(),
- mockChild =
- makeMockDomainObject(id, {}, testCaps);
-
- testCaps.context.getPath
- .and.returnValue([mockDomainObject, mockChild]);
-
- return mockChild;
- });
-
- testCapabilities.composition =
- jasmine.createSpyObj('composition', ['invoke']);
- testCapabilities.composition.invoke
- .and.returnValue(Promise.resolve(mockComposition));
-
- treeView.model(mockDomainObject);
-
- return testCapabilities.composition.invoke();
- });
-
- it("adds one node per composition element", function () {
- expect(treeView.elements()[0].childElementCount)
- .toEqual(mockComposition.length);
- });
-
- it("listens for mutation", function () {
- expect(testCapabilities.mutation.listen)
- .toHaveBeenCalledWith(jasmine.any(Function));
- });
-
- describe("when mutation occurs", function () {
- beforeEach(function () {
- mockComposition.pop();
- testCapabilities.mutation.listen
- .calls.mostRecent().args[0](mockDomainObject.getModel());
-
- return testCapabilities.composition.invoke();
- });
-
- it("continues to show one node per composition element", function () {
- expect(treeView.elements()[0].childElementCount)
- .toEqual(mockComposition.length);
- });
- });
-
- describe("when replaced with a non-compositional domain object", function () {
- beforeEach(function () {
- delete testCapabilities.composition;
- treeView.model(mockDomainObject);
- });
-
- it("stops listening for mutation", function () {
- expect(mockUnlisten).toHaveBeenCalled();
- });
-
- it("removes all tree nodes", function () {
- expect(treeView.elements()[0].childElementCount)
- .toEqual(0);
- });
- });
-
- describe("when selection state changes", function () {
- var selectionIndex = 1;
-
- beforeEach(function () {
- treeView.value(mockComposition[selectionIndex]);
- });
-
- it("communicates selection state to an appropriate node", function () {
- var selected = $(treeView.elements()[0]).find('.selected');
- expect(selected.length).toEqual(1);
- });
- });
-
- describe("when a context-less object is selected", function () {
- beforeEach(function () {
- var testCaps = makeGenericCapabilities(),
- mockDomainObj =
- makeMockDomainObject('xyz', {}, testCaps);
- delete testCaps.context;
- treeView.value(mockDomainObj);
- });
-
- it("clears all selection state", function () {
- var selected = $(treeView.elements()[0]).find('.selected');
- expect(selected.length).toEqual(0);
- });
- });
-
- describe("when children contain children", function () {
- beforeEach(function () {
- var newCapabilities = makeGenericCapabilities(),
- gcCapabilities = makeGenericCapabilities(),
- mockNewChild =
- makeMockDomainObject('d', {}, newCapabilities),
- mockGrandchild =
- makeMockDomainObject('gc', {}, gcCapabilities);
-
- newCapabilities.composition =
- jasmine.createSpyObj('composition', ['invoke']);
- newCapabilities.composition.invoke
- .and.returnValue(Promise.resolve([mockGrandchild]));
- mockComposition.push(mockNewChild);
-
- newCapabilities.context.getPath.and.returnValue([
- mockDomainObject,
- mockNewChild
- ]);
- gcCapabilities.context.getPath.and.returnValue([
- mockDomainObject,
- mockNewChild,
- mockGrandchild
- ]);
-
- testCapabilities.mutation.listen
- .calls.mostRecent().args[0](mockDomainObject);
-
- return testCapabilities.composition.invoke().then(function () {
- treeView.value(mockGrandchild);
-
- return newCapabilities.composition.invoke();
- });
- });
-
- it("creates inner trees", function () {
- expect($(treeView.elements()[0]).find('ul').length)
- .toEqual(1);
- });
- });
-
- describe("when status changes", function () {
- var testStatuses;
-
- beforeEach(function () {
- var mockStatus = mockComposition[1].getCapability('status');
-
- testStatuses = ['foo'];
-
- mockStatus.list.and.returnValue(testStatuses);
- mockStatus.listen.calls.mostRecent().args[0](testStatuses);
- });
-
- it("reflects the status change in the tree", function () {
- expect($(treeView.elements()).find('.s-status-foo').length)
- .toEqual(1);
- });
- });
- });
-
- describe("observe", function () {
- var mockCallback,
- unobserve;
-
- beforeEach(function () {
- mockCallback = jasmine.createSpy('callback');
- unobserve = treeView.observe(mockCallback);
- });
-
- it("notifies listeners when value is changed", function () {
- treeView.value(mockDomainObject, {some: event});
- expect(mockCallback)
- .toHaveBeenCalledWith(mockDomainObject, {some: event});
- });
-
- it("does not notify listeners when deactivated", function () {
- unobserve();
- treeView.value(mockDomainObject);
- expect(mockCallback).not.toHaveBeenCalled();
- });
- });
- });
-
-});
diff --git a/platform/commonUI/inspect/bundle.js b/platform/commonUI/inspect/bundle.js
deleted file mode 100644
index d06017e18..000000000
--- a/platform/commonUI/inspect/bundle.js
+++ /dev/null
@@ -1,117 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
- "./src/gestures/InfoGesture",
- "./src/gestures/InfoButtonGesture",
- "./src/services/InfoService",
- "./res/info-table.html",
- "./res/info-bubble.html",
- "./res/bubble.html",
- "./res/templates/info-button.html"
-], function (
- InfoGesture,
- InfoButtonGesture,
- InfoService,
- infoTableTemplate,
- infoBubbleTemplate,
- bubbleTemplate,
- infoButtonTemplate
-) {
-
- return {
- name: "platform/commonUI/inspect",
- definition: {
- "extensions": {
- "templates": [
- {
- "key": "info-table",
- "template": infoTableTemplate
- },
- {
- "key": "info-bubble",
- "template": infoBubbleTemplate
- }
- ],
- "containers": [
- {
- "key": "bubble",
- "template": bubbleTemplate,
- "attributes": [
- "bubbleTitle",
- "bubbleLayout"
- ],
- "alias": "bubble"
- }
- ],
- "gestures": [
- {
- "key": "info",
- "implementation": InfoGesture,
- "depends": [
- "$timeout",
- "agentService",
- "infoService",
- "INFO_HOVER_DELAY"
- ]
- },
- {
- "key": "infobutton",
- "implementation": InfoButtonGesture,
- "depends": [
- "$document",
- "agentService",
- "infoService"
- ]
- }
- ],
- "services": [
- {
- "key": "infoService",
- "implementation": InfoService,
- "depends": [
- "$compile",
- "$rootScope",
- "popupService",
- "agentService"
- ]
- }
- ],
- "constants": [
- {
- "key": "INFO_HOVER_DELAY",
- "value": 2000
- }
- ],
- "representations": [
- {
- "key": "info-button",
- "template": infoButtonTemplate,
- "gestures": [
- "infobutton"
- ]
- }
- ]
- }
- }
- };
-});
diff --git a/platform/commonUI/inspect/res/bubble.html b/platform/commonUI/inspect/res/bubble.html
deleted file mode 100644
index f528d6432..000000000
--- a/platform/commonUI/inspect/res/bubble.html
+++ /dev/null
@@ -1,9 +0,0 @@
- <div class="t-infobubble s-infobubble l-infobubble-wrapper {{bubble.bubbleLayout}}">
- <div class="l-infobubble">
- <div ng-show="bubble.bubbleTitle.length > 0"
- class="title">
- {{bubble.bubbleTitle}}
- </div>
- <span ng-transclude></span>
- </div>
- </div>
diff --git a/platform/commonUI/inspect/res/info-bubble.html b/platform/commonUI/inspect/res/info-bubble.html
deleted file mode 100644
index 82545cb29..000000000
--- a/platform/commonUI/inspect/res/info-bubble.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<mct-container
- key="bubble"
- bubble-title="{{parameters.title}}"
- bubble-layout="{{parameters.layout}}"
- >
- <mct-include key="info-table"
- ng-model="ngModel">
- </mct-include>
-</mct-container>
diff --git a/platform/commonUI/inspect/res/info-table.html b/platform/commonUI/inspect/res/info-table.html
deleted file mode 100644
index 0c89a498d..000000000
--- a/platform/commonUI/inspect/res/info-table.html
+++ /dev/null
@@ -1,8 +0,0 @@
-<table>
- <tr ng-repeat="property in ngModel">
- <td class="label">{{property.name}}</td>
- <td title="{{property.value}}" class="value align-{{property.align}}">
- {{property.value}}
- </td>
- </tr>
-</table>
diff --git a/platform/commonUI/inspect/res/infobubble.html b/platform/commonUI/inspect/res/infobubble.html
deleted file mode 100644
index 3afb65c71..000000000
--- a/platform/commonUI/inspect/res/infobubble.html
+++ /dev/null
@@ -1,71 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
- as represented by the Administrator of the National Aeronautics and Space
- Administration. All rights reserved.
-
- Open MCT is licensed under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0.
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Open MCT includes source code licensed under additional open source
- licenses. See the Open Source Licenses file (LICENSES.md) included with
- this source code distribution or the Licensing information page available
- at runtime from the About dialog for additional information.
--->
-<div ng-init="
-bubbles = [
- {layout: 'arw-top arw-left'},
- {layout: 'arw-top arw-right'},
- {layout: 'arw-btm arw-left'},
- {layout: 'arw-btm arw-right'}
-];
-
-titlex='Egress Scenario scelerisque mauris pid montes nunc ut aliquam elementum tincidunt phasellus 1';
-title='Egress Scenario 1';
-
-propertiesShort=[{label:'Type', value:'Timeline', align:'left'},
-{label:'Start', value:'2015-04-27 00:00:00 UTC', align:'left'},
-{label:'End', value:'2015-04-27 09:15:37 UTC', align:'left'},
-{label:'ID', value:'WRP-T-89', align:'left'}];
-
-properties=[{label:'Type', value:'Timeline', align:'left'},
-{label:'Start', value:'2015-04-27 00:00:00 UTC', align:'left'},
-{label:'End', value:'2015-04-27 06:07:26 UTC', align:'left'},
-{label:'Duration', value:'06:07:26', align:'left'},
-{label:'Created', value:'2015-04-26 12:27:00 UTC', align:'left'},
-{label:'Modified', value:'2015-04-26 23:21:00 UTC', align:'left'},
-{label:'Status', value:'Up to date', align:'left'},
-{label:'URL', value:'http://www.logitech.com/en-us/product/bluetooth-mouse-m557?crid=7', align:'wrap'},
-{label:'Description', value:'Enjoy exceptional battery life for a Bluetooth mouse, and work for up to a full year between battery changes, An On/Off switch helps conserve power, smart sleep mode extends battery life, and an indicator light helps to ensure that you’ll never be caught off guard.', align:'wrap'},
-{label:'ID', value:'WRP-T-89', align:'left'}];
-"></div>
-
-
-<div
- style="position: absolute"
- ng-repeat="bubble in bubbles"
- ng-style="{left: $index * 300 + 'px'}"
- >
- <div class="t-infobubble s-infobubble l-infobubble-wrapper {{bubble.layout}}">
- <div class="l-infobubble">
- <div
- ng-show="title.length > 0"
- class="title"
- >{{title}}
- </div>
- <table>
- <tr ng-repeat="property in properties">
- <td class="label">{{property.label}}</td>
- <td title="{{property.value}}" class="value align-{{property.align}}">{{property.value}}</td>
- </tr>
- </table>
- </div>
- </div>
-</div>
diff --git a/platform/commonUI/inspect/res/templates/info-button.html b/platform/commonUI/inspect/res/templates/info-button.html
deleted file mode 100644
index 5e22b5d6c..000000000
--- a/platform/commonUI/inspect/res/templates/info-button.html
+++ /dev/null
@@ -1,24 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
- as represented by the Administrator of the National Aeronautics and Space
- Administration. All rights reserved.
-
- Open MCT is licensed under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0.
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Open MCT includes source code licensed under additional open source
- licenses. See the Open Source Licenses file (LICENSES.md) included with
- this source code distribution or the Licensing information page available
- at runtime from the About dialog for additional information.
--->
-
-<!--The icon for the info button appearing in a grid item (list in folder)-->
-<a class='s-icon-button icon-info'></a>
diff --git a/platform/commonUI/inspect/src/InfoConstants.js b/platform/commonUI/inspect/src/InfoConstants.js
deleted file mode 100644
index 0a66b51e4..000000000
--- a/platform/commonUI/inspect/src/InfoConstants.js
+++ /dev/null
@@ -1,47 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * This bundle provides support for object inspection (specifically, metadata
- * show in bubbles on hover.)
- * @namespace platform/commonUI/inspect
- */
-
-define({
- BUBBLE_TEMPLATE: "<mct-container key=\"bubble\" "
- + "bubble-title=\"{{bubbleTitle}}\" "
- + "bubble-layout=\"{{bubbleLayout}}\">"
- + "<mct-include key=\"bubbleTemplate\" "
- + "ng-model=\"bubbleModel\">"
- + "</mct-include>"
- + "</mct-container>",
- // Options and classes for bubble
- BUBBLE_OPTIONS: {
- offsetX: 0,
- offsetY: -26
- },
- BUBBLE_MOBILE_POSITION: [0, -25],
- // Max width and margins allowed for bubbles;
- // defined in /platform/commonUI/general/res/sass/_constants.scss
- BUBBLE_MARGIN_LR: 10,
- BUBBLE_MAX_WIDTH: 300
-});
diff --git a/platform/commonUI/inspect/src/gestures/InfoButtonGesture.js b/platform/commonUI/inspect/src/gestures/InfoButtonGesture.js
deleted file mode 100644
index 7c9482e07..000000000
--- a/platform/commonUI/inspect/src/gestures/InfoButtonGesture.js
+++ /dev/null
@@ -1,115 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- /**
- * The `info` gesture displays domain object metadata in a
- * bubble on hover.
- *
- * @constructor
- * @param $document Angular's `$document`
- * @param {InfoService} infoService a service which shows info bubbles
- * @param element jqLite-wrapped DOM element
- * @param {DomainObject} domainObject the domain object for which to
- * show information
- */
- function InfoGestureButton($document, agentService, infoService, element, domainObject) {
- var dismissBubble,
- touchPosition,
- body = $document.find('body');
-
- function trackPosition(event) {
- // Record touch position, so bubble can be shown at latest
- // touch position, also offset by 22px to left (accounts for
- // a finger-sized touch on the info button)
- touchPosition = [event.clientX - 22, event.clientY];
- }
-
- // Hides the bubble and detaches the
- // body hidebubble listener
- function hideBubble() {
- // If a bubble is showing, dismiss it
- if (dismissBubble) {
- dismissBubble();
- dismissBubble = undefined;
- }
-
- // Detaches body touch listener
- body.off('touchstart', hideBubble);
- }
-
- // Displays the bubble by tracking position of
- // touch, using infoService to display the bubble,
- // and then on any body touch the bubble is dismissed
- function showBubble(event) {
- trackPosition(event);
- event.stopPropagation();
- // Show the bubble, but on any touchstart on the
- // body (anywhere) call hidebubble
- dismissBubble = infoService.display(
- "info-table",
- domainObject.getModel().name,
- domainObject.useCapability('metadata'),
- touchPosition
- );
-
- // On any touch on the body, default body touches/events
- // are prevented, the bubble is dismissed, and the touchstart
- // body event is unbound, reallowing gestures
- body.on('touchstart', function (evt) {
- evt.preventDefault();
- hideBubble();
- body.unbind('touchstart');
- });
- }
-
- // Checks if you are on a mobile device, if the device is
- // mobile (agentService.isMobile() = true), then
- // the a click on something (info button) brings up
- // the bubble
- if (agentService.isMobile()) {
- element.on('click', showBubble);
- }
-
- return {
- /**
- * Detach any event handlers associated with this gesture.
- * @memberof InfoGesture
- * @method
- */
- destroy: function () {
- // Dismiss any active bubble...
- hideBubble();
- // ...and detach listeners
- element.off('click', showBubble);
- }
- };
- }
-
- return InfoGestureButton;
-
- }
-
-);
diff --git a/platform/commonUI/inspect/src/gestures/InfoGesture.js b/platform/commonUI/inspect/src/gestures/InfoGesture.js
deleted file mode 100644
index 424d3557f..000000000
--- a/platform/commonUI/inspect/src/gestures/InfoGesture.js
+++ /dev/null
@@ -1,149 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- /**
- * The `info` gesture displays domain object metadata in a
- * bubble on hover.
- *
- * @memberof platform/commonUI/inspect
- * @constructor
- * @implements {Gesture}
- * @param $timeout Angular's `$timeout`
- * @param {InfoService} infoService a service which shows info bubbles
- * @param {number} delay delay, in milliseconds, before bubble appears
- * @param element jqLite-wrapped DOM element
- * @param {DomainObject} domainObject the domain object for which to
- * show information
- */
- function InfoGesture($timeout, agentService, infoService, delay, element, domainObject) {
- var self = this;
-
- // Callback functions to preserve the "this" pointer (in the
- // absence of Function.prototype.bind)
- this.showBubbleCallback = function (event) {
- self.showBubble(event);
- };
-
- this.hideBubbleCallback = function (event) {
- self.hideBubble(event);
- };
-
- this.trackPositionCallback = function (event) {
- self.trackPosition(event);
- };
-
- this.element = element;
- this.$timeout = $timeout;
- this.infoService = infoService;
- this.delay = delay;
- this.domainObject = domainObject;
-
- // Checks if you are on a mobile device, if the device is
- // not mobile (agentService.isMobile() = false), then
- // the pendingBubble and therefore hovering is allowed
- if (!agentService.isMobile()) {
- // Show bubble (on a timeout) on mouse over
- element.on('mouseenter', this.showBubbleCallback);
- }
- }
-
- InfoGesture.prototype.trackPosition = function (event) {
- // Record mouse position, so bubble can be shown at latest
- // mouse position (not just where the mouse entered)
- this.mousePosition = [event.clientX, event.clientY];
- };
-
- InfoGesture.prototype.hideBubble = function () {
- // If a bubble is showing, dismiss it
- if (this.dismissBubble) {
- this.dismissBubble();
- this.element.off('mouseleave', this.hideBubbleCallback);
- this.dismissBubble = undefined;
- }
-
- // If a bubble will be shown on a timeout, cancel that
- if (this.pendingBubble) {
- this.$timeout.cancel(this.pendingBubble);
- this.element.off('mousemove', this.trackPositionCallback);
- this.element.off('mouseleave', this.hideBubbleCallback);
- this.pendingBubble = undefined;
- }
-
- // Also clear mouse position so we don't have a ton of tiny
- // arrays allocated while user mouses over things
- this.mousePosition = undefined;
- };
-
- InfoGesture.prototype.showBubble = function (event) {
- var self = this;
-
- function displayBubble() {
- self.dismissBubble = self.infoService.display(
- "info-table",
- self.domainObject.getModel().name,
- self.domainObject.useCapability('metadata'),
- self.mousePosition
- );
- self.element.off('mousemove', self.trackPositionCallback);
- self.pendingBubble = undefined;
- }
-
- this.trackPosition(event);
-
- // Do nothing if we're already scheduled to show a bubble.
- // This may happen due to redundant event firings caused
- // by https://github.com/angular/angular.js/issues/12795
- if (this.pendingBubble) {
- return;
- }
-
- // Also need to track position during hover
- this.element.on('mousemove', this.trackPositionCallback);
-
- // Show the bubble, after a suitable delay (if mouse has
- // left before this time is up, this will be canceled.)
- this.pendingBubble = this.$timeout(displayBubble, this.delay);
-
- this.element.on('mouseleave', this.hideBubbleCallback);
- };
-
- /**
- * Detach any event handlers associated with this gesture.
- * @method
- */
- InfoGesture.prototype.destroy = function () {
- // Dismiss any active bubble...
- this.hideBubble();
- // ...and detach listeners
- this.element.off('mouseenter', this.showBubbleCallback);
- };
-
- return InfoGesture;
-
- }
-
-);
-
diff --git a/platform/commonUI/inspect/src/services/InfoService.js b/platform/commonUI/inspect/src/services/InfoService.js
deleted file mode 100644
index 36b789230..000000000
--- a/platform/commonUI/inspect/src/services/InfoService.js
+++ /dev/null
@@ -1,106 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ['../InfoConstants'],
- function (InfoConstants) {
-
- var BUBBLE_TEMPLATE = InfoConstants.BUBBLE_TEMPLATE,
- MOBILE_POSITION = InfoConstants.BUBBLE_MOBILE_POSITION,
- OPTIONS = InfoConstants.BUBBLE_OPTIONS;
-
- /**
- * Displays informative content ("info bubbles") for the user.
- * @memberof platform/commonUI/inspect
- * @constructor
- */
- function InfoService($compile, $rootScope, popupService, agentService) {
- this.$compile = $compile;
- this.$rootScope = $rootScope;
- this.popupService = popupService;
- this.agentService = agentService;
- }
-
- /**
- * Display an info bubble at the specified location.
- * @param {string} templateKey template to place in bubble
- * @param {string} title title for the bubble
- * @param {*} content content to pass to the template, via
- * `ng-model`
- * @param {number[]} x,y position of the info bubble, in
- * pixel coordinates.
- * @returns {Function} a function that may be invoked to
- * dismiss the info bubble
- */
- InfoService.prototype.display = function (templateKey, title, content, position) {
- var $compile = this.$compile,
- $rootScope = this.$rootScope,
- scope = $rootScope.$new(),
- span = $compile('<span></span>')(scope),
- bubbleSpaceLR = InfoConstants.BUBBLE_MARGIN_LR
- + InfoConstants.BUBBLE_MAX_WIDTH,
- options,
- popup,
- bubble;
-
- options = Object.create(OPTIONS);
- options.marginX = -bubbleSpaceLR;
-
- // prevent bubble from appearing right under pointer,
- // which causes hover callback to be called multiple times
- options.offsetX = 1;
-
- // On a phone, bubble takes up more screen real estate,
- // so position it differently (toward the bottom)
- if (this.agentService.isPhone()) {
- position = MOBILE_POSITION;
- options = {};
- }
-
- popup = this.popupService.display(span, position, options);
-
- // Pass model & container parameters into the scope
- scope.bubbleModel = content;
- scope.bubbleTemplate = templateKey;
- scope.bubbleTitle = title;
- // Style the bubble according to how it was positioned
- scope.bubbleLayout = [
- popup.goesUp() ? 'arw-btm' : 'arw-top',
- popup.goesLeft() ? 'arw-right' : 'arw-left'
- ].join(' ');
-
- // Create the info bubble, now that we know how to
- // point the arrow...
- bubble = $compile(BUBBLE_TEMPLATE)(scope);
- span.append(bubble);
-
- // Return a function to dismiss the info bubble
- return function dismiss() {
- popup.dismiss();
- scope.$destroy();
- };
- };
-
- return InfoService;
- }
-);
-
diff --git a/platform/commonUI/inspect/test/gestures/InfoButtonGestureSpec.js b/platform/commonUI/inspect/test/gestures/InfoButtonGestureSpec.js
deleted file mode 100644
index 2f81e4b32..000000000
--- a/platform/commonUI/inspect/test/gestures/InfoButtonGestureSpec.js
+++ /dev/null
@@ -1,148 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ['../../src/gestures/InfoButtonGesture'],
- function (InfoButtonGesture) {
-
- describe("The info button gesture", function () {
- var mockDocument,
- mockBody,
- mockAgentService,
- mockInfoService,
- mockElement,
- mockDomainObject,
- mockEvent,
- mockScope,
- mockOff,
- testMetadata,
- mockHide,
- gesture,
- fireGesture,
- fireDismissGesture;
-
- beforeEach(function () {
- mockDocument = jasmine.createSpyObj('$document', ['find']);
- mockBody = jasmine.createSpyObj('body', ['on', 'off', 'scope', 'css', 'unbind']);
- mockDocument.find.and.returnValue(mockBody);
- mockAgentService = jasmine.createSpyObj('agentService', ['isMobile', 'isPhone']);
- mockInfoService = jasmine.createSpyObj(
- 'infoService',
- ['display']
- );
- mockElement = jasmine.createSpyObj(
- 'element',
- ['on', 'off', 'scope', 'css']
- );
- mockDomainObject = jasmine.createSpyObj(
- 'domainObject',
- ['getId', 'getCapability', 'useCapability', 'getModel']
- );
-
- mockEvent = jasmine.createSpyObj("event", ["preventDefault", "stopPropagation"]);
- mockEvent.pageX = 0;
- mockEvent.pageY = 0;
- mockScope = jasmine.createSpyObj('$scope', ['$on']);
- mockOff = jasmine.createSpy('$off');
- testMetadata = [{
- name: "Test name",
- value: "Test value"
- }];
- mockHide = jasmine.createSpy('hide');
-
- mockDomainObject.getModel.and.returnValue({ name: "Test Object" });
- mockDomainObject.useCapability.and.callFake(function (c) {
- return (c === 'metadata') ? testMetadata : undefined;
- });
- mockElement.scope.and.returnValue(mockScope);
- mockScope.$on.and.returnValue(mockOff);
- mockInfoService.display.and.returnValue(mockHide);
- mockAgentService.isMobile.and.returnValue(true);
- gesture = new InfoButtonGesture(
- mockDocument,
- mockAgentService,
- mockInfoService,
- mockElement,
- mockDomainObject
- );
- fireGesture = mockElement.on.calls.mostRecent().args[1];
- });
-
- it("expect click on the representation", function () {
- // Fires a click call on element and then
- // expects the click to have happened
- fireGesture(mockEvent);
- expect(mockElement.on).toHaveBeenCalledWith(
- "click",
- jasmine.any(Function)
- );
- });
-
- it("expect click then dismiss on the representation", function () {
- // Fire the click and then expect the click
- fireGesture(mockEvent);
- expect(mockElement.on).toHaveBeenCalledWith(
- "click",
- jasmine.any(Function)
- );
-
- // Get the touch start on the body
- // and fire the dismiss gesture
- fireDismissGesture = mockBody.on.calls.mostRecent().args[1];
- fireDismissGesture(mockEvent);
- // Expect Body to have been touched, event.preventDefault()
- // to be called, then the mockBody listener to be detached
- // lastly unbind the touchstart used to dismiss so other
- // events can be called
- expect(mockBody.on).toHaveBeenCalledWith(
- "touchstart",
- jasmine.any(Function)
- );
- expect(mockEvent.preventDefault).toHaveBeenCalled();
- expect(mockBody.off).toHaveBeenCalledWith(
- "touchstart",
- jasmine.any(Function)
- );
- expect(mockBody.unbind).toHaveBeenCalledWith(
- 'touchstart'
- );
- });
-
- it("detaches a callback for info bubble events when destroyed", function () {
- expect(mockElement.off).not.toHaveBeenCalled();
-
- gesture.destroy();
-
- expect(mockElement.off).toHaveBeenCalledWith(
- "click",
- jasmine.any(Function)
- );
- });
-
- // https://github.com/nasa/openmct/issues/948
- it("does not try to access scope", function () {
- expect(mockElement.scope).not.toHaveBeenCalled();
- });
-
- });
- }
-);
diff --git a/platform/commonUI/inspect/test/gestures/InfoGestureSpec.js b/platform/commonUI/inspect/test/gestures/InfoGestureSpec.js
deleted file mode 100644
index 6f66d8247..000000000
--- a/platform/commonUI/inspect/test/gestures/InfoGestureSpec.js
+++ /dev/null
@@ -1,188 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ['../../src/gestures/InfoGesture'],
- function (InfoGesture) {
-
- describe("The info gesture", function () {
- var mockTimeout,
- mockAgentService,
- mockInfoService,
- testDelay = 12321,
- mockElement,
- mockDomainObject,
- mockScope,
- mockOff,
- testMetadata,
- mockPromise,
- mockHide,
- gesture;
-
- function fireEvent(evt, value) {
- mockElement.on.calls.all().forEach(function (call) {
- if (call.args[0] === evt) {
- call.args[1](value);
- }
- });
- }
-
- beforeEach(function () {
- mockTimeout = jasmine.createSpy('$timeout');
- mockTimeout.cancel = jasmine.createSpy('cancel');
- mockAgentService = jasmine.createSpyObj('agentService', ['isMobile']);
- mockInfoService = jasmine.createSpyObj(
- 'infoService',
- ['display']
- );
- mockElement = jasmine.createSpyObj(
- 'element',
- ['on', 'off', 'scope', 'css']
- );
- mockDomainObject = jasmine.createSpyObj(
- 'domainObject',
- ['getId', 'getCapability', 'useCapability', 'getModel']
- );
- mockScope = jasmine.createSpyObj('$scope', ['$on']);
- mockOff = jasmine.createSpy('$off');
- testMetadata = [{
- name: "Test name",
- value: "Test value"
- }];
- mockPromise = jasmine.createSpyObj('promise', ['then']);
- mockHide = jasmine.createSpy('hide');
-
- mockDomainObject.getModel.and.returnValue({ name: "Test Object" });
- mockDomainObject.useCapability.and.callFake(function (c) {
- return (c === 'metadata') ? testMetadata : undefined;
- });
- mockElement.scope.and.returnValue(mockScope);
- mockScope.$on.and.returnValue(mockOff);
- mockTimeout.and.returnValue(mockPromise);
- mockInfoService.display.and.returnValue(mockHide);
-
- gesture = new InfoGesture(
- mockTimeout,
- mockAgentService,
- mockInfoService,
- testDelay,
- mockElement,
- mockDomainObject
- );
- });
-
- it("listens for mouseenter on the representation", function () {
- expect(mockElement.on)
- .toHaveBeenCalledWith('mouseenter', jasmine.any(Function));
- });
-
- it("displays an info bubble on a delay after mouseenter", function () {
- fireEvent("mouseenter", {
- clientX: 1977,
- clientY: 42
- });
- expect(mockTimeout)
- .toHaveBeenCalledWith(jasmine.any(Function), testDelay);
- mockTimeout.calls.mostRecent().args[0]();
- expect(mockInfoService.display).toHaveBeenCalledWith(
- jasmine.any(String),
- "Test Object",
- testMetadata,
- [1977, 42]
- );
- });
-
- it("does not display info bubble if mouse leaves too soon", function () {
- fireEvent("mouseenter", {
- clientX: 1977,
- clientY: 42
- });
- fireEvent("mouseleave", {
- clientX: 1977,
- clientY: 42
- });
- expect(mockTimeout.cancel).toHaveBeenCalledWith(mockPromise);
- expect(mockInfoService.display).not.toHaveBeenCalled();
- });
-
- it("hides a shown bubble when mouse leaves", function () {
- fireEvent("mouseenter", {
- clientX: 1977,
- clientY: 42
- });
- mockTimeout.calls.mostRecent().args[0]();
- expect(mockHide).not.toHaveBeenCalled(); // verify precondition
- fireEvent("mouseleave", {});
- expect(mockHide).toHaveBeenCalled();
- });
-
- it("tracks mouse position", function () {
- fireEvent("mouseenter", {
- clientX: 1977,
- clientY: 42
- });
- fireEvent("mousemove", {
- clientX: 1999,
- clientY: 11
- });
- fireEvent("mousemove", {
- clientX: 1984,
- clientY: 11
- });
- mockTimeout.calls.mostRecent().args[0]();
- // Should have displayed at the latest observed mouse position
- expect(mockInfoService.display).toHaveBeenCalledWith(
- jasmine.any(String),
- "Test Object",
- testMetadata,
- [1984, 11]
- );
- });
-
- it("hides shown bubbles when destroyed", function () {
- fireEvent("mouseenter", {
- clientX: 1977,
- clientY: 42
- });
- mockTimeout.calls.mostRecent().args[0]();
- expect(mockHide).not.toHaveBeenCalled(); // verify precondition
- gesture.destroy();
- expect(mockHide).toHaveBeenCalled();
- });
-
- it("detaches listeners when destroyed", function () {
- fireEvent("mouseenter", {
- clientX: 1977,
- clientY: 42
- });
- gesture.destroy();
- mockElement.on.calls.all().forEach(function (call) {
- expect(mockElement.off).toHaveBeenCalledWith(
- call.args[0],
- call.args[1]
- );
- });
- });
-
- });
- }
-);
diff --git a/platform/commonUI/inspect/test/services/InfoServiceSpec.js b/platform/commonUI/inspect/test/services/InfoServiceSpec.js
deleted file mode 100644
index 9641bf94d..000000000
--- a/platform/commonUI/inspect/test/services/InfoServiceSpec.js
+++ /dev/null
@@ -1,142 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ['../../src/services/InfoService'],
- function (InfoService) {
-
- describe("The info service", function () {
- var mockCompile,
- mockRootScope,
- mockPopupService,
- mockAgentService,
- mockScope,
- mockElements,
- mockPopup,
- service;
-
- beforeEach(function () {
- mockCompile = jasmine.createSpy('$compile');
- mockRootScope = jasmine.createSpyObj('$rootScope', ['$new']);
- mockAgentService = jasmine.createSpyObj('agentService', ['isMobile', 'isPhone']);
- mockPopupService = jasmine.createSpyObj(
- 'popupService',
- ['display']
- );
- mockPopup = jasmine.createSpyObj('popup', [
- 'dismiss',
- 'goesLeft',
- 'goesRight',
- 'goesUp',
- 'goesDown'
- ]);
-
- mockScope = jasmine.createSpyObj("scope", ["$destroy"]);
- mockElements = [];
-
- mockPopupService.display.and.returnValue(mockPopup);
- mockCompile.and.callFake(function () {
- var mockCompiledTemplate = jasmine.createSpy('template'),
- mockElement = jasmine.createSpyObj('element', [
- 'css',
- 'remove',
- 'append'
- ]);
- mockCompiledTemplate.and.returnValue(mockElement);
- mockElements.push(mockElement);
-
- return mockCompiledTemplate;
- });
- mockRootScope.$new.and.returnValue(mockScope);
-
- service = new InfoService(
- mockCompile,
- mockRootScope,
- mockPopupService,
- mockAgentService
- );
- });
-
- it("creates elements and displays them as popups", function () {
- service.display('', '', {}, [123, 456]);
- expect(mockPopupService.display).toHaveBeenCalledWith(
- mockElements[0],
- [123, 456],
- jasmine.any(Object)
- );
- });
-
- it("provides a function to remove displayed info bubbles", function () {
- var fn = service.display('', '', {}, [0, 0]);
- expect(mockPopup.dismiss).not.toHaveBeenCalled();
- fn();
- expect(mockPopup.dismiss).toHaveBeenCalled();
- });
-
- it("when on phone device, positions at bottom", function () {
- mockAgentService.isPhone.and.returnValue(true);
- service = new InfoService(
- mockCompile,
- mockRootScope,
- mockPopupService,
- mockAgentService
- );
- service.display('', '', {}, [123, 456]);
- expect(mockPopupService.display).toHaveBeenCalledWith(
- mockElements[0],
- [0, -25],
- jasmine.any(Object)
- );
- });
-
- [false, true].forEach(function (goesLeft) {
- [false, true].forEach(function (goesUp) {
- var vertical = goesUp ? "up" : "down",
- horizontal = goesLeft ? "left" : "right",
- location = [vertical, horizontal].join('-');
- describe("when bubble goes " + location, function () {
- var expectedLocation = [
- goesUp ? "bottom" : "top",
- goesLeft ? "right" : "left"
- ].join('-');
-
- beforeEach(function () {
- mockPopup.goesUp.and.returnValue(goesUp);
- mockPopup.goesDown.and.returnValue(!goesUp);
- mockPopup.goesLeft.and.returnValue(goesLeft);
- mockPopup.goesRight.and.returnValue(!goesLeft);
- service.display('', '', {}, [10, 10]);
- });
-
- it("positions the arrow in the " + expectedLocation, function () {
- expect(mockScope.bubbleLayout).toEqual([
- goesUp ? "arw-btm" : "arw-top",
- goesLeft ? "arw-right" : "arw-left"
- ].join(' '));
- });
- });
- });
- });
-
- });
- }
-);
diff --git a/platform/commonUI/mobile/bundle.js b/platform/commonUI/mobile/bundle.js
deleted file mode 100644
index 0cacd0f21..000000000
--- a/platform/commonUI/mobile/bundle.js
+++ /dev/null
@@ -1,44 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
- "./src/AgentService"
-], function (
- AgentService
-) {
- return {
- name: "platform/commonUI/mobile",
- definition: {
- "extensions": {
- "services": [
- {
- "key": "agentService",
- "implementation": AgentService,
- "depends": [
- "$window"
- ]
- }
- ]
- }
- }
- };
-});
diff --git a/platform/commonUI/mobile/src/AgentServiceSpec.js b/platform/commonUI/mobile/src/AgentServiceSpec.js
deleted file mode 100644
index d024c5995..000000000
--- a/platform/commonUI/mobile/src/AgentServiceSpec.js
+++ /dev/null
@@ -1,96 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-import AgentService from "./AgentService";
-
-const TEST_USER_AGENTS = {
- DESKTOP:
- "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.89 Safari/537.36",
- IPAD:
- "Mozilla/5.0 (iPad; CPU OS 7_0 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11A465 Safari/9537.53",
- IPHONE:
- "Mozilla/5.0 (iPhone; CPU iPhone OS 7_0 like Mac OS X; en-us) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11A465 Safari/9537.53"
-};
-
-describe("The AgentService", function () {
- let testWindow;
- let agentService;
-
- beforeEach(function () {
- testWindow = {
- innerWidth: 640,
- innerHeight: 480,
- navigator: {
- userAgent: TEST_USER_AGENTS.DESKTOP
- }
- };
- });
-
- it("recognizes desktop devices as non-mobile", function () {
- testWindow.navigator.userAgent = TEST_USER_AGENTS.DESKTOP;
- agentService = new AgentService(testWindow);
- expect(agentService.isMobile()).toBeFalsy();
- expect(agentService.isPhone()).toBeFalsy();
- expect(agentService.isTablet()).toBeFalsy();
- });
-
- it("detects iPhones", function () {
- testWindow.navigator.userAgent = TEST_USER_AGENTS.IPHONE;
- agentService = new AgentService(testWindow);
- expect(agentService.isMobile()).toBeTruthy();
- expect(agentService.isPhone()).toBeTruthy();
- expect(agentService.isTablet()).toBeFalsy();
- });
-
- it("detects iPads", function () {
- testWindow.navigator.userAgent = TEST_USER_AGENTS.IPAD;
- agentService = new AgentService(testWindow);
- expect(agentService.isMobile()).toBeTruthy();
- expect(agentService.isPhone()).toBeFalsy();
- expect(agentService.isTablet()).toBeTruthy();
- });
-
- it("detects display orientation", function () {
- agentService = new AgentService(testWindow);
- testWindow.innerWidth = 1024;
- testWindow.innerHeight = 400;
- expect(agentService.isPortrait()).toBeFalsy();
- expect(agentService.isLandscape()).toBeTruthy();
- testWindow.innerWidth = 400;
- testWindow.innerHeight = 1024;
- expect(agentService.isPortrait()).toBeTruthy();
- expect(agentService.isLandscape()).toBeFalsy();
- });
-
- it("detects touch support", function () {
- testWindow.ontouchstart = null;
- expect(new AgentService(testWindow).isTouch()).toBe(true);
- delete testWindow.ontouchstart;
- expect(new AgentService(testWindow).isTouch()).toBe(false);
- });
-
- it("allows for checking browser type", function () {
- testWindow.navigator.userAgent = "Chromezilla Safarifox";
- agentService = new AgentService(testWindow);
- expect(agentService.isBrowser("Chrome")).toBe(true);
- expect(agentService.isBrowser("Firefox")).toBe(false);
- });
-});
diff --git a/platform/commonUI/notification/bundle.js b/platform/commonUI/notification/bundle.js
deleted file mode 100644
index 0bb7477fa..000000000
--- a/platform/commonUI/notification/bundle.js
+++ /dev/null
@@ -1,47 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
- "./src/NotificationService"
-], function (
- NotificationService
-) {
-
- return {
- name: "platform/commonUI/notification",
- definition: {
- "extensions": {
- "services": [
- {
- "key": "notificationService",
- "implementation": function (openmct) {
- return new NotificationService.default(openmct);
- },
- "depends": [
- "openmct"
- ]
- }
- ]
- }
- }
- };
-});
diff --git a/platform/commonUI/notification/src/NotificationService.js b/platform/commonUI/notification/src/NotificationService.js
deleted file mode 100644
index 181c097c1..000000000
--- a/platform/commonUI/notification/src/NotificationService.js
+++ /dev/null
@@ -1,64 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-export default class NotificationService {
- constructor(openmct) {
- this.openmct = openmct;
- }
- info(message) {
- if (typeof message === 'string') {
- return this.openmct.notifications.info(message);
- } else {
- if (Object.prototype.hasOwnProperty.call(message, 'progress')) {
- return this.openmct.notifications.progress(message.title, message.progress, message.progressText);
- } else {
- return this.openmct.notifications.info(message.title);
- }
- }
- }
- alert(message) {
- if (typeof message === 'string') {
- return this.openmct.notifications.alert(message);
- } else {
- return this.openmct.notifications.alert(message.title);
- }
- }
- error(message) {
- if (typeof message === 'string') {
- return this.openmct.notifications.error(message);
- } else {
- return this.openmct.notifications.error(message.title);
- }
- }
- notify(options) {
- switch (options.severity) {
- case 'info':
- return this.info(options);
- case 'alert':
- return this.alert(options);
- case 'error':
- return this.error(options);
- }
- }
- getAllNotifications() {
- return this.openmct.notifications.notifications;
- }
-}
diff --git a/platform/commonUI/regions/bundle.js b/platform/commonUI/regions/bundle.js
deleted file mode 100644
index b6ecc8ec6..000000000
--- a/platform/commonUI/regions/bundle.js
+++ /dev/null
@@ -1,55 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
- './src/InspectorController',
- './src/EditableRegionPolicy'
-], function (
- InspectorController,
- EditableRegionPolicy
-) {
-
- return {
- name: "platform/commonUI/regions",
- definition: {
- "extensions": {
- "controllers": [
- {
- "key": "InspectorController",
- "implementation": InspectorController,
- "depends": [
- "$scope",
- "openmct",
- "$document"
- ]
- }
- ],
- "policies": [
- {
- "category": "region",
- "implementation": EditableRegionPolicy
- }
- ]
- }
- }
- };
-});
diff --git a/platform/commonUI/regions/src/EditableRegionPolicy.js b/platform/commonUI/regions/src/EditableRegionPolicy.js
deleted file mode 100644
index 03215bcad..000000000
--- a/platform/commonUI/regions/src/EditableRegionPolicy.js
+++ /dev/null
@@ -1,56 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- /**
- * A policy for determining whether a region part should be visible or
- * not, based on its editability and the current state of the domain
- * object .
- * @constructor
- * @implements {Policy}
- * @memberof platform/commonUI/regions
- */
- function EditableRegionPolicy() {
- }
-
- EditableRegionPolicy.prototype.allow = function (regionPart, domainObject) {
- if (!regionPart.modes) {
- return true;
- }
-
- if (domainObject.hasCapability('editor') && domainObject.getCapability('editor').inEditContext()) {
- //If the domain object is in edit mode, only include a part
- // if it is marked editable
- return regionPart.modes.indexOf('edit') !== -1;
- } else {
- //If the domain object is not in edit mode, return any parts
- // that are not explicitly marked editable
- return regionPart.modes.indexOf('browse') !== -1;
- }
- };
-
- return EditableRegionPolicy;
- }
-);
diff --git a/platform/commonUI/regions/src/InspectorController.js b/platform/commonUI/regions/src/InspectorController.js
deleted file mode 100644
index e82556b68..000000000
--- a/platform/commonUI/regions/src/InspectorController.js
+++ /dev/null
@@ -1,93 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- /**
- * The InspectorController listens for the selection changes and adds the selection
- * object to the scope.
- *
- * @constructor
- */
- function InspectorController($scope, openmct, $document) {
- var self = this;
- self.$scope = $scope;
-
- /**
- * Callback handler for the selection change event.
- * Adds the selection object to the scope. If the selected item has an inspector view,
- * it puts the key in the scope. If provider view exists, it shows the view.
- */
- function setSelection(selection) {
- if (selection[0]) {
- var view = openmct.inspectorViews.get(selection);
- var container = $document[0].querySelectorAll('.inspector-provider-view')[0];
- container.innerHTML = "";
-
- if (view) {
- self.providerView = true;
- view.show(container);
- } else {
- self.providerView = false;
- var selectedItem = selection[0].context.oldItem;
-
- if (selectedItem) {
- $scope.inspectorKey = selectedItem.getCapability("type").typeDef.inspector;
- }
- }
- }
-
- self.$scope.selection = selection;
- }
-
- openmct.selection.on("change", setSelection);
-
- setSelection(openmct.selection.get());
-
- $scope.$on("$destroy", function () {
- openmct.selection.off("change", setSelection);
- });
- }
-
- /**
- * Gets the selected item.
- *
- * @returns a domain object
- */
- InspectorController.prototype.selectedItem = function () {
- return this.$scope.selection[0] && this.$scope.selection[0].context.oldItem;
- };
-
- /**
- * Checks if a provider view exists.
- *
- * @returns 'true' if provider view exists, 'false' otherwise
- */
- InspectorController.prototype.hasProviderView = function () {
- return this.providerView;
- };
-
- return InspectorController;
- }
-);
diff --git a/platform/commonUI/regions/src/Region.js b/platform/commonUI/regions/src/Region.js
deleted file mode 100644
index 90c5be3b0..000000000
--- a/platform/commonUI/regions/src/Region.js
+++ /dev/null
@@ -1,99 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- /**
- * @typeDef {object} PartContents
- * @property {string} key If the part is defined as a
- * representation, the key corresponding to the representation.
- * @memberOf platform/commonUI/regions
- */
-
- /**
- * @typeDef {object} RegionConfiguration
- * @property {string} name A unique name for this region part
- * @property {PartContents} [content] the details of the region being
- * defined
- * @property {Array<string>} [modes] the modes that this region
- * should be included in. Options are 'edit' and 'browse'. By
- * default, will be included in both. Inclusion of regions is
- * determined by policies of category 'region'. By default, the
- * {EditableRegionPolicy} will be applied.
- * @memberOf platform/commonUI/regions
- */
-
- /**
- * Defines the interface for a screen region. A screen region is a
- * section of the browse an edit screens for an object. Regions are
- * declared in object type definitions.
- * @memberOf platform/commonUI/regions
- * @abstract
- * @constructor
- */
- function Region(configuration) {
- configuration = configuration || {};
- this.name = configuration.name;
- this.content = configuration.content;
- this.modes = configuration.modes;
-
- this.regions = [];
- }
-
- /**
- * Adds a sub-region to this region.
- * @param {Region} region the part to add
- * @param {number} [index] the position to insert the region. By default
- * will add to the end
- */
- Region.prototype.addRegion = function (region, index) {
- if (index) {
- this.regions.splice(index, 0, region);
- } else {
- this.regions.push(region);
- }
- };
-
- /**
- * Removes a sub-region from this region.
- * @param {Region | number | strnig} region The region to
- * remove. If a number, will remove the region at that index. If a
- * string, will remove the region with the matching name. If an
- * object, will attempt to remove that object from the Region
- */
- Region.prototype.removeRegion = function (region) {
- if (typeof region === 'number') {
- this.regions.splice(region, 1);
- } else if (typeof region === 'string') {
- this.regions = this.regions.filter(function (thisRegion) {
- return thisRegion.name !== region;
- });
- } else {
- this.regions.splice(this.regions.indexOf(region), 1);
- }
- };
-
- return Region;
- }
-);
diff --git a/platform/commonUI/regions/test/EditableRegionPolicySpec.js b/platform/commonUI/regions/test/EditableRegionPolicySpec.js
deleted file mode 100644
index 3aac19465..000000000
--- a/platform/commonUI/regions/test/EditableRegionPolicySpec.js
+++ /dev/null
@@ -1,74 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ['../src/EditableRegionPolicy'],
- function (EditableRegionPolicy) {
-
- describe("The editable region policy ", function () {
-
- var editableRegionPolicy,
- mockDomainObject,
- mockEditorCapability,
- mockBrowseRegionPart = {
- modes: 'browse'
- },
- mockEditRegionPart = {
- modes: 'edit'
- },
- mockAllModesRegionPart = {};
-
- beforeEach(function () {
- editableRegionPolicy = new EditableRegionPolicy();
-
- mockEditorCapability = jasmine.createSpyObj("editorCapability", [
- "inEditContext"
- ]);
- mockDomainObject = jasmine.createSpyObj("domainObject", [
- "hasCapability", "getCapability"
- ]);
- mockDomainObject.hasCapability.and.returnValue(true);
- mockDomainObject.getCapability.and.returnValue(mockEditorCapability);
- });
-
- it("includes only browse region parts for object not in edit mode", function () {
- mockEditorCapability.inEditContext.and.returnValue(false);
- expect(editableRegionPolicy.allow(mockBrowseRegionPart, mockDomainObject)).toBe(true);
- expect(editableRegionPolicy.allow(mockEditRegionPart, mockDomainObject)).toBe(false);
- });
-
- it("includes only edit region parts for object in edit mode", function () {
- mockEditorCapability.inEditContext.and.returnValue(true);
- expect(editableRegionPolicy.allow(mockBrowseRegionPart, mockDomainObject)).toBe(false);
- expect(editableRegionPolicy.allow(mockEditRegionPart, mockDomainObject)).toBe(true);
- });
-
- it("includes region parts with no mode specification", function () {
- mockEditorCapability.inEditContext.and.returnValue(false);
- expect(editableRegionPolicy.allow(mockAllModesRegionPart, mockDomainObject)).toBe(true);
- mockEditorCapability.inEditContext.and.returnValue(true);
- expect(editableRegionPolicy.allow(mockAllModesRegionPart, mockDomainObject)).toBe(true);
- });
-
- });
- }
-);
diff --git a/platform/commonUI/regions/test/InspectorControllerSpec.js b/platform/commonUI/regions/test/InspectorControllerSpec.js
deleted file mode 100644
index 7a6987097..000000000
--- a/platform/commonUI/regions/test/InspectorControllerSpec.js
+++ /dev/null
@@ -1,120 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ['../src/InspectorController'],
- function (InspectorController) {
-
- describe("The inspector controller ", function () {
- var mockScope,
- mockDomainObject,
- mockOpenMCT,
- mockSelection,
- mockInspectorViews,
- mockTypeDef,
- controller,
- container,
- $document = [],
- selectable = [];
-
- beforeEach(function () {
- mockTypeDef = {
- typeDef: {
- inspector: "some-key"
- }
- };
-
- mockDomainObject = jasmine.createSpyObj('domainObject', [
- 'getCapability'
- ]);
- mockDomainObject.getCapability.and.returnValue(mockTypeDef);
-
- mockScope = jasmine.createSpyObj('$scope',
- ['$on', 'selection']
- );
-
- selectable[0] = {
- context: {
- oldItem: mockDomainObject
- }
- };
-
- mockSelection = jasmine.createSpyObj("selection", [
- 'on',
- 'off',
- 'get'
- ]);
- mockSelection.get.and.returnValue(selectable);
-
- mockInspectorViews = jasmine.createSpyObj('inspectorViews', ['get']);
- mockOpenMCT = {
- selection: mockSelection,
- inspectorViews: mockInspectorViews
- };
-
- container = jasmine.createSpy('container', ['innerHTML']);
- $document[0] = jasmine.createSpyObj("$document", ['querySelectorAll']);
- $document[0].querySelectorAll.and.returnValue([container]);
-
- controller = new InspectorController(mockScope, mockOpenMCT, $document);
- });
-
- it("listens for selection change event", function () {
- expect(mockOpenMCT.selection.on).toHaveBeenCalledWith(
- 'change',
- jasmine.any(Function)
- );
-
- expect(controller.selectedItem()).toEqual(mockDomainObject);
-
- var mockItem = jasmine.createSpyObj('domainObject', [
- 'getCapability'
- ]);
- mockItem.getCapability.and.returnValue(mockTypeDef);
- selectable[0].context.oldItem = mockItem;
-
- mockOpenMCT.selection.on.calls.mostRecent().args[1](selectable);
-
- expect(controller.selectedItem()).toEqual(mockItem);
- });
-
- it("cleans up on scope destroy", function () {
- expect(mockScope.$on).toHaveBeenCalledWith(
- '$destroy',
- jasmine.any(Function)
- );
-
- mockScope.$on.calls.all()[0].args[1]();
-
- expect(mockOpenMCT.selection.off).toHaveBeenCalledWith(
- 'change',
- jasmine.any(Function)
- );
- });
-
- it("adds selection object to scope", function () {
- expect(mockScope.selection).toEqual(selectable);
- expect(controller.selectedItem()).toEqual(mockDomainObject);
- });
- });
- }
-);
diff --git a/platform/commonUI/regions/test/RegionSpec.js b/platform/commonUI/regions/test/RegionSpec.js
deleted file mode 100644
index 5b3151e8f..000000000
--- a/platform/commonUI/regions/test/RegionSpec.js
+++ /dev/null
@@ -1,103 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ['../src/Region'],
- function (Region) {
-
- describe("The region class ", function () {
-
- var region,
- part2 = new Region({'name': 'part2'});
-
- beforeEach(function () {
- region = new Region();
- region.regions = [
- new Region({name: 'part1'}),
- new Region({name: 'part3'}),
- new Region({name: 'part4'})
- ];
- });
-
- it("adding a region at a specified index adds it in that"
- + " position", function () {
-
- region.addRegion(part2, 1);
-
- expect(region.regions.length).toBe(4);
- expect(region.regions[1]).toBe(part2);
- });
-
- it("adding a region without an index adds it at the end", function () {
- var partN = new Region({'name': 'partN'});
-
- region.addRegion(partN);
-
- expect(region.regions.length).toBe(4);
- expect(region.regions[region.regions.length - 1]).toBe(partN);
- });
-
- describe("removing a region", function () {
- var partName = "part2";
-
- beforeEach(function () {
- region.regions = [
- new Region({name: 'part1'}),
- part2,
- new Region({name: 'part3'}),
- new Region({name: 'part4'})
- ];
- });
-
- it("with a string matches on region name", function () {
- expect(region.regions.length).toBe(4);
- expect(region.regions.indexOf(part2)).toBe(1);
-
- region.removeRegion(partName);
-
- expect(region.regions.length).toBe(3);
- expect(region.regions.indexOf(part2)).toBe(-1);
- });
-
- it("with a number removes by index", function () {
- expect(region.regions.length).toBe(4);
- expect(region.regions.indexOf(part2)).toBe(1);
-
- region.removeRegion(1);
-
- expect(region.regions.length).toBe(3);
- expect(region.regions.indexOf(part2)).toBe(-1);
- });
-
- it("with object matches that object", function () {
- expect(region.regions.length).toBe(4);
- expect(region.regions.indexOf(part2)).toBe(1);
-
- region.removeRegion(part2);
-
- expect(region.regions.length).toBe(3);
- expect(region.regions.indexOf(part2)).toBe(-1);
- });
- });
- });
- }
-);
diff --git a/platform/containment/README.md b/platform/containment/README.md
deleted file mode 100644
index b72c3674f..000000000
--- a/platform/containment/README.md
+++ /dev/null
@@ -1,2 +0,0 @@
-Implements support for rules which determine which objects are allowed
-to contain other objects, typically by type.
diff --git a/platform/containment/bundle.js b/platform/containment/bundle.js
deleted file mode 100644
index 6dfc7be34..000000000
--- a/platform/containment/bundle.js
+++ /dev/null
@@ -1,76 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
- "./src/CompositionPolicy",
- "./src/CompositionMutabilityPolicy",
- "./src/CompositionModelPolicy",
- "./src/ComposeActionPolicy",
- "./src/PersistableCompositionPolicy"
-], function (
- CompositionPolicy,
- CompositionMutabilityPolicy,
- CompositionModelPolicy,
- ComposeActionPolicy,
- PersistableCompositionPolicy
-) {
-
- return {
- name: "platform/containment",
- definition: {
- "extensions": {
- "policies": [
- {
- "category": "composition",
- "implementation": CompositionPolicy,
- "message": "Objects of this type cannot contain objects of that type."
- },
- {
- "category": "composition",
- "implementation": CompositionMutabilityPolicy,
- "message": "Objects of this type cannot be modified."
- },
- {
- "category": "composition",
- "implementation": CompositionModelPolicy,
- "message": "Objects of this type cannot contain other objects."
- },
- {
- "category": "action",
- "implementation": ComposeActionPolicy,
- "depends": [
- "$injector",
- "openmct"
- ],
- "message": "Objects of this type cannot contain objects of that type."
- },
- {
- "category": "composition",
- "implementation": PersistableCompositionPolicy,
- "depends": ["openmct"],
- "message": "Change cannot be made to composition of non-persistable object"
- }
- ]
- }
- }
- };
-});
diff --git a/platform/containment/src/ComposeActionPolicy.js b/platform/containment/src/ComposeActionPolicy.js
deleted file mode 100644
index b47b33982..000000000
--- a/platform/containment/src/ComposeActionPolicy.js
+++ /dev/null
@@ -1,78 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- /**
- * Restrict `compose` actions to cases where composition
- * is explicitly allowed.
- *
- * Note that this is a policy that needs the `policyService`,
- * since it's delegated to a different policy category.
- * To avoid a circular dependency, the service is obtained via
- * Angular's `$injector`.
- * @constructor
- * @memberof platform/containment
- * @implements {Policy.<Action, ActionContext>}
- */
- function ComposeActionPolicy($injector, openmct) {
- this.getPolicyService = function () {
- return $injector.get('policyService');
- };
-
- this.openmct = openmct;
- }
-
- ComposeActionPolicy.prototype.allowComposition = function (containerObject, selectedObject) {
-
- // Get a reference to the policy service if needed...
- this.policyService = this.policyService || this.getPolicyService();
-
- // ...and delegate to the composition policy
- return containerObject.getId() !== selectedObject.getId()
- && this.openmct.composition.checkPolicy(containerObject.useCapability('adapter'),
- selectedObject.useCapability('adapter'));
- };
-
- /**
- * Check whether or not a compose action should be allowed
- * in this context.
- * @returns {boolean} true if it may be allowed
- * @memberof platform/containment.ComposeActionPolicy#
- */
- ComposeActionPolicy.prototype.allow = function (candidate, context) {
- if (candidate.getMetadata().key === 'compose') {
- return this.allowComposition(
- (context || {}).domainObject,
- (context || {}).selectedObject
- );
- }
-
- return true;
- };
-
- return ComposeActionPolicy;
-
- }
-);
diff --git a/platform/containment/src/CompositionModelPolicy.js b/platform/containment/src/CompositionModelPolicy.js
deleted file mode 100644
index 11f27449e..000000000
--- a/platform/containment/src/CompositionModelPolicy.js
+++ /dev/null
@@ -1,26 +0,0 @@
-
-define(
- [],
- function () {
-
- /**
- * Policy allowing composition only for domain object types which
- * have a composition property.
- * @constructor
- * @memberof platform/containment
- * @implements {Policy.<Type, Type>}
- */
- function CompositionModelPolicy() {
- }
-
- CompositionModelPolicy.prototype.allow = function (candidate) {
- var candidateType = candidate.getCapability('type');
-
- return Array.isArray(
- (candidateType.getInitialModel() || {}).composition
- );
- };
-
- return CompositionModelPolicy;
- }
-);
diff --git a/platform/containment/src/CompositionMutabilityPolicy.js b/platform/containment/src/CompositionMutabilityPolicy.js
deleted file mode 100644
index cb0eec720..000000000
--- a/platform/containment/src/CompositionMutabilityPolicy.js
+++ /dev/null
@@ -1,45 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- /**
- * Disallow composition changes to objects which are not mutable.
- * @memberof platform/containment
- * @constructor
- * @implements {Policy.<Type, Type>}
- */
- function CompositionMutabilityPolicy() {
- }
-
- CompositionMutabilityPolicy.prototype.allow = function (candidate) {
- // Equate creatability with mutability; that is, users
- // can only modify objects of types they can create, and
- // vice versa.
- return candidate.getCapability('type').hasFeature('creation');
- };
-
- return CompositionMutabilityPolicy;
- }
-);
diff --git a/platform/containment/src/CompositionPolicy.js b/platform/containment/src/CompositionPolicy.js
deleted file mode 100644
index 53fa48933..000000000
--- a/platform/containment/src/CompositionPolicy.js
+++ /dev/null
@@ -1,71 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * This bundle implements "containment" rules, which determine which objects
- * can be contained within which other objects.
- * @namespace platform/containment
- */
-define(
- [],
- function () {
-
- /**
- * Determines whether a given object can contain a candidate child object.
- * @constructor
- * @memberof platform/containment
- * @implements {Policy.<DomainObjectImpl, DomainObjectImpl>}
- */
- function CompositionPolicy() {
- }
-
- CompositionPolicy.prototype.allow = function (parent, child) {
- var parentDef = parent.getCapability('type').getDefinition();
-
- // A parent without containment rules can contain anything.
- if (!parentDef.contains) {
- return true;
- }
-
- // If any containment rule matches context type, the candidate
- // can contain this type.
- return parentDef.contains.some(function (c) {
- // Simple containment rules are supported typeKeys.
- if (typeof c === 'string') {
- return c === child.getCapability('type').getKey();
- }
-
- // More complicated rules require context to have all specified
- // capabilities.
- if (!Array.isArray(c.has)) {
- c.has = [c.has];
- }
-
- return c.has.every(function (capability) {
- return child.hasCapability(capability);
- });
- });
- };
-
- return CompositionPolicy;
- }
-);
diff --git a/platform/containment/src/PersistableCompositionPolicy.js b/platform/containment/src/PersistableCompositionPolicy.js
deleted file mode 100644
index 389fcb8f9..000000000
--- a/platform/containment/src/PersistableCompositionPolicy.js
+++ /dev/null
@@ -1,61 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2016, 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.
- *****************************************************************************/
-
-/**
- * This bundle implements "containment" rules, which determine which objects
- * can be contained within which other objects.
- * @namespace platform/containment
- */
-define(
- ['objectUtils'],
- function (objectUtils) {
-
- function PersistableCompositionPolicy(openmct) {
- this.openmct = openmct;
- }
-
- /**
- * Only allow changes to composition if the changes can be saved. This in
- * effect prevents selection of objects from the locator that do not
- * support persistence.
- * @param parent
- * @param child
- * @returns {boolean}
- */
- PersistableCompositionPolicy.prototype.allow = function (parent) {
- // If object is in edit mode, allow composition because it is
- // part of object creation, and the object may be saved to another
- // namespace that does support persistence. The EditPersistableObjectsPolicy
- // prevents editing of objects that cannot be persisted, so we can assume that this
- // is a new object.
- if (!(parent.hasCapability('editor') && parent.getCapability('editor').isEditContextRoot())) {
- let identifier = this.openmct.objects.parseKeyString(parent.getId());
-
- return this.openmct.objects.isPersistable(identifier);
- }
-
- return true;
- };
-
- return PersistableCompositionPolicy;
- }
-);
diff --git a/platform/containment/test/ComposeActionPolicySpec.js b/platform/containment/test/ComposeActionPolicySpec.js
deleted file mode 100644
index a80ee4f37..000000000
--- a/platform/containment/test/ComposeActionPolicySpec.js
+++ /dev/null
@@ -1,95 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../src/ComposeActionPolicy"],
- function (ComposeActionPolicy) {
- xdescribe("The compose action policy", function () {
- var mockInjector,
- mockPolicyService,
- mockTypes,
- mockDomainObjects,
- mockAction,
- testContext,
- policy;
-
- beforeEach(function () {
- mockInjector = jasmine.createSpyObj('$injector', ['get']);
- mockPolicyService = jasmine.createSpyObj(
- 'policyService',
- ['allow']
- );
- mockTypes = ['a', 'b'].map(function (type) {
- var mockType = jasmine.createSpyObj('type-' + type, ['getKey']);
- mockType.getKey.and.returnValue(type);
-
- return mockType;
- });
- mockDomainObjects = ['a', 'b'].map(function (id, index) {
- var mockDomainObject = jasmine.createSpyObj(
- 'domainObject-' + id,
- ['getId', 'getCapability']
- );
- mockDomainObject.getId.and.returnValue(id);
- mockDomainObject.getCapability.and.callFake(function (c) {
- return c === 'type' && mockTypes[index];
- });
-
- return mockDomainObject;
- });
- mockAction = jasmine.createSpyObj('action', ['getMetadata']);
-
- testContext = {
- key: 'compose',
- domainObject: mockDomainObjects[0],
- selectedObject: mockDomainObjects[1]
- };
-
- mockAction.getMetadata.and.returnValue(testContext);
- mockInjector.get.and.callFake(function (service) {
- return service === 'policyService' && mockPolicyService;
- });
-
- policy = new ComposeActionPolicy(mockInjector);
- });
-
- it("defers to composition policy", function () {
- mockPolicyService.allow.and.returnValue(false);
- expect(policy.allow(mockAction, testContext)).toBeFalsy();
- mockPolicyService.allow.and.returnValue(true);
- expect(policy.allow(mockAction, testContext)).toBeTruthy();
-
- expect(mockPolicyService.allow).toHaveBeenCalledWith(
- 'composition',
- mockDomainObjects[0],
- mockDomainObjects[1]
- );
- });
-
- it("allows actions other than compose", function () {
- testContext.key = 'somethingElse';
- mockPolicyService.allow.and.returnValue(false);
- expect(policy.allow(mockAction, testContext)).toBeTruthy();
- });
- });
- }
-);
diff --git a/platform/containment/test/CompositionModelPolicySpec.js b/platform/containment/test/CompositionModelPolicySpec.js
deleted file mode 100644
index c2ebb9e45..000000000
--- a/platform/containment/test/CompositionModelPolicySpec.js
+++ /dev/null
@@ -1,30 +0,0 @@
-
-define(
- ["../src/CompositionModelPolicy"],
- function (CompositionModelPolicy) {
-
- describe("The composition model policy", function () {
- var mockObject,
- mockType,
- policy;
-
- beforeEach(function () {
- mockType = jasmine.createSpyObj('type', ['getInitialModel']);
- mockObject = {
- getCapability: function () {
- return mockType;
- }
- };
- policy = new CompositionModelPolicy();
- });
-
- it("only allows composition for types which will have a composition property", function () {
- mockType.getInitialModel.and.returnValue({});
- expect(policy.allow(mockObject)).toBeFalsy();
- mockType.getInitialModel.and.returnValue({ composition: [] });
- expect(policy.allow(mockObject)).toBeTruthy();
- });
- });
-
- }
-);
diff --git a/platform/containment/test/CompositionMutabilityPolicySpec.js b/platform/containment/test/CompositionMutabilityPolicySpec.js
deleted file mode 100644
index 900a821d5..000000000
--- a/platform/containment/test/CompositionMutabilityPolicySpec.js
+++ /dev/null
@@ -1,51 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../src/CompositionMutabilityPolicy"],
- function (CompositionMutabilityPolicy) {
-
- describe("The composition mutability policy", function () {
- var mockObject,
- mockType,
- policy;
-
- beforeEach(function () {
- mockType = jasmine.createSpyObj('type', ['hasFeature']);
- mockObject = {
- getCapability: function () {
- return mockType;
- }
- };
- policy = new CompositionMutabilityPolicy();
- });
-
- it("only allows composition for types which can be created/modified", function () {
- expect(policy.allow(mockObject)).toBeFalsy();
- mockType.hasFeature.and.returnValue(true);
- expect(policy.allow(mockObject)).toBeTruthy();
- expect(mockType.hasFeature).toHaveBeenCalledWith('creation');
- });
- });
-
- }
-);
diff --git a/platform/containment/test/CompositionPolicySpec.js b/platform/containment/test/CompositionPolicySpec.js
deleted file mode 100644
index ad29b572a..000000000
--- a/platform/containment/test/CompositionPolicySpec.js
+++ /dev/null
@@ -1,131 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../src/CompositionPolicy"],
- function (CompositionPolicy) {
- describe("Composition policy", function () {
- var mockParentObject,
- typeA,
- typeB,
- typeC,
- mockChildObject,
- policy;
-
- beforeEach(function () {
- mockParentObject = jasmine.createSpyObj('domainObject', [
- 'getCapability'
- ]);
-
- typeA = jasmine.createSpyObj(
- 'type A-- the particular kind',
- ['getKey', 'getDefinition']
- );
- typeA.getKey.and.returnValue('a');
- typeA.getDefinition.and.returnValue({
- contains: ['a']
- });
-
- typeB = jasmine.createSpyObj(
- 'type B-- anything goes',
- ['getKey', 'getDefinition']
- );
- typeB.getKey.and.returnValue('b');
- typeB.getDefinition.and.returnValue({
- contains: ['a', 'b']
- });
-
- typeC = jasmine.createSpyObj(
- 'type C-- distinguishing and interested in telemetry',
- ['getKey', 'getDefinition']
- );
- typeC.getKey.and.returnValue('c');
- typeC.getDefinition.and.returnValue({
- contains: [{has: 'telemetry'}]
- });
-
- mockChildObject = jasmine.createSpyObj(
- 'childObject',
- ['getCapability', 'hasCapability']
- );
-
- policy = new CompositionPolicy();
- });
-
- describe('enforces simple containment rules', function () {
-
- it('allows when type matches', function () {
- mockParentObject.getCapability.and.returnValue(typeA);
-
- mockChildObject.getCapability.and.returnValue(typeA);
- expect(policy.allow(mockParentObject, mockChildObject))
- .toBeTruthy();
-
- mockParentObject.getCapability.and.returnValue(typeB);
- expect(policy.allow(mockParentObject, mockChildObject))
- .toBeTruthy();
-
- mockChildObject.getCapability.and.returnValue(typeB);
- expect(policy.allow(mockParentObject, mockChildObject))
- .toBeTruthy();
- });
-
- it('disallows when type doesn\'t match', function () {
-
- mockParentObject.getCapability.and.returnValue(typeA);
- mockChildObject.getCapability.and.returnValue(typeB);
- expect(policy.allow(mockParentObject, mockChildObject))
- .toBeFalsy();
-
- mockChildObject.getCapability.and.returnValue(typeC);
- expect(policy.allow(mockParentObject, mockChildObject))
- .toBeFalsy();
- });
-
- });
-
- describe('enforces capability-based containment rules', function () {
- it('allows when object has capability', function () {
- mockParentObject.getCapability.and.returnValue(typeC);
-
- mockChildObject.hasCapability.and.returnValue(true);
- expect(policy.allow(mockParentObject, mockChildObject))
- .toBeTruthy();
- expect(mockChildObject.hasCapability)
- .toHaveBeenCalledWith('telemetry');
- });
-
- it('skips when object doesn\'t have capability', function () {
- mockChildObject.hasCapability.and.returnValue(false);
-
- mockParentObject.getCapability.and.returnValue(typeC);
-
- expect(policy.allow(mockParentObject, mockChildObject))
- .toBeFalsy();
- expect(mockChildObject.hasCapability)
- .toHaveBeenCalledWith('telemetry');
- });
- });
-
- });
- }
-);
diff --git a/platform/containment/test/PersistableCompositionPolicySpec.js b/platform/containment/test/PersistableCompositionPolicySpec.js
deleted file mode 100644
index 6f9aa74bd..000000000
--- a/platform/containment/test/PersistableCompositionPolicySpec.js
+++ /dev/null
@@ -1,85 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2016, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../src/PersistableCompositionPolicy"],
- function (PersistableCompositionPolicy) {
- describe("Persistable Composition policy", function () {
- var objectAPI;
- var mockOpenMCT;
- var persistableCompositionPolicy;
- var mockParent;
- var mockChild;
- var mockEditorCapability;
-
- beforeEach(function () {
- objectAPI = jasmine.createSpyObj('objectsAPI', [
- 'isPersistable',
- 'parseKeyString'
- ]);
-
- mockOpenMCT = {
- objects: objectAPI
- };
- mockParent = jasmine.createSpyObj('domainObject', [
- 'hasCapability',
- 'getCapability',
- 'getId'
- ]);
- mockParent.hasCapability.and.returnValue(true);
- mockParent.getId.and.returnValue('someNamespace:someId');
- mockChild = {};
- mockEditorCapability = jasmine.createSpyObj('domainObject', [
- 'isEditContextRoot'
- ]);
- mockParent.getCapability.and.returnValue(mockEditorCapability);
- persistableCompositionPolicy = new PersistableCompositionPolicy(mockOpenMCT);
- });
-
- //Parent
- // - getCapability ('editor')
- // - isEditContextRoot
- // - openMct.objects.getProvider
-
- it("Does not allow composition for objects that are not persistable", function () {
- mockEditorCapability.isEditContextRoot.and.returnValue(false);
- objectAPI.isPersistable.and.returnValue(true);
- expect(persistableCompositionPolicy.allow(mockParent, mockChild)).toBe(true);
- objectAPI.isPersistable.and.returnValue(false);
- expect(persistableCompositionPolicy.allow(mockParent, mockChild)).toBe(false);
- });
-
- it("Always allows composition of objects in edit mode to support object creation", function () {
- mockEditorCapability.isEditContextRoot.and.returnValue(true);
- objectAPI.isPersistable.and.returnValue(true);
- expect(persistableCompositionPolicy.allow(mockParent, mockChild)).toBe(true);
- expect(objectAPI.isPersistable).not.toHaveBeenCalled();
-
- mockEditorCapability.isEditContextRoot.and.returnValue(false);
- objectAPI.isPersistable.and.returnValue(true);
- expect(persistableCompositionPolicy.allow(mockParent, mockChild)).toBe(true);
- expect(objectAPI.isPersistable).toHaveBeenCalled();
- });
-
- });
- }
-);
diff --git a/platform/core/README.md b/platform/core/README.md
deleted file mode 100644
index 422ab4ce6..000000000
--- a/platform/core/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-This bundle contains core software components of Open MCT. These
-components describe MCT's information model, and are responsible for
-introducing the API and infrastructure which supports domain objects.
diff --git a/platform/core/bundle.js b/platform/core/bundle.js
deleted file mode 100644
index 97013e835..000000000
--- a/platform/core/bundle.js
+++ /dev/null
@@ -1,357 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
- "./src/objects/DomainObjectProvider",
- "./src/capabilities/CoreCapabilityProvider",
- "./src/models/StaticModelProvider",
- "./src/models/ModelAggregator",
- "./src/models/ModelCacheService",
- "./src/models/PersistedModelProvider",
- "./src/models/CachingModelDecorator",
- "./src/types/TypeProvider",
- "./src/actions/ActionProvider",
- "./src/actions/ActionAggregator",
- "./src/actions/LoggingActionDecorator",
- "./src/views/ViewProvider",
- "./src/identifiers/IdentifierProvider",
- "./src/capabilities/CompositionCapability",
- "./src/capabilities/RelationshipCapability",
- "./src/types/TypeCapability",
- "./src/actions/ActionCapability",
- "./src/views/ViewCapability",
- "./src/capabilities/PersistenceCapability",
- "./src/capabilities/MetadataCapability",
- "./src/capabilities/MutationCapability",
- "./src/capabilities/DelegationCapability",
- "./src/capabilities/InstantiationCapability",
- "./src/services/Now",
- "./src/services/Throttle",
- "./src/services/Topic",
- "./src/services/Instantiate"
-], function (
- DomainObjectProvider,
- CoreCapabilityProvider,
- StaticModelProvider,
- ModelAggregator,
- ModelCacheService,
- PersistedModelProvider,
- CachingModelDecorator,
- TypeProvider,
- ActionProvider,
- ActionAggregator,
- LoggingActionDecorator,
- ViewProvider,
- IdentifierProvider,
- CompositionCapability,
- RelationshipCapability,
- TypeCapability,
- ActionCapability,
- ViewCapability,
- PersistenceCapability,
- MetadataCapability,
- MutationCapability,
- DelegationCapability,
- InstantiationCapability,
- Now,
- Throttle,
- Topic,
- Instantiate
-) {
-
- return {
- name: "platform/core",
- definition: {
- "name": "Open MCT Core",
- "description": "Defines core concepts of Open MCT.",
- "sources": "src",
- "configuration": {
- "paths": {
- "uuid": "uuid"
- }
- },
- "extensions": {
- "components": [
- {
- "provides": "objectService",
- "type": "provider",
- "implementation": DomainObjectProvider,
- "depends": [
- "modelService",
- "instantiate"
- ]
- },
- {
- "provides": "capabilityService",
- "type": "provider",
- "implementation": CoreCapabilityProvider,
- "depends": [
- "capabilities[]",
- "$log"
- ]
- },
- {
- "provides": "modelService",
- "type": "provider",
- "implementation": StaticModelProvider,
- "depends": [
- "models[]",
- "$q",
- "$log"
- ]
- },
- {
- "provides": "modelService",
- "type": "aggregator",
- "implementation": ModelAggregator,
- "depends": [
- "$q"
- ]
- },
- {
- "provides": "modelService",
- "type": "provider",
- "implementation": PersistedModelProvider,
- "depends": [
- "persistenceService",
- "$q",
- "now",
- "PERSISTENCE_SPACE"
- ]
- },
- {
- "provides": "modelService",
- "type": "decorator",
- "implementation": CachingModelDecorator,
- "depends": [
- "cacheService"
- ]
- },
- {
- "provides": "typeService",
- "type": "provider",
- "implementation": TypeProvider,
- "depends": [
- "types[]"
- ]
- },
- {
- "provides": "actionService",
- "type": "provider",
- "implementation": ActionProvider,
- "depends": [
- "actions[]",
- "$log"
- ]
- },
- {
- "provides": "actionService",
- "type": "aggregator",
- "implementation": ActionAggregator
- },
- {
- "provides": "actionService",
- "type": "decorator",
- "implementation": LoggingActionDecorator,
- "depends": [
- "$log"
- ]
- },
- {
- "provides": "viewService",
- "type": "provider",
- "implementation": ViewProvider,
- "depends": [
- "views[]",
- "$log"
- ]
- },
- {
- "provides": "identifierService",
- "type": "provider",
- "implementation": IdentifierProvider,
- "depends": [
- "PERSISTENCE_SPACE"
- ]
- }
- ],
- "types": [
- {
- "properties": [
- {
- "control": "textfield",
- "name": "Title",
- "key": "name",
- "property": "name",
- "pattern": "\\S+",
- "required": true,
- "cssClass": "l-input-lg"
- },
- {
- "name": "Notes",
- "key": "notes",
- "property": "notes",
- "control": "textarea",
- "required": false,
- "cssClass": "l-textarea-sm"
- }
- ]
- },
- {
- "key": "root",
- "name": "Root",
- "cssClass": "icon-folder"
- }
- ],
- "capabilities": [
- {
- "key": "composition",
- "implementation": CompositionCapability,
- "depends": [
- "$injector"
- ]
- },
- {
- "key": "relationship",
- "implementation": RelationshipCapability,
- "depends": [
- "$injector"
- ]
- },
- {
- "key": "type",
- "implementation": TypeCapability,
- "depends": [
- "typeService"
- ]
- },
- {
- "key": "action",
- "implementation": ActionCapability,
- "depends": [
- "$q",
- "actionService"
- ]
- },
- {
- "key": "view",
- "implementation": ViewCapability,
- "depends": [
- "viewService"
- ]
- },
- {
- "key": "persistence",
- "implementation": PersistenceCapability,
- "depends": [
- "cacheService",
- "persistenceService",
- "identifierService",
- "notificationService",
- "$q",
- "openmct"
- ]
- },
- {
- "key": "metadata",
- "implementation": MetadataCapability
- },
- {
- "key": "mutation",
- "implementation": MutationCapability,
- "depends": [
- "topic",
- "now"
- ]
- },
- {
- "key": "delegation",
- "implementation": DelegationCapability,
- "depends": [
- "$q"
- ]
- },
- {
- "key": "instantiation",
- "implementation": InstantiationCapability,
- "depends": [
- "$injector",
- "identifierService",
- "now"
- ]
- }
- ],
- "services": [
- {
- "key": "cacheService",
- "implementation": ModelCacheService
- },
- {
- "key": "now",
- "implementation": Now
- },
- {
- "key": "throttle",
- "implementation": Throttle,
- "depends": [
- "$timeout"
- ]
- },
- {
- "key": "topic",
- "implementation": Topic,
- "depends": [
- "$log"
- ]
- },
- {
- "key": "instantiate",
- "implementation": Instantiate,
- "depends": [
- "capabilityService",
- "identifierService",
- "cacheService"
- ]
- }
- ],
- "constants": [
- {
- "key": "PERSISTENCE_SPACE",
- "value": "mct"
- }
- ],
- "licenses": [
- {
- "name": "Math.uuid.js",
- "version": "1.4.7",
- "description": "Unique identifier generation (code adapted.)",
- "author": "Robert Kieffer",
- "website": "https://github.com/broofa/node-uuid",
- "copyright": "Copyright (c) 2010-2012 Robert Kieffer",
- "license": "license-mit",
- "link": "http://opensource.org/licenses/MIT"
- }
- ]
- }
- }
- };
-});
diff --git a/platform/core/src/actions/ActionAggregator.js b/platform/core/src/actions/ActionAggregator.js
deleted file mode 100644
index 7d664f009..000000000
--- a/platform/core/src/actions/ActionAggregator.js
+++ /dev/null
@@ -1,124 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- function () {
-
- /**
- * Actions are reusable processes/behaviors performed by users within
- * the system, typically upon domain objects. Actions are commonly
- * exposed to users as menu items or buttons.
- *
- * Actions are usually registered via the `actions` extension
- * category, or (in advanced cases) via an `actionService`
- * implementation.
- *
- * @interface Action
- */
-
- /**
- * Perform the behavior associated with this action. The return type
- * may vary depending on which action has been performed; in general,
- * no return value should be expected.
- *
- * @method Action#perform
- */
-
- /**
- * Get metadata associated with this action.
- *
- * @method Action#getMetadata
- * @returns {ActionMetadata}
- */
-
- /**
- * Metadata associated with an Action. Actions of specific types may
- * extend this with additional properties.
- *
- * @typedef {Object} ActionMetadata
- * @property {string} key machine-readable identifier for this action
- * @property {string} name human-readable name for this action
- * @property {string} description human-readable description
- * @property {string} cssClass CSS class for icon
- * @property {ActionContext} context the context in which the action
- * will be performed.
- */
-
- /**
- * Provides actions that can be performed within specific contexts.
- *
- * @interface ActionService
- */
-
- /**
- * Get actions which can be performed within a certain context.
- *
- * @method ActionService#getActions
- * @param {ActionContext} context the context in which the action will
- * be performed
- * @return {Action[]} relevant actions
- */
-
- /**
- * A description of the context in which an action may occur.
- *
- * @typedef ActionContext
- * @property {DomainObject} [domainObject] the domain object being
- * acted upon.
- * @property {DomainObject} [selectedObject] the selection at the
- * time of action (e.g. the dragged object in a
- * drag-and-drop operation.)
- * @property {string} [key] the machine-readable identifier of
- * the relevant action
- * @property {string} [category] a string identifying the category
- * of action being performed
- */
-
- /**
- * The ActionAggregator makes several actionService
- * instances act as those they were one. When requesting
- * actions for a given context, results from all
- * services will be assembled and concatenated.
- *
- * @memberof platform/core
- * @constructor
- * @implements {ActionService}
- * @param {ActionService[]} actionProviders an array
- * of action services
- */
- function ActionAggregator(actionProviders) {
- this.actionProviders = actionProviders;
- }
-
- ActionAggregator.prototype.getActions = function (context) {
- // Get all actions from all providers, reduce down
- // to one array by concatenation
- return this.actionProviders.map(function (provider) {
- return provider.getActions(context);
- }).reduce(function (a, b) {
- return a.concat(b);
- }, []);
- };
-
- return ActionAggregator;
- }
-);
diff --git a/platform/core/src/actions/ActionCapability.js b/platform/core/src/actions/ActionCapability.js
deleted file mode 100644
index 09a51ee03..000000000
--- a/platform/core/src/actions/ActionCapability.js
+++ /dev/null
@@ -1,119 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Module defining ActionCapability. Created by vwoeltje on 11/10/14.
- */
-define(
- ['lodash'],
- function (_) {
-
- /**
- * The ActionCapability allows applicable Actions to be retrieved and
- * performed for specific domain objects, e.g.:
- *
- * `domainObject.getCapability("action").perform("navigate");`
- *
- * ...will initiate a navigate action upon the domain object,
- * if an action with key "navigate" is defined.
- *
- * @param {*} $q Angular's $q service, for promises
- * @param {ActionService} actionService the service from
- * which to retrieve actions.
- * @param {DomainObject} domainObject the object upon
- * which the action will be performed (also, the
- * action which exposes the capability.)
- *
- * @memberof platform/core
- * @constructor
- */
- function ActionCapability($q, actionService, domainObject) {
- this.$q = $q;
- this.actionService = actionService;
- this.domainObject = domainObject;
- }
-
- /**
- * Perform an action. This will find and perform the
- * first matching action available for the specified
- * context or key.
- *
- * @param {ActionContext|string} context the context in which
- * to perform the action; this is passed along to
- * the action service to match against available
- * actions. The "domainObject" field will automatically
- * be populated with the domain object that exposed
- * this capability. If given as a string, this will
- * be taken as the "key" field to match against
- * specific actions.
- * @returns {Promise} the result of the action that was
- * performed, or undefined if no matching action
- * was found.
- * @memberof platform/core.ActionCapability#
- */
- ActionCapability.prototype.getActions = function (context) {
- // Get all actions which are valid in this context;
- // this simply redirects to the action service,
- // but additionally adds a domainObject field.
- var baseContext;
- if (typeof context === 'string') {
- baseContext = { key: context };
- } else {
- baseContext = context || {};
- }
-
- var actionContext = Object.assign({}, baseContext);
- actionContext.domainObject = this.domainObject;
-
- return this.actionService.getActions(actionContext);
- };
-
- /**
- * Get actions which are available for this domain object,
- * in this context.
- *
- * @param {ActionContext|string} context the context in which
- * to perform the action; this is passed along to
- * the action service to match against available
- * actions. The "domainObject" field will automatically
- * be populated with the domain object that exposed
- * this capability. If given as a string, this will
- * be taken as the "key" field to match against
- * specific actions.
- * @returns {Action[]} an array of matching actions
- * @memberof platform/core.ActionCapability#
- */
- ActionCapability.prototype.perform = function (context, flag) {
- // Alias to getActions(context)[0].perform, with a
- // check for empty arrays.
- var actions = this.getActions(context);
-
- return this.$q.when(
- (actions && actions.length > 0)
- ? actions[0].perform(flag)
- : undefined
- );
- };
-
- return ActionCapability;
- }
-);
diff --git a/platform/core/src/actions/ActionProvider.js b/platform/core/src/actions/ActionProvider.js
deleted file mode 100644
index 6aa7a06f0..000000000
--- a/platform/core/src/actions/ActionProvider.js
+++ /dev/null
@@ -1,157 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Module defining ActionProvider. Created by vwoeltje on 11/7/14.
- */
-define(
- [],
- function () {
-
- /**
- * An ActionProvider (implementing ActionService) provides actions
- * that are applicable in specific contexts, chosen from a set
- * of actions exposed via extension (specifically, the "actions"
- * category of extension.)
- *
- * @memberof platform/core
- * @imeplements {ActionService}
- * @constructor
- */
- function ActionProvider(actions, $log) {
- var self = this;
-
- this.$log = $log;
-
- // Build up look-up tables
- this.actions = actions;
- this.actionsByKey = {};
- this.actionsByCategory = {};
- actions.forEach(function (Action) {
- // Get an action's category or categories
- var categories = Action.category || [];
-
- // Convert to an array if necessary
- categories = Array.isArray(categories)
- ? categories : [categories];
-
- // Store action under all relevant categories
- categories.forEach(function (category) {
- self.actionsByCategory[category] =
- self.actionsByCategory[category] || [];
- self.actionsByCategory[category].push(Action);
- });
-
- // Store action by ekey as well
- if (Action.key) {
- self.actionsByKey[Action.key] =
- self.actionsByKey[Action.key] || [];
- self.actionsByKey[Action.key].push(Action);
- }
- });
- }
-
- ActionProvider.prototype.getActions = function (actionContext) {
- var context = (actionContext || {}),
- category = context.category,
- key = context.key,
- $log = this.$log,
- candidates;
-
- // Instantiate an action; invokes the constructor and
- // additionally fills in the action's getMetadata method
- // with the extension definition (if no getMetadata
- // method was supplied.)
- function instantiateAction(Action, ctxt) {
- var action = new Action(ctxt),
- metadata;
-
- // Provide a getMetadata method that echos
- // declarative bindings, as well as context,
- // unless the action has defined its own.
- if (!action.getMetadata) {
- metadata = Object.create(Action.definition || {});
- metadata.context = ctxt;
- action.getMetadata = function () {
- return metadata;
- };
- }
-
- return action;
- }
-
- // Filter the array of actions down to those which are
- // applicable in a given context, according to the static
- // appliesTo method of given actions (if defined), and
- // instantiate those applicable actions.
- function createIfApplicable(actions, ctxt) {
- function isApplicable(Action) {
- return Action.appliesTo ? Action.appliesTo(ctxt) : true;
- }
-
- function instantiate(Action) {
- try {
- return instantiateAction(Action, ctxt);
- } catch (e) {
- $log.error([
- "Could not instantiate action",
- Action.key,
- "due to:",
- e.message
- ].join(" "));
-
- return undefined;
- }
- }
-
- function isDefined(action) {
- return action !== undefined;
- }
-
- return (actions || []).filter(isApplicable)
- .map(instantiate)
- .filter(isDefined);
- }
-
- // Match actions to the provided context by comparing "key"
- // and/or "category" parameters, if specified.
- candidates = this.actions;
- if (key) {
- candidates = this.actionsByKey[key];
- if (category) {
- candidates = candidates.filter(function (Action) {
- return Action.category === category;
- });
- }
- } else if (category) {
- candidates = this.actionsByCategory[category];
- }
-
- // Instantiate those remaining actions, with additional
- // filtering per any appliesTo methods defined on those
- // actions.
- return createIfApplicable(candidates, context);
- };
-
- return ActionProvider;
- }
-);
diff --git a/platform/core/src/actions/LoggingActionDecorator.js b/platform/core/src/actions/LoggingActionDecorator.js
deleted file mode 100644
index 5d4d1bd4a..000000000
--- a/platform/core/src/actions/LoggingActionDecorator.js
+++ /dev/null
@@ -1,80 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Module defining LoggingActionDecorator. Created by vwoeltje on 11/17/14.
- */
-define(
- [],
- function () {
-
- /**
- * The LoggingActionDecorator decorates an ActionService such that
- * the actions it exposes always emit a log message when they are
- * performed.
- *
- * @memberof platform/core
- * @constructor
- * @implements {ActionService}
- * @param $log Angular's logging service
- * @param {ActionService} actionService the decorated action service
- */
- function LoggingActionDecorator($log, actionService) {
- this.$log = $log;
- this.actionService = actionService;
- }
-
- LoggingActionDecorator.prototype.getActions = function () {
- var actionService = this.actionService,
- $log = this.$log;
-
- // Decorate the perform method of the specified action, such that
- // it emits a log message whenever performed.
- function addLogging(action) {
- var logAction = Object.create(action),
- metadata = action.getMetadata() || {},
- context = metadata.context || {},
- domainObject = context.domainObject;
-
- logAction.perform = function () {
- $log.info([
- "Performing action ",
- metadata.key,
- " upon ",
- domainObject && domainObject.getId()
- ].join(""));
-
- return action.perform.apply(action, arguments);
- };
-
- return logAction;
- }
-
- return actionService.getActions.apply(
- actionService,
- arguments
- ).map(addLogging);
- };
-
- return LoggingActionDecorator;
- }
-);
diff --git a/platform/core/src/capabilities/CompositionCapability.js b/platform/core/src/capabilities/CompositionCapability.js
deleted file mode 100644
index 9b432a6f2..000000000
--- a/platform/core/src/capabilities/CompositionCapability.js
+++ /dev/null
@@ -1,162 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Module defining CompositionCapability. Created by vwoeltje on 11/7/14.
- */
-define(
- ['./ContextualDomainObject'],
- function (ContextualDomainObject) {
-
- /**
- * Composition capability. A domain object's composition is the set of
- * domain objects it contains. This is available as an array of
- * identifiers in the model; the composition capability makes this
- * available as an array of domain object instances, which may
- * require consulting the object service (e.g. to trigger a database
- * query to retrieve the nested object models.)
- *
- * @memberof platform/core
- * @constructor
- * @implements {Capability}
- */
- function CompositionCapability($injector, domainObject) {
- // Get a reference to the object service from $injector
- this.injectObjectService = function () {
- this.objectService = $injector.get("objectService");
- };
-
- this.domainObject = domainObject;
- }
-
- /**
- * Add a domain object to the composition of this domain object.
- *
- * If no index is given, this is added to the end of the composition.
- *
- * @param {DomainObject|string} domainObject the domain object to add,
- * or simply its identifier
- * @param {number} [index] the index at which to add the object
- * @returns {Promise.<DomainObject>} a promise for the added object
- * in its new context
- */
- CompositionCapability.prototype.add = function (domainObject, index) {
- var self = this,
- id = typeof domainObject === 'string'
- ? domainObject : domainObject.getId(),
- model = self.domainObject.getModel(),
- composition = model.composition,
- oldIndex = composition.indexOf(id);
-
- // Find the object with the above id, used to contextualize
- function findObject(objects) {
- var i;
- for (i = 0; i < objects.length; i += 1) {
- if (objects[i].getId() === id) {
- return objects[i];
- }
- }
- }
-
- function contextualize(mutationResult) {
- return mutationResult && self.invoke().then(findObject);
- }
-
- function addIdToModel(objModel) {
- // Pick a specific index if needed.
- index = isNaN(index) ? composition.length : index;
- // Also, don't put past the end of the array
- index = Math.min(composition.length, index);
-
- // Remove the existing instance of the id
- if (oldIndex !== -1) {
- objModel.composition.splice(oldIndex, 1);
- }
-
- // ...and add it back at the appropriate index.
- objModel.composition.splice(index, 0, id);
- }
-
- // If no index has been specified already and the id is already
- // present, nothing to do. If the id is already at that index,
- // also nothing to do, so cancel mutation.
- if ((isNaN(index) && oldIndex !== -1) || (index === oldIndex)) {
- return contextualize(true);
- }
-
- return this.domainObject.useCapability('mutation', addIdToModel)
- .then(contextualize);
- };
-
- /**
- * Request the composition of this object.
- * @returns {Promise.<DomainObject[]>} a list of all domain
- * objects which compose this domain object.
- */
- CompositionCapability.prototype.invoke = function () {
- var domainObject = this.domainObject,
- model = domainObject.getModel(),
- ids;
-
- // Then filter out non-existent objects,
- // and wrap others (such that they expose a
- // "context" capability)
- function contextualizeObjects(objects) {
- return ids.filter(function (id) {
- return objects[id];
- }).map(function (id) {
- return new ContextualDomainObject(objects[id], domainObject);
- });
- }
-
- // Lazily acquire object service (avoids cyclical dependency)
- if (!this.objectService) {
- this.injectObjectService();
- }
-
- // Make a new request if we haven't made one, or if the
- // object has been modified.
- if (!this.lastPromise || this.lastModified !== model.modified) {
- ids = model.composition || [];
- this.lastModified = model.modified;
- // Load from the underlying object service
- this.lastPromise = this.objectService.getObjects(ids)
- .then(contextualizeObjects);
- }
-
- return this.lastPromise;
- };
-
- /**
- * Test to determine whether or not this capability should be exposed
- * by a domain object based on its model. Checks for the presence of
- * a composition field, that must be an array.
- * @param model the domain object model
- * @returns {boolean} true if this object has a composition
- */
- CompositionCapability.appliesTo = function (model) {
- return Array.isArray((model || {}).composition);
- };
-
- return CompositionCapability;
- }
-);
diff --git a/platform/core/src/capabilities/ContextCapability.js b/platform/core/src/capabilities/ContextCapability.js
deleted file mode 100644
index 16857e5f5..000000000
--- a/platform/core/src/capabilities/ContextCapability.js
+++ /dev/null
@@ -1,111 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Module defining ContextCapability. Created by vwoeltje on 11/17/14.
- */
-define(
- [],
- function () {
-
- /**
- * The `context` capability of a domain object (retrievable with
- * `domainObject.getCapability("context")`) allows an object's
- * hierarchical parents and ancestors to be retrieved (specifically,
- * those whose `composition` capability was used to access this
- * object.)
- *
- * @memberof platform/core
- * @constructor
- * @implements {Capability}
- */
- function ContextCapability(parentObject, domainObject) {
- this.parentObject = parentObject;
- this.domainObject = domainObject;
- }
-
- /**
- * Get the immediate parent of a domain object.
- *
- * A domain object may be contained in multiple places; its
- * parent (as exposed by this capability) is the domain
- * object from which this object was accessed, usually
- * by way of a `composition` capability.
- *
- * @returns {DomainObject} the immediate parent of this
- * domain object.
- */
- ContextCapability.prototype.getParent = function () {
- return this.parentObject;
- };
-
- /**
- * Get an array containing the complete direct ancestry
- * of this domain object, including the domain object
- * itself.
- *
- * A domain object may be contained in multiple places; its
- * parent and all ancestors (as exposed by this capability)
- * serve as a record of how this specific domain object
- * instance was reached.
- *
- * The first element in the returned array is the deepest
- * ancestor; subsequent elements are progressively more
- * recent ancestors, with the domain object which exposed
- * the capability occupying the last element of the array.
- *
- * @returns {DomainObject[]} the full composition ancestry
- * of the domain object which exposed this
- * capability.
- */
- ContextCapability.prototype.getPath = function () {
- var parentObject = this.parentObject,
- parentContext =
- parentObject && parentObject.getCapability('context'),
- parentPath = parentContext
- ? parentContext.getPath() : [this.parentObject];
-
- return parentPath.concat([this.domainObject]);
- };
-
- /**
- * Get the deepest ancestor available for this domain object;
- * equivalent to `getPath()[0]`.
- *
- * See notes on `getPath()` for how ancestry is defined in
- * the context of this capability.
- *
- * @returns {DomainObject} the deepest ancestor of the domain
- * object which exposed this capability.
- */
- ContextCapability.prototype.getRoot = function () {
- var parentContext = this.parentObject
- && this.parentObject.getCapability('context');
-
- return parentContext
- ? parentContext.getRoot()
- : (this.parentObject || this.domainObject);
- };
-
- return ContextCapability;
- }
-);
diff --git a/platform/core/src/capabilities/ContextualDomainObject.js b/platform/core/src/capabilities/ContextualDomainObject.js
deleted file mode 100644
index 7e5eb50ac..000000000
--- a/platform/core/src/capabilities/ContextualDomainObject.js
+++ /dev/null
@@ -1,66 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Module defining ContextualDomainObject. Created by vwoeltje on 11/18/14.
- */
-define(
- ["./ContextCapability"],
- function (ContextCapability) {
-
- /**
- * Wraps a domain object, such that it exposes a `context` capability.
- * A domain object may be contained by multiple other domain objects;
- * the `context` capability allows two instances of the same domain
- * object to be distinguished from one another based on which
- * specific instance of a containing object exposed them (by way of a
- * `composition` capability.)
- *
- * @param {DomainObject} domainObject the domain object for which
- * context should be exposed
- * @param {DomainObject} parentObject the domain object from which
- * the wrapped object was retrieved
- *
- * @memberof platform/core
- * @constructor
- * @implements {DomainObject}
- */
- function ContextualDomainObject(domainObject, parentObject) {
- // Prototypally inherit from the domain object, and
- // instantiate its context capability ahead of time.
- var contextualObject = Object.create(domainObject),
- contextCapability =
- new ContextCapability(parentObject, domainObject);
-
- // Intercept requests for a context capability.
- contextualObject.getCapability = function (name) {
- return name === "context"
- ? contextCapability
- : domainObject.getCapability.apply(this, arguments);
- };
-
- return contextualObject;
- }
-
- return ContextualDomainObject;
- }
-);
diff --git a/platform/core/src/capabilities/CoreCapabilityProvider.js b/platform/core/src/capabilities/CoreCapabilityProvider.js
deleted file mode 100644
index 5c9385561..000000000
--- a/platform/core/src/capabilities/CoreCapabilityProvider.js
+++ /dev/null
@@ -1,110 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Module defining CoreCapabilityProvider. Created by vwoeltje on 11/7/14.
- */
-define(
- [],
- function () {
-
- /**
- * A capability provides an interface with dealing with some
- * dynamic behavior associated with a domain object.
- * @interface Capability
- */
-
- /**
- * Optional; if present, will be used by `DomainObject#useCapability`
- * to simplify interaction with a specific capability. Parameters
- * and return values vary depending on capability type.
- * @method Capability#invoke
- */
-
- /**
- * Provides capabilities based on extension definitions,
- * matched to domain object models.
- *
- * @param {Array.<function(DomainObject) : Capability>} an array
- * of constructor functions for capabilities, as
- * exposed by extensions defined at the bundle level.
- *
- * @memberof platform/core
- * @constructor
- */
- function CoreCapabilityProvider(capabilities, $log) {
- // Filter by invoking the capability's appliesTo method
- function filterCapabilities(model, id) {
- return capabilities.filter(function (capability) {
- return capability.appliesTo
- ? capability.appliesTo(model, id)
- : true;
- });
- }
-
- // Package capabilities as key-value pairs
- function packageCapabilities(caps) {
- var result = {};
- caps.forEach(function (capability) {
- if (capability.key) {
- result[capability.key] =
- result[capability.key] || capability;
- } else {
- $log.warn("No key defined for capability; skipping.");
- }
- });
-
- return result;
- }
-
- function getCapabilities(model, id) {
- return packageCapabilities(filterCapabilities(model, id));
- }
-
- return {
- /**
- * Get all capabilities associated with a given domain
- * object.
- *
- * This returns a promise for an object containing key-value
- * pairs, where keys are capability names and values are
- * either:
- *
- * * Capability instances
- * * Capability constructors (which take a domain object
- * as their argument.)
- *
- *
- * @param {*} model the object model
- * @returns {Object.<string,function|Capability>} all
- * capabilities known to be valid for this model, as
- * key-value pairs
- * @memberof platform/core.CoreCapabilityProvider#
- */
- getCapabilities: getCapabilities
- };
- }
-
- return CoreCapabilityProvider;
- }
-);
-
diff --git a/platform/core/src/capabilities/DelegationCapability.js b/platform/core/src/capabilities/DelegationCapability.js
deleted file mode 100644
index 61f652f6a..000000000
--- a/platform/core/src/capabilities/DelegationCapability.js
+++ /dev/null
@@ -1,129 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Module defining DelegationCapability. Created by vwoeltje on 11/18/14.
- */
-define(
- [],
- function () {
-
- /**
- * The `delegation` capability allows a domain object to indicate
- * that it wishes to delegate responsibility for some other
- * capability to some other domain objects.
- *
- * This is specifically useful in the case of telemetry panels,
- * which delegate responsibility for the `telemetry` capability
- * to their contained objects.
- *
- * A type of domain object may indicate that it wishes to delegate
- * responsibility for one or more capabilities to the members of
- * its composition; this is done by included a `delegates` field
- * in the type's definition, which contains an array of names of
- * capabilities to be delegated.
- *
- * @param $q Angular's $q, for promises
- * @param {DomainObject} domainObject the delegating domain object
- * @memberof platform/core
- * @constructor
- * @implements {Capability}
- */
- function DelegationCapability($q, domainObject) {
- var type = domainObject.getCapability("type"),
- self = this;
-
- this.$q = $q;
- this.delegateCapabilities = {};
- this.domainObject = domainObject;
-
- // Generate set for easy lookup of capability delegation
- if (type && type.getDefinition) {
- (type.getDefinition().delegates || []).forEach(function (key) {
- self.delegateCapabilities[key] = true;
- });
- }
- }
-
- /**
- * Get the domain objects which are intended to be delegated
- * responsibility for some specific capability.
- *
- * @param {string} key the name of the delegated capability
- * @returns {DomainObject[]} the domain objects to which
- * responsibility for this capability is delegated.
- * @memberof platform/core.DelegationCapability#
- */
- DelegationCapability.prototype.getDelegates = function (key) {
- var domainObject = this.domainObject;
-
- function filterObjectsWithCapability(capability) {
- return function (objects) {
- return objects.filter(function (obj) {
- return obj.hasCapability(capability);
- });
- };
- }
-
- function promiseChildren() {
- return domainObject.useCapability('composition');
- }
-
- return this.doesDelegateCapability(key)
- ? promiseChildren().then(
- filterObjectsWithCapability(key)
- )
- : this.$q.when([]);
- };
-
- /**
- * Check if the domain object which exposed this capability
- * wishes to delegate another capability.
- *
- * @param {string} key the capability to check for
- * @returns {boolean} true if the capability is delegated
- */
- DelegationCapability.prototype.doesDelegateCapability = function (key) {
- return Boolean(this.delegateCapabilities[key]);
- };
-
- /**
- * Invoke this capability; alias of `getDelegates`, used to
- * simplify usage, e.g.:
- *
- * `domainObject.useCapability("delegation", "telemetry")`
- *
- * ...will retrieve all members of a domain object's
- * composition which have a "telemetry" capability.
- *
- * @param {string} the name of the delegated capability
- * @returns {DomainObject[]} the domain objects to which
- * responsibility for this capability is delegated.
- * @memberof platform/core.DelegationCapability#
- */
- DelegationCapability.prototype.invoke =
- DelegationCapability.prototype.getDelegates;
-
- return DelegationCapability;
-
- }
-);
diff --git a/platform/core/src/capabilities/InstantiationCapability.js b/platform/core/src/capabilities/InstantiationCapability.js
deleted file mode 100644
index f8592f613..000000000
--- a/platform/core/src/capabilities/InstantiationCapability.js
+++ /dev/null
@@ -1,85 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ['./ContextualDomainObject'],
- function (ContextualDomainObject) {
-
- /**
- * Implements the `instantiation` capability. This allows new domain
- * objects to be instantiated.
- *
- * @constructor
- * @memberof platform/core
- * @param $injector Angular's `$injector`
- * @implements {Capability}
- */
- function InstantiationCapability(
- $injector,
- identifierService,
- now,
- domainObject
- ) {
- this.$injector = $injector;
- this.identifierService = identifierService;
- this.domainObject = domainObject;
- this.now = now;
- }
-
- /**
- * Instantiate a new domain object with the provided model.
- *
- * This domain object will have been simply instantiated; it will not
- * have been persisted, nor will it have been added to the
- * composition of the object which exposed this capability.
- *
- * @param {object} the model for the new domain object
- * @returns {DomainObject} the new domain object
- */
- InstantiationCapability.prototype.instantiate = function (model) {
- var parsedId =
- this.identifierService.parse(this.domainObject.getId()),
- space = parsedId.getDefinedSpace(),
- id = this.identifierService.generate(space);
-
- model.modified = this.now();
-
- // Lazily initialize; instantiate depends on capabilityService,
- // which depends on all capabilities, including this one.
- this.instantiateFn = this.instantiateFn
- || this.$injector.get("instantiate");
-
- var newObject = this.instantiateFn(model, id);
-
- return new ContextualDomainObject(newObject, this.domainObject);
- };
-
- /**
- * Alias of `instantiate`.
- * @see {platform/core.CreationCapability#instantiate}
- */
- InstantiationCapability.prototype.invoke =
- InstantiationCapability.prototype.instantiate;
-
- return InstantiationCapability;
- }
-);
diff --git a/platform/core/src/capabilities/MetadataCapability.js b/platform/core/src/capabilities/MetadataCapability.js
deleted file mode 100644
index fa8f93516..000000000
--- a/platform/core/src/capabilities/MetadataCapability.js
+++ /dev/null
@@ -1,92 +0,0 @@
-
-define(
- ['moment'],
- function (moment) {
-
- /**
- * A piece of information about a domain object.
- * @typedef {Object} MetadataProperty
- * @property {string} name the human-readable name of this property
- * @property {string} value the human-readable value of this property,
- * for this specific domain object
- */
-
- var TIME_FORMAT = "YYYY-MM-DD HH:mm:ss";
-
- /**
- * Implements the `metadata` capability of a domain object, providing
- * properties of that object for display.
- *
- * Usage: `domainObject.useCapability("metadata")`
- *
- * ...which will return an array of objects containing `name` and
- * `value` properties describing that domain object (suitable for
- * display.)
- *
- * @param {DomainObject} domainObject the domain object whose
- * metadata is to be exposed
- * @implements {Capability}
- * @constructor
- * @memberof platform/core
- */
- function MetadataCapability(domainObject) {
- this.domainObject = domainObject;
- }
-
- /**
- * Get metadata about this object.
- * @returns {MetadataProperty[]} metadata about this object
- */
- MetadataCapability.prototype.invoke = function () {
- var domainObject = this.domainObject,
- model = domainObject.getModel();
-
- function hasDisplayableValue(metadataProperty) {
- var t = typeof metadataProperty.value;
-
- return (t === 'string' || t === 'number');
- }
-
- function formatTimestamp(timestamp) {
- return typeof timestamp === 'number'
- ? (moment.utc(timestamp).format(TIME_FORMAT) + " UTC")
- : undefined;
- }
-
- function getProperties() {
- var type = domainObject.getCapability('type');
-
- function lookupProperty(typeProperty) {
- return {
- name: typeProperty.getDefinition().name,
- value: typeProperty.getValue(model)
- };
- }
-
- return (type ? type.getProperties() : []).map(lookupProperty);
- }
-
- function getCommonMetadata() {
- var type = domainObject.getCapability('type');
-
- // Note that invalid values will be filtered out later
- return [
- {
- name: "Updated",
- value: formatTimestamp(model.modified)
- },
- {
- name: "Type",
- value: type && type.getName()
- }
- ];
- }
-
- return getProperties().concat(getCommonMetadata())
- .filter(hasDisplayableValue);
- };
-
- return MetadataCapability;
- }
-);
-
diff --git a/platform/core/src/capabilities/MutationCapability.js b/platform/core/src/capabilities/MutationCapability.js
deleted file mode 100644
index 62e82c27f..000000000
--- a/platform/core/src/capabilities/MutationCapability.js
+++ /dev/null
@@ -1,189 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Module defining MutationCapability. Created by vwoeltje on 11/12/14.
- */
-define(
- [],
- function () {
-
- var GENERAL_TOPIC = "mutation",
- TOPIC_PREFIX = "mutation:";
-
- // Utility function to overwrite a destination object
- // with the contents of a source object.
- function copyValues(destination, source) {
- // First, remove all previously-existing keys
- Object.keys(destination).forEach(function (k) {
- delete destination[k];
- });
- // Second, write all new keys
- Object.keys(source).forEach(function (k) {
- destination[k] = source[k];
- });
- }
-
- // Utility function to cast to a promise, without waiting
- // for nextTick if a value is non-promise-like.
- function fastPromise(value) {
- return (value || {}).then ? value : {
- then: function (callback) {
- return fastPromise(callback(value));
- }
- };
- }
-
- /**
- * The `mutation` capability allows a domain object's model to be
- * modified. Wrapping such modifications in calls made through
- * this capability allows these changes to be tracked (e.g. to
- * ensure that a domain object's `modified` timestamp is kept
- * up-to-date.)
- *
- * Usage:
- *
- * ```
- * domainObject.useCapability("mutation", function (model) {
- * // make changes to model here...
- * });
- * ```
- *
- * @param {Function} topic a service for creating listeners
- * @param {Function} now a service to get the current time
- * @param {DomainObject} domainObject the domain object
- * which will expose this capability
- * @memberof platform/core
- * @constructor
- * @implements {Capability}
- */
- function MutationCapability(topic, now, domainObject) {
- this.generalMutationTopic =
- topic(GENERAL_TOPIC);
- this.specificMutationTopic =
- topic(TOPIC_PREFIX + domainObject.getId());
-
- this.now = now;
- this.domainObject = domainObject;
- }
-
- /**
- * Modify the domain object's model, using a provided
- * function. This function will receive a copy of the
- * domain object's model as an argument; behavior
- * varies depending on that function's return value:
- *
- * * If no value (or undefined) is returned by the mutator,
- * the state of the model object delivered as the mutator's
- * argument will become the domain object's new model.
- * This is useful for writing code that modifies the model
- * directly.
- * * If a plain object is returned, that object will be used
- * as the domain object's new model.
- * * If boolean `false` is returned, the mutation will be
- * cancelled.
- * * If a promise is returned, its resolved value will be
- * handled as one of the above.
- *
- *
- * @param {Function} mutator the function which will make
- * changes to the domain object's model.
- * @param {number} [timestamp] timestamp to record for
- * this mutation (otherwise, system time will be
- * used)
- * @returns {Promise.<boolean>} a promise for the result
- * of the mutation; true if changes were made.
- */
- MutationCapability.prototype.mutate = function (mutator, timestamp) {
- // Get the object's model and clone it, so the
- // mutator function has a temporary copy to work with.
- var domainObject = this.domainObject,
- now = this.now,
- generalTopic = this.generalMutationTopic,
- specificTopic = this.specificMutationTopic,
- model = domainObject.getModel(),
- clone = JSON.parse(JSON.stringify(model)),
- useTimestamp = arguments.length > 1;
-
- function notifyListeners(newModel) {
- generalTopic.notify(domainObject);
- specificTopic.notify(newModel);
- }
-
- // Function to handle copying values to the actual
- function handleMutation(mutationResult) {
- // If mutation result was undefined, just use
- // the clone; this allows the mutator to omit return
- // values and just change the model directly.
- var result = mutationResult || clone;
-
- // Allow mutators to change their mind by
- // returning false.
- if (mutationResult !== false) {
- // Copy values if result was a different object
- // (either our clone or some other new thing)
- let modelHasChanged = _.isEqual(model, result) === false;
- if (modelHasChanged) {
- copyValues(model, result);
- }
-
- if (modelHasChanged
- || (useTimestamp !== undefined)
- || (model.modified === undefined)) {
- model.modified = useTimestamp ? timestamp : now();
- }
-
- notifyListeners(model);
- }
-
- // Report the result of the mutation
- return mutationResult !== false;
- }
-
- // Invoke the provided mutator, then make changes to
- // the underlying model (if applicable.)
- return fastPromise(mutator(clone)).then(handleMutation);
- };
-
- /**
- * Listen for mutations of this domain object's model.
- * The provided listener will be invoked with the domain
- * object's new model after any changes. To stop listening,
- * invoke the function returned by this method.
- * @param {Function} listener function to call on mutation
- * @returns {Function} a function to stop listening
- * @memberof platform/core.MutationCapability#
- */
- MutationCapability.prototype.listen = function (listener) {
- return this.specificMutationTopic.listen(listener);
- };
-
- /**
- * Alias of `mutate`, used to support useCapability.
- */
- MutationCapability.prototype.invoke =
- MutationCapability.prototype.mutate;
-
- return MutationCapability;
- }
-);
-
diff --git a/platform/core/src/capabilities/PersistenceCapability.js b/platform/core/src/capabilities/PersistenceCapability.js
deleted file mode 100644
index 64842a595..000000000
--- a/platform/core/src/capabilities/PersistenceCapability.js
+++ /dev/null
@@ -1,206 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(["objectUtils"],
- function (objectUtils) {
-
- /**
- * Defines the `persistence` capability, used to trigger the
- * writing of changes to a domain object to an underlying
- * persistence store.
- *
- * @param {PersistenceService} persistenceService the underlying
- * provider of persistence capabilities.
- * @param {string} space the name of the persistence space to
- * use (this is an arbitrary string, useful in principle
- * for distinguishing different persistence stores from
- * one another.)
- * @param {DomainObject} the domain object which shall expose
- * this capability
- *
- * @memberof platform/core
- * @constructor
- * @implements {Capability}
- */
- function PersistenceCapability(
- cacheService,
- persistenceService,
- identifierService,
- notificationService,
- $q,
- openmct,
- domainObject
- ) {
- // Cache modified timestamp
- this.modified = domainObject.getModel().modified;
-
- this.domainObject = domainObject;
- this.cacheService = cacheService;
- this.identifierService = identifierService;
- this.persistenceService = persistenceService;
- this.notificationService = notificationService;
- this.$q = $q;
- this.openmct = openmct;
- }
-
- /**
- * Checks if the value returned is falsey, and if so returns a
- * rejected promise
- */
- function rejectIfFalsey(value, $q) {
- if (!value) {
- return Promise.reject("Error persisting object");
- } else {
- return value;
- }
- }
-
- function formatError(error) {
- if (error && error.message) {
- return error.message;
- } else if (error && typeof error === "string") {
- return error;
- } else {
- return "unknown error";
- }
- }
-
- /**
- * Display a notification message if an error has occurred during
- * persistence.
- */
- function notifyOnError(error, domainObject, notificationService, $q) {
- var errorMessage = "Unable to persist " + domainObject.getModel().name;
- if (error) {
- errorMessage += ": " + formatError(error);
- }
-
- notificationService.error({
- title: "Error persisting " + domainObject.getModel().name,
- hint: errorMessage,
- dismissable: true
- });
-
- return Promise.reject(error);
- }
-
- /**
- * Persist any changes which have been made to this
- * domain object's model.
- * @returns {Promise} a promise which will be resolved
- * if persistence is successful, and rejected
- * if not.
- */
- PersistenceCapability.prototype.persist = function () {
- var self = this,
- domainObject = this.domainObject;
-
- const identifier = {
- namespace: this.getSpace(),
- key: this.getKey()
- };
-
- let newStyleObject = objectUtils.toNewFormat(domainObject.getModel(), identifier);
-
- return this.openmct.objects
- .save(newStyleObject)
- .then(function (result) {
- return rejectIfFalsey(result, self.$q);
- }).catch(function (error) {
- return notifyOnError(error, domainObject, self.notificationService, self.$q);
- });
- };
-
- /**
- * Update this domain object to match the latest from
- * persistence.
- * @returns {Promise} a promise which will be resolved
- * when the update is complete
- */
- PersistenceCapability.prototype.refresh = function () {
- var domainObject = this.domainObject;
- var $q = this.$q;
-
- // Update a domain object's model upon refresh
- function updateModel(model) {
- if (model === undefined) {
- //Get failed, reject promise
- return $q.reject('Got empty object model');
- } else {
- var modified = model.modified;
-
- return domainObject.useCapability("mutation", function () {
- return model;
- }, modified);
-
- }
- }
-
- if (domainObject.getModel().persisted === undefined) {
- return this.$q.when(true);
- }
-
- return this.persistenceService.readObject(
- this.getSpace(),
- this.getKey()
- ).then(updateModel);
- };
-
- /**
- * Get the space in which this domain object is persisted;
- * this is useful when, for example, decided which space a
- * newly-created domain object should be persisted to (by
- * default, this should be the space of its containing
- * object.)
- *
- * @returns {string} the name of the space which should
- * be used to persist this object
- */
- PersistenceCapability.prototype.getSpace = function () {
- var id = this.domainObject.getId();
-
- return this.identifierService.parse(id).getSpace();
- };
-
- /**
- * Check if this domain object has been persisted at some
- * point.
- * @returns {boolean} true if the object has been persisted
- */
- PersistenceCapability.prototype.persisted = function () {
- return this.domainObject.getModel().persisted !== undefined;
- };
-
- /**
- * Get the key for this domain object in the given space.
- *
- * @returns {string} the key of the object in it's space.
- */
- PersistenceCapability.prototype.getKey = function () {
- var id = this.domainObject.getId();
-
- return this.identifierService.parse(id).getKey();
- };
-
- return PersistenceCapability;
- }
-);
diff --git a/platform/core/src/capabilities/RelationshipCapability.js b/platform/core/src/capabilities/RelationshipCapability.js
deleted file mode 100644
index 4ab11e4c8..000000000
--- a/platform/core/src/capabilities/RelationshipCapability.js
+++ /dev/null
@@ -1,128 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- /**
- * Relationship capability. Describes a domain objects relationship
- * to other domain objects within the system, and provides a way to
- * access related objects.
- *
- * For most cases, this is not the capability to use; the
- * `composition` capability describes the more general relationship
- * between objects typically seen (e.g. in the tree.) This capability
- * is instead intended for the more unusual case of relationships
- * which are not intended to appear in the tree, but are instead
- * intended only for special, limited usage.
- *
- * @memberof platform/core
- * @constructor
- * @implements {Capability}
- */
- function RelationshipCapability($injector, domainObject) {
- // Get a reference to the object service from $injector
- this.injectObjectService = function () {
- this.objectService = $injector.get("objectService");
- };
-
- this.lastPromise = {};
- this.domainObject = domainObject;
- }
-
- /**
- * List all types of relationships exposed by this
- * object.
- * @returns {string[]} a list of all relationship types
- */
- RelationshipCapability.prototype.listRelationships = function listRelationships() {
- var relationships =
- (this.domainObject.getModel() || {}).relationships || {};
-
- // Check if this key really does expose an array of ids
- // (to filter out malformed relationships)
- function isArray(key) {
- return Array.isArray(relationships[key]);
- }
-
- return Object.keys(relationships).filter(isArray).sort();
- };
-
- /**
- * Request related objects, with a given relationship type.
- * This will typically require asynchronous lookup, so this
- * returns a promise.
- * @param {string} key the type of relationship
- * @returns {Promise.<DomainObject[]>} a promise for related
- * domain objects
- */
- RelationshipCapability.prototype.getRelatedObjects = function (key) {
- var model = this.domainObject.getModel(),
- ids;
-
- // Package objects as an array
- function packageObject(objects) {
- return ids.map(function (id) {
- return objects[id];
- }).filter(function (obj) {
- return obj;
- });
- }
-
- // Clear cached promises if modification has occurred
- if (this.lastModified !== model.modified) {
- this.lastPromise = {};
- this.lastModified = model.modified;
- }
-
- // Make a new request if needed
- if (!this.lastPromise[key]) {
- ids = (model.relationships || {})[key] || [];
- this.lastModified = model.modified;
- // Lazily initialize object service now that we need it
- if (!this.objectService) {
- this.injectObjectService();
- }
-
- // Load from the underlying object service
- this.lastPromise[key] = this.objectService.getObjects(ids)
- .then(packageObject);
- }
-
- return this.lastPromise[key];
- };
-
- /**
- * Test to determine whether or not this capability should be exposed
- * by a domain object based on its model. Checks for the presence of
- * a `relationships` field, that must be an object.
- * @param model the domain object model
- * @returns {boolean} true if this object has relationships
- */
- RelationshipCapability.appliesTo = function (model) {
- return Boolean((model || {}).relationships);
- };
-
- return RelationshipCapability;
- }
-);
diff --git a/platform/core/src/identifiers/Identifier.js b/platform/core/src/identifiers/Identifier.js
deleted file mode 100644
index 7f8065c3f..000000000
--- a/platform/core/src/identifiers/Identifier.js
+++ /dev/null
@@ -1,84 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- var SEPARATOR = ":";
-
- /**
- * Provides an interface for interpreting domain object identifiers;
- * in particular, parses out persistence space/key pairs associated
- * with the domain object.
- *
- * @memberof platform/core
- * @constructor
- * @param {string} id the domain object identifier
- * @param {string} defaultSpace the persistence space to use if
- * one is not encoded in the identifier
- */
- function Identifier(id, defaultSpace) {
- var separatorIndex = id.indexOf(SEPARATOR);
-
- if (separatorIndex > -1) {
- this.key = id.substring(separatorIndex + 1);
- this.space = id.substring(0, separatorIndex);
- this.definedSpace = this.space;
- } else {
- this.key = id;
- this.space = defaultSpace;
- this.definedSpace = undefined;
- }
- }
-
- /**
- * Get the key under which the identified domain object's model
- * should be persisted, within its persistence space.
- * @returns {string} the key within its persistence space
- */
- Identifier.prototype.getKey = function () {
- return this.key;
- };
-
- /**
- * Get the space in which the identified domain object's model should
- * be persisted.
- * @returns {string} the persistence space
- */
- Identifier.prototype.getSpace = function () {
- return this.space;
- };
-
- /**
- * Get the persistence space, if any, which has been explicitly
- * encoded in this domain object's identifier. Returns undefined
- * if no such space has been specified.
- * @returns {string} the persistence space, or undefined
- */
- Identifier.prototype.getDefinedSpace = function () {
- return this.definedSpace;
- };
-
- return Identifier;
- }
-);
diff --git a/platform/core/src/identifiers/IdentifierProvider.js b/platform/core/src/identifiers/IdentifierProvider.js
deleted file mode 100644
index 82c20b219..000000000
--- a/platform/core/src/identifiers/IdentifierProvider.js
+++ /dev/null
@@ -1,65 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["uuid", "./Identifier"],
- function (uuid, Identifier) {
-
- /**
- * Parses and generates domain object identifiers.
- * @param {string} defaultSpace the default persistence space
- * @constructor
- * @memberof {platform/core}
- */
- function IdentifierProvider(defaultSpace) {
- this.defaultSpace = defaultSpace;
- }
-
- /**
- * Generate a new domain object identifier. A persistence space
- * may optionally be included; if not specified, no space will
- * be encoded into the identifier.
- * @param {string} [space] the persistence space to encode
- * in this identifier
- * @returns {string} a new domain object identifier
- */
- IdentifierProvider.prototype.generate = function (space) {
- var id = uuid();
- if (space !== undefined) {
- id = space + ":" + id;
- }
-
- return id;
- };
-
- /**
- * Parse a domain object identifier to examine its component
- * parts (e.g. its persistence space.)
- * @returns {platform/core.Identifier} the parsed identifier
- */
- IdentifierProvider.prototype.parse = function (id) {
- return new Identifier(id, this.defaultSpace);
- };
-
- return IdentifierProvider;
- }
-);
diff --git a/platform/core/src/models/CachingModelDecorator.js b/platform/core/src/models/CachingModelDecorator.js
deleted file mode 100644
index 59f943547..000000000
--- a/platform/core/src/models/CachingModelDecorator.js
+++ /dev/null
@@ -1,65 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- /**
- * The caching model decorator maintains a cache of loaded domain
- * object models, and ensures that duplicate models for the same
- * object are not provided.
- * @memberof platform/core
- * @constructor
- * @param {ModelService} modelService this service to decorate
- * @implements {ModelService}
- */
- function CachingModelDecorator(cacheService, modelService) {
- this.cacheService = cacheService;
- this.modelService = modelService;
- }
-
- CachingModelDecorator.prototype.getModels = function (ids) {
- var loadFromCache = ids.filter(function cached(id) {
- return this.cacheService.has(id);
- }, this),
- loadFromService = ids.filter(function notCached(id) {
- return !this.cacheService.has(id);
- }, this);
-
- if (!loadFromCache.length) {
- return this.modelService.getModels(loadFromService);
- }
-
- return this.modelService.getModels(loadFromService)
- .then(function (modelResults) {
- loadFromCache.forEach(function (id) {
- modelResults[id] = this.cacheService.get(id);
- }, this);
-
- return modelResults;
- }.bind(this));
- };
-
- return CachingModelDecorator;
- }
-);
diff --git a/platform/core/src/models/ModelAggregator.js b/platform/core/src/models/ModelAggregator.js
deleted file mode 100644
index aa1ce242d..000000000
--- a/platform/core/src/models/ModelAggregator.js
+++ /dev/null
@@ -1,99 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Module defining ModelAggregator. Created by vwoeltje on 11/7/14.
- */
-define(
- [],
- function () {
-
- /**
- * Allow domain object models to be looked up by their identifiers.
- *
- * @interface ModelService
- */
-
- /**
- * Get domain object models.
- *
- * This may provide either a superset or a subset of the models
- * requested. Absence of a model means it does not exist within
- * this service instance.
- *
- * @method ModelService#getModels
- * @param {string[]} ids identifiers for models desired.
- * @returns {Promise.<Object>} a promise for an object mapping
- * string identifiers to domain object models.
- */
-
- /**
- * Allows multiple services which provide models for domain objects
- * to be treated as one.
- *
- * @memberof platform/core
- * @constructor
- * @implements {ModelService}
- * @param $q Angular's $q, for promises
- * @param {ModelService[]} providers the model providers to be
- * aggregated
- */
- function ModelAggregator($q, providers) {
- this.providers = providers;
- this.$q = $q;
- }
-
- // Pick a domain object model to use, favoring the one
- // with the most recent timestamp
- function pick(a, b) {
- var aModified = (a || {}).modified || Number.NEGATIVE_INFINITY,
- bModified = (b || {}).modified || Number.NEGATIVE_INFINITY;
-
- return (aModified > bModified) ? a : (b || a);
- }
-
- // Merge results from multiple providers into one
- // large result object.
- function mergeModels(provided, ids) {
- var result = {};
- ids.forEach(function (id) {
- provided.forEach(function (models) {
- if (models[id]) {
- result[id] = pick(result[id], models[id]);
- }
- });
- });
-
- return result;
- }
-
- ModelAggregator.prototype.getModels = function (ids) {
- return this.$q.all(this.providers.map(function (provider) {
- return provider.getModels(ids);
- })).then(function (provided) {
- return mergeModels(provided, ids);
- });
- };
-
- return ModelAggregator;
- }
-);
diff --git a/platform/core/src/models/ModelCacheService.js b/platform/core/src/models/ModelCacheService.js
deleted file mode 100644
index e2ca5e4e4..000000000
--- a/platform/core/src/models/ModelCacheService.js
+++ /dev/null
@@ -1,85 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([], function () {
-
- /**
- * Provides a cache for domain object models which exist in memory,
- * but may or may not exist in backing persistence stores.
- * @constructor
- * @memberof platform/core
- */
- function ModelCacheService() {
- this.cache = {};
- }
-
- /**
- * Put a domain object model in the cache.
- * @param {string} id the domain object's identifier
- * @param {object} model the domain object's model
- */
- ModelCacheService.prototype.put = function (id, model) {
- this.cache[id] = model;
- };
-
- /**
- * Retrieve a domain object model from the cache.
- * @param {string} id the domain object's identifier
- * @returns {object} the domain object's model
- */
- ModelCacheService.prototype.get = function (id) {
- return this.cache[id];
- };
-
- /**
- * Check if a domain object model is in the cache.
- * @param {string} id the domain object's identifier
- * @returns {boolean} true if present; false if not
- */
- ModelCacheService.prototype.has = function (id) {
- return Object.prototype.hasOwnProperty.call(this.cache, id);
- };
-
- /**
- * Remove a domain object model from the cache.
- * @param {string} id the domain object's identifier
- */
- ModelCacheService.prototype.remove = function (id) {
- delete this.cache[id];
- };
-
- /**
- * Retrieve all cached domain object models. These are given
- * as an object containing key-value pairs, where keys are
- * domain object identifiers and values are domain object models.
- * @returns {object} all domain object models
- */
- ModelCacheService.prototype.all = function () {
- return this.cache;
- };
-
- ModelCacheService.prototype.flush = function () {
- this.cache = {};
- };
-
- return ModelCacheService;
-});
diff --git a/platform/core/src/models/PersistedModelProvider.js b/platform/core/src/models/PersistedModelProvider.js
deleted file mode 100644
index 23f05371a..000000000
--- a/platform/core/src/models/PersistedModelProvider.js
+++ /dev/null
@@ -1,136 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Module defining PersistedModelProvider. Created by vwoeltje on 11/12/14.
- */
-define(
- [],
- function () {
-
- /**
- * A model service which reads domain object models from an external
- * persistence service.
- *
- * Identifiers will be interpreted as follows:
- * * If no colon is present, the model will be read from the default
- * persistence space.
- * * If a colon is present, everything before the first colon will be
- * taken to refer to the persistence space, and everything after
- * will be taken to be that model's key within this space. (If
- * no such space exists within the `persistenceService`, that
- * identifier will simply be ignored.)
- *
- * @memberof platform/core
- * @constructor
- * @implements {ModelService}
- * @param {PersistenceService} persistenceService the service in which
- * domain object models are persisted.
- * @param $q Angular's $q service, for working with promises
- * @param {function} now a function which provides the current time
- * @param {string} space the name of the persistence space(s)
- * from which models should be retrieved by default
- */
- function PersistedModelProvider(persistenceService, $q, now, space) {
- this.persistenceService = persistenceService;
- this.$q = $q;
- this.now = now;
- this.defaultSpace = space;
- }
-
- PersistedModelProvider.prototype.getModels = function (ids) {
- var persistenceService = this.persistenceService,
- $q = this.$q,
- now = this.now,
- defaultSpace = this.defaultSpace,
- parsedIds;
-
- // Load a single object model from any persistence spaces
- function loadModel(parsedId) {
- return persistenceService
- .readObject(parsedId.space, parsedId.key);
- }
-
- // Ensure that models read from persistence have some
- // sensible timestamp indicating they've been persisted.
- function addPersistedTimestamp(model) {
- if (model && (model.persisted === undefined)) {
- model.persisted = model.modified !== undefined
- ? model.modified : now();
- }
-
- return model;
- }
-
- // Package the result as id->model
- function packageResult(parsedIdsToPackage, models) {
- var result = {};
- parsedIdsToPackage.forEach(function (parsedId, index) {
- var id = parsedId.id;
- if (models[index]) {
- result[id] = models[index];
- }
- });
-
- return result;
- }
-
- function loadModels(parsedIdsToLoad) {
- return $q.all(parsedIdsToLoad.map(loadModel))
- .then(function (models) {
- return packageResult(
- parsedIdsToLoad,
- models.map(addPersistedTimestamp)
- );
- });
- }
-
- function restrictToSpaces(spaces) {
- return parsedIds.filter(function (parsedId) {
- return spaces.indexOf(parsedId.space) !== -1;
- });
- }
-
- parsedIds = ids.map(function (id) {
- var parts = id.split(":");
-
- return (parts.length > 1)
- ? {
- id: id,
- space: parts[0],
- key: parts.slice(1).join(":")
- }
- : {
- id: id,
- space: defaultSpace,
- key: id
- };
- });
-
- return persistenceService.listSpaces()
- .then(restrictToSpaces)
- .then(loadModels);
- };
-
- return PersistedModelProvider;
- }
-);
diff --git a/platform/core/src/models/StaticModelProvider.js b/platform/core/src/models/StaticModelProvider.js
deleted file mode 100644
index 9da70ec52..000000000
--- a/platform/core/src/models/StaticModelProvider.js
+++ /dev/null
@@ -1,71 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Module defining StaticModelProvider. Created by vwoeltje on 11/7/14.
- */
-define(
- [],
- function () {
-
- /**
- * Loads static models, provided as declared extensions of bundles.
- * @memberof platform/core
- * @constructor
- */
- function StaticModelProvider(models, $q, $log) {
- var modelMap = {};
-
- function addModelToMap(model) {
- // Skip models which don't look right
- if (typeof model !== 'object'
- || typeof model.id !== 'string'
- || typeof model.model !== 'object') {
- $log.warn([
- "Skipping malformed domain object model exposed by ",
- ((model || {}).bundle || {}).path
- ].join(""));
- } else {
- modelMap[model.id] = model.model;
- }
- }
-
- // Prepopulate maps with models to make subsequent lookup faster.
- models.forEach(addModelToMap);
-
- this.modelMap = modelMap;
- this.$q = $q;
- }
-
- StaticModelProvider.prototype.getModels = function (ids) {
- var modelMap = this.modelMap,
- result = {};
- ids.forEach(function (id) {
- result[id] = modelMap[id];
- });
-
- return this.$q.when(result);
- };
-
- return StaticModelProvider;
- }
-);
diff --git a/platform/core/src/objects/DomainObjectImpl.js b/platform/core/src/objects/DomainObjectImpl.js
deleted file mode 100644
index 0b9fd0b79..000000000
--- a/platform/core/src/objects/DomainObjectImpl.js
+++ /dev/null
@@ -1,142 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Module defining DomainObject. Created by vwoeltje on 11/7/14.
- */
-define(
- [],
- function () {
-
- /**
- * A domain object is an entity of interest to the user.
- *
- * @interface DomainObject
- */
-
- /**
- * Get the unique identifier for this domain object.
- *
- * @method DomainObject#getId
- * @return {string} the domain object's unique identifier
- */
-
- /**
- * Get the domain object's model. This is useful to
- * directly look up known properties of an object, but
- * direct modification of a returned model is generally
- * discouraged and may result in errors. Instead, an
- * object's `mutation` capability should be used.
- *
- * @method DomainObject#getModel
- * @return {object} the domain object's persistent state
- */
-
- /**
- * Get a capability associated with this object.
- * Capabilities are looked up by string identifiers;
- * prior knowledge of a capability's interface is
- * necessary.
- *
- * @method DomainObject#getCapability
- * @param {string} key the identifier for the capability
- * @return {Capability} the named capability, or undefined
- * if not present.
- */
-
- /**
- * Check if this domain object supports a capability
- * with the provided name.
- *
- * @method DomainObject#hasCapability
- * @param {string} key the identifier for the capability
- * @return {boolean} true if this domain object has this capability
- */
-
- /**
- * Use a capability of an object; the behavior of this method
- * depends on the interface of the capability, and whether
- * or not it is present.
- *
- * * If the capability is not present for this object,
- * no operation occurs.
- * * If the capability is present and has an `invoke` method,
- * that method is called with any additional arguments
- * provided, and its return value is returned.
- * * If the capability is present but has no `invoke` method,
- * this capability itself is returned.
- *
- * @method DomainObject#useCapability
- * @param {string} name the name of the capability to invoke
- * @param {...*} [arguments] to pass to the invocation
- * @returns {*|Capability} the result of invocation (see description)
- */
-
- /**
- * Construct a new domain object with the specified
- * identifier, model, and capabilities.
- *
- * @param {string} id the object's unique identifier
- * @param {object} model the "JSONifiable" state of the object
- * @param {Object.<string, Capability>|function} capabilities all
- * capabilities to be exposed by this object
- * @memberof platform/core
- * @constructor
- */
- function DomainObjectImpl(id, model, capabilities) {
- this.id = id;
- this.model = model;
- this.capabilities = capabilities;
- }
-
- DomainObjectImpl.prototype.getId = function () {
- return this.id;
- };
-
- DomainObjectImpl.prototype.getModel = function () {
- return this.model;
- };
-
- DomainObjectImpl.prototype.getCapability = function (name) {
- var capability = this.capabilities[name];
-
- return typeof capability === 'function'
- ? capability(this) : capability;
- };
-
- DomainObjectImpl.prototype.hasCapability = function (name) {
- return this.getCapability(name) !== undefined;
- };
-
- DomainObjectImpl.prototype.useCapability = function (name) {
- // Get tail of args to pass to invoke
- var args = Array.prototype.slice.apply(arguments, [1]),
- capability = this.getCapability(name);
-
- return (capability && capability.invoke)
- ? capability.invoke.apply(capability, args)
- : capability;
- };
-
- return DomainObjectImpl;
- }
-);
diff --git a/platform/core/src/objects/DomainObjectProvider.js b/platform/core/src/objects/DomainObjectProvider.js
deleted file mode 100644
index 40d865a44..000000000
--- a/platform/core/src/objects/DomainObjectProvider.js
+++ /dev/null
@@ -1,93 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * This bundle implements core components of Open MCT's service
- * infrastructure and information model.
- * @namespace platform/core
- */
-define(
- [],
- function () {
-
- /**
- * Provides instances of domain objects, as retrieved by their
- * identifiers.
- *
- * @interface ObjectService
- */
-
- /**
- * Get a set of objects associated with a list of identifiers.
- * The provided result may contain a subset or a superset of
- * the total number of objects.
- *
- * @method ObjectService#getObjects
- * @param {string[]} ids the identifiers for domain objects
- * of interest.
- * @return {Promise<object<string, DomainObject>>} a promise
- * for an object containing key-value pairs, where keys
- * are string identifiers for domain objects, and
- * values are the corresponding domain objects themselves.
- */
-
- /**
- * Construct a new provider for domain objects.
- *
- * @param {ModelService} modelService the service which shall
- * provide models (persistent state) for domain objects
- * @param {Function} instantiate a service to instantiate new
- * domain object instances
- * @param $q Angular's $q, for promise consolidation
- * @memberof platform/core
- * @constructor
- */
- function DomainObjectProvider(modelService, instantiate) {
- this.modelService = modelService;
- this.instantiate = instantiate;
- }
-
- DomainObjectProvider.prototype.getObjects = function getObjects(ids) {
- var modelService = this.modelService,
- instantiate = this.instantiate;
-
- // Assemble the results from the model service and the
- // capability service into one value, suitable to return
- // from this service.
- function assembleResult(models) {
- var result = {};
- ids.forEach(function (id) {
- if (models[id]) {
- // Create the domain object
- result[id] = instantiate(models[id], id);
- }
- });
-
- return result;
- }
-
- return modelService.getModels(ids).then(assembleResult);
- };
-
- return DomainObjectProvider;
- }
-);
diff --git a/platform/core/src/services/Instantiate.js b/platform/core/src/services/Instantiate.js
deleted file mode 100644
index c32f74e89..000000000
--- a/platform/core/src/services/Instantiate.js
+++ /dev/null
@@ -1,61 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ['../objects/DomainObjectImpl'],
- function (DomainObjectImpl) {
-
- /**
- * The `instantiate` service allows new domain object instances to be
- * created. These objects are not persisted to any back-end or
- * placed anywhere in the object hierarchy by default.
- *
- * Usage: `instantiate(model, [id])`
- *
- * ...returns a new instance of a domain object with the specified
- * model. An identifier may be provided; if omitted, one will be
- * generated instead.
- *
- * @constructor
- * @memberof platform/core
- * @param {CapabilityService} capabilityService the service which will
- * provide instantiated domain objects with their capabilities
- * @param {IdentifierService} identifierService service to generate
- * new identifiers
- */
- function Instantiate(
- capabilityService,
- identifierService,
- cacheService
- ) {
- return function (model, id) {
- var capabilities = capabilityService.getCapabilities(model);
- id = id || identifierService.generate();
- cacheService.put(id, model);
-
- return new DomainObjectImpl(id, model, capabilities);
- };
- }
-
- return Instantiate;
- }
-);
diff --git a/platform/core/src/services/Now.js b/platform/core/src/services/Now.js
deleted file mode 100644
index d3cd6e87a..000000000
--- a/platform/core/src/services/Now.js
+++ /dev/null
@@ -1,48 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- /**
- * Defines the `now` service, which is a simple wrapper upon
- * `Date.now()` which can be injected to support testability.
- *
- * @returns {Function} a function which returns current system time
- * @memberof platform/core
- */
- function Now() {
- /**
- * Get the current time.
- * @returns {number} current time, in milliseconds since
- * 1970-01-01 00:00:00Z
- * @memberof platform/core.Now#
- */
- return function () {
- return Date.now();
- };
- }
-
- return Now;
- }
-);
diff --git a/platform/core/src/services/Throttle.js b/platform/core/src/services/Throttle.js
deleted file mode 100644
index 22d28b785..000000000
--- a/platform/core/src/services/Throttle.js
+++ /dev/null
@@ -1,93 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- /**
- * Throttler for function executions, registered as the `throttle`
- * service.
- *
- * Usage:
- *
- * throttle(fn, delay, [apply])
- *
- * Returns a function that, when invoked, will invoke `fn` after
- * `delay` milliseconds, only if no other invocations are pending.
- * The optional argument `apply` determines whether or not a
- * digest cycle should be triggered.
- *
- * The returned function will itself return a `Promise` which will
- * resolve to the returned value of `fn` whenever that is invoked.
- *
- * In cases where arguments are provided, only the most recent
- * set of arguments will be passed on to the throttled function
- * at the time it is executed.
- *
- * @returns {Function}
- * @memberof platform/core
- */
- function Throttle($timeout) {
- /**
- * Throttle this function.
- * @param {Function} fn the function to throttle
- * @param {number} [delay] the delay, in milliseconds, before
- * executing this function; defaults to 0.
- * @param {boolean} apply true if a `$apply` call should be
- * invoked after this function executes; defaults to
- * `false`.
- * @memberof platform/core.Throttle#
- */
- return function (fn, delay, apply) {
- var promise,
- args = [];
-
- function invoke() {
- // Clear the active timeout so a new one starts next time.
- promise = undefined;
-
- // Invoke the function with the latest supplied arguments.
- return fn.apply(null, args);
- }
-
- // Defaults
- delay = delay || 0;
- apply = apply || false;
-
- return function () {
- // Store arguments from this invocation
- args = Array.prototype.slice.apply(arguments, [0]);
- // Start a timeout if needed
- promise = promise || $timeout(invoke, delay, apply);
-
- // Return whichever timeout is active (to get
- // a promise for the results of fn)
- return promise;
- };
- };
- }
-
- return Throttle;
- }
-);
-
diff --git a/platform/core/src/services/Topic.js b/platform/core/src/services/Topic.js
deleted file mode 100644
index 920107345..000000000
--- a/platform/core/src/services/Topic.js
+++ /dev/null
@@ -1,97 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- var ERROR_PREFIX = "Error when notifying listener: ";
-
- /**
- * The `topic` service provides a way to create both named,
- * shared listeners and anonymous, private listeners.
- *
- * Usage:
- *
- * ```
- * var t = topic('foo'); // Use/create a named topic
- * t.listen(function () { ... });
- * t.notify({ some: "message" });
- * ```
- *
- * Named topics are shared; multiple calls to `topic`
- * with the same argument will return a single object instance.
- * Anonymous topics (where `topic` has been called with no
- * arguments) are private; each call returns a new instance.
- *
- * @returns {Function}
- * @memberof platform/core
- */
- function Topic($log) {
- var topics = {};
-
- function createTopic() {
- var listeners = [];
-
- return {
- listen: function (listener) {
- listeners.push(listener);
-
- return function unlisten() {
- listeners = listeners.filter(function (l) {
- return l !== listener;
- });
- };
- },
- notify: function (message) {
- listeners.forEach(function (listener) {
- try {
- listener(message);
- } catch (e) {
- $log.error(ERROR_PREFIX + e.message);
- $log.error(e);
- }
- });
- }
- };
- }
-
- /**
- * Use and (if necessary) create a new topic.
- * @param {string} [key] name of the topic to use
- * @memberof platform/core.Topic#
- */
- return function (key) {
- if (arguments.length < 1) {
- return createTopic();
- } else {
- topics[key] = topics[key] || createTopic();
-
- return topics[key];
- }
- };
- }
-
- return Topic;
- }
-);
-
diff --git a/platform/core/src/types/MergeModels.js b/platform/core/src/types/MergeModels.js
deleted file mode 100644
index 4df2009e8..000000000
--- a/platform/core/src/types/MergeModels.js
+++ /dev/null
@@ -1,106 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Defines MergedModel, which allows a deep merge of domain object
- * models, or JSONifiable JavaScript objects generally.
- *
- */
-define(
- function () {
-
- /**
- * Utility function for merging domain object models (or any
- * JavaScript object which follows the same conventions.)
- * Performs a "deep merge", resolving conflicts (occurrences
- * of the same property in both objects) such that:
- *
- * * Non-conflicting properties are both contained in the
- * result object.
- * * Conflicting properties which are both arrays are
- * concatenated.
- * * Conflicting properties which are both objects are
- * merged recursively.
- * * Conflicting properties which do not fall into any of the
- * preceding categories are taken from the second argument,
- * shadowing any values from the first.
- *
- * An optional third argument, the "merger", may be provided.
- * This may be either a function, or an object containing
- * key-value pairs where keys are strings (corresponding to
- * the names of properties) and values are other mergers
- * (either functions or objects.)
- *
- * * If the merger is a function, it will be used upon the
- * two input objects in lieu of the behavior described
- * above.
- * * If the merger is an object, then its values will be
- * used as mergers when resolving properties with
- * corresponding keys in the recursive step.
- *
- *
- * @param modelA the first object to be merged
- * @param modelB the second object to be merged
- * @param merger the merger, as described above
- * @returns {*} the result of merging `modelA` and `modelB`
- * @constructor
- * @memberof platform/core
- */
- function mergeModels(modelA, modelB, merger) {
- var mergeFunction;
-
- function mergeArrays(a, b) {
- return a.concat(b);
- }
-
- function mergeObjects(a, b) {
- var result = {};
- Object.keys(a).forEach(function (k) {
- result[k] = Object.prototype.hasOwnProperty.call(b, k)
- ? mergeModels(a[k], b[k], (merger || {})[k])
- : a[k];
- });
- Object.keys(b).forEach(function (k) {
- // Copy any properties not already merged
- if (!Object.prototype.hasOwnProperty.call(a, k)) {
- result[k] = b[k];
- }
- });
-
- return result;
- }
-
- function mergeOther(a, b) {
- return b;
- }
-
- mergeFunction = (merger && Function.isFunction(merger)) ? merger
- : (Array.isArray(modelA) && Array.isArray(modelB)) ? mergeArrays
- : (modelA instanceof Object && modelB instanceof Object) ? mergeObjects
- : mergeOther;
-
- return mergeFunction(modelA, modelB);
- }
-
- return mergeModels;
- }
-);
diff --git a/platform/core/src/types/TypeCapability.js b/platform/core/src/types/TypeCapability.js
deleted file mode 100644
index 99ded1a89..000000000
--- a/platform/core/src/types/TypeCapability.js
+++ /dev/null
@@ -1,55 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Module defining TypeCapability. Created by vwoeltje on 11/10/14.
- */
-define(
- [],
- function () {
-
- /**
- * The `type` capability makes information about a domain object's
- * type directly available when working with that object, by way
- * of a `domainObject.getCapability('type')` invocation.
- *
- * @memberof platform/core
- * @constructor
- * @augments {Type}
- * @implements {Capability}
- * @param {TypeService} typeService the service which
- * provides type information
- * @param {DomainObject} domainObject the domain object
- * which exposes the type capability
- */
- function TypeCapability(typeService, domainObject) {
- var typeKey = domainObject.getModel().type,
- type = typeService.getType(typeKey);
-
- // Simply return the type, but wrap with Object.create
- // to avoid exposing the type object directly.
- return Object.create(type);
- }
-
- return TypeCapability;
- }
-);
diff --git a/platform/core/src/types/TypeImpl.js b/platform/core/src/types/TypeImpl.js
deleted file mode 100644
index 4c2d9ed3d..000000000
--- a/platform/core/src/types/TypeImpl.js
+++ /dev/null
@@ -1,194 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ['./TypeProperty'],
- function (TypeProperty) {
-
- /**
- * Describes a type of domain object.
- *
- * @interface Type
- */
-
- /**
- * Get the string key which identifies this type.
- * This is the type's machine-readable name/identifier,
- * and will correspond to the "type" field of the models
- * of domain objects of this type.
- *
- * @returns {string} the key which identifies this type
- * @method Type#getKey
- */
- /**
- * Get the human-readable name for this type, as should
- * be displayed in the user interface when referencing
- * this type.
- *
- * @returns {string} the human-readable name of this type
- * @method Type#getName
- */
- /**
- * Get the human-readable description for this type, as should
- * be displayed in the user interface when describing
- * this type.
- *
- * @returns {string} the human-readable description of this type
- * @method Type#getDescription
- */
- /**
- * Get the cssClass associated with this type. cssClass is a
- * string which will appear as an icon (when
- * displayed in an appropriate font) which visually
- * distinguish types from one another.
- *
- * @returns {string} the cssClass for this type
- * @method Type#getCssClass
- */
- /**
- * Get an array of properties associated with objects of
- * this type, as might be shown in a Create wizard or
- * an Edit Properties view.
- *
- * @return {TypeProperty[]} properties associated with
- * objects of this type
- * @method Type#getPropertiees
- */
- /**
- * Get the initial state of a model for domain objects of
- * this type.
- *
- * @return {object} initial domain object model
- * @method Type#getInitialModel
- */
- /**
- * Get the raw type definition for this type. This is an
- * object containing key-value pairs of type metadata;
- * this allows the retrieval and use of custom type
- * properties which are not recognized within this interface.
- *
- * @returns {object} the raw definition for this type
- * @method Type#getDefinition
- */
- /**
- * Check if this type is or inherits from some other type.
- *
- * @param {string|Type} key either
- * a string key for a type, or an instance of a type
- * object, which this
- * @returns {boolean} true
- * @method Type#instanceOf
- */
- /**
- * Check if a type should support a given feature. This simply
- * checks for the presence or absence of the feature key in
- * the type definition's "feature" field.
- * @param {string} feature a string identifying the feature
- * @returns {boolean} true if the feature is supported
- * @method Type#hasFeature
- */
-
- /**
- * Construct a new type. Types describe categories of
- * domain objects.
- *
- * @implements {Type}
- * @param {TypeDefinition} typeDef an object containing
- * key-value pairs describing a type and its
- * relationship to other types.
- * @constructor
- * @memberof platform/core
- */
- function TypeImpl(typeDef) {
- var inheritList = typeDef.inherits || [],
- featureSet = {};
-
- (typeDef.features || []).forEach(function (feature) {
- featureSet[feature] = true;
- });
-
- this.typeDef = typeDef;
- this.featureSet = featureSet;
- this.inheritList = inheritList;
- }
-
- TypeImpl.prototype.getKey = function () {
- return this.typeDef.key;
- };
-
- TypeImpl.prototype.getName = function () {
- return this.typeDef.name;
- };
-
- TypeImpl.prototype.getDescription = function () {
- return this.typeDef.description;
- };
-
- TypeImpl.prototype.getCssClass = function () {
- return this.typeDef.cssClass;
- };
-
- TypeImpl.prototype.getProperties = function () {
- return (this.typeDef.properties || []).map(function (propertyDef) {
- return new TypeProperty(propertyDef);
- });
- };
-
- /**
- * Returns the default model for an object of this type. Note that
- * this method returns a clone of the original model, so if using this
- * method heavily, consider caching the result to optimize performance.
- *
- * @return {object} The default model for an object of this type.
- */
- TypeImpl.prototype.getInitialModel = function () {
- return JSON.parse(JSON.stringify(this.typeDef.model || {}));
- };
-
- TypeImpl.prototype.getDefinition = function () {
- return this.typeDef;
- };
-
- TypeImpl.prototype.instanceOf = function instanceOf(key) {
- var typeDef = this.typeDef,
- inheritList = this.inheritList;
-
- if (key === typeDef.key) {
- return true;
- } else if (inheritList.indexOf(key) > -1) {
- return true;
- } else if (!key) {
- return true;
- } else if (key !== null && typeof key === 'object') {
- return key.getKey ? this.instanceOf(key.getKey()) : false;
- } else {
- return false;
- }
- };
-
- TypeImpl.prototype.hasFeature = function (feature) {
- return this.featureSet[feature] || false;
- };
-
- return TypeImpl;
- }
-);
diff --git a/platform/core/src/types/TypeProperty.js b/platform/core/src/types/TypeProperty.js
deleted file mode 100644
index 1abc82acf..000000000
--- a/platform/core/src/types/TypeProperty.js
+++ /dev/null
@@ -1,163 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ['./TypePropertyConversion'],
- function (TypePropertyConversion) {
-
- /**
- * Instantiate a property associated with domain objects of a
- * given type. This provides an interface by which
- *
- * @memberof platform/core
- * @constructor
- */
- function TypeProperty(propertyDefinition) {
- // Load an appropriate conversion
- this.conversion = new TypePropertyConversion(
- propertyDefinition.conversion || "identity"
- );
- this.propertyDefinition = propertyDefinition;
- }
-
- // Check if a value is defined; used to check if initial array
- // values have been populated.
- function isUnpopulatedArray(value) {
- var i;
-
- if (!Array.isArray(value) || value.length === 0) {
- return false;
- }
-
- for (i = 0; i < value.length; i += 1) {
- if (value[i] !== undefined) {
- return false;
- }
- }
-
- return true;
- }
-
- // Specify a field deeply within an object
- function specifyValue(object, propertyPath, value) {
- // If path is not an array, just set the property
- if (!Array.isArray(propertyPath)) {
- object[propertyPath] = value;
- } else if (propertyPath.length > 1) {
- // Otherwise, look up in defined sequence
- object[propertyPath[0]] = object[propertyPath[0]] || {};
- specifyValue(
- object[propertyPath[0]],
- propertyPath.slice(1),
- value
- );
- } else if (propertyPath.length === 1) {
- object[propertyPath[0]] = value;
- }
- }
-
- // Perform a lookup for a value from an object,
- // which may recursively look at contained objects
- // based on the path provided.
- function lookupValue(object, propertyPath) {
- var value;
-
- // Can't look up from a non-object
- if (!object) {
- return undefined;
- }
-
- // If path is not an array, just look up the property
- if (!Array.isArray(propertyPath)) {
- return object[propertyPath];
- }
-
- // Otherwise, look up in the sequence defined in the array
- if (propertyPath.length > 0) {
- value = object[propertyPath[0]];
-
- return propertyPath.length > 1
- ? lookupValue(value, propertyPath.slice(1))
- : value;
- }
-
- // Fallback; property path was empty
- return undefined;
- }
-
- /**
- * Retrieve the value associated with this property
- * from a given model.
- * @param {object} model a domain object model to read from
- * @returns {*} the value for this property, as read from the model
- */
- TypeProperty.prototype.getValue = function (model) {
- var property = this.propertyDefinition.property
- || this.propertyDefinition.key,
- initialValue =
- property && lookupValue(model, property);
-
- // Provide an empty array if this is a multi-item
- // property.
- if (Array.isArray(this.propertyDefinition.items)) {
- initialValue = initialValue
- || new Array(this.propertyDefinition.items.length);
- }
-
- return this.conversion.toFormValue(initialValue);
- };
-
- /**
- * Set a value associated with this property in
- * an object's model.
- * @param {object} model a domain object model to update
- * @param {*} value the new value to set for this property
- */
- TypeProperty.prototype.setValue = function (model, value) {
- var property = this.propertyDefinition.property
- || this.propertyDefinition.key;
-
- // If an array contains all undefined values, treat it
- // as undefined, to filter back out arrays for input
- // that never got entered.
- value = isUnpopulatedArray(value) ? undefined : value;
-
- // Convert to a value suitable for storage in the
- // domain object's model
- value = this.conversion.toModelValue(value);
-
- return property
- ? specifyValue(model, property, value)
- : undefined;
- };
-
- /**
- * Get the raw definition for this property.
- * @returns {TypePropertyDefinition}
- */
- TypeProperty.prototype.getDefinition = function () {
- return this.propertyDefinition;
- };
-
- return TypeProperty;
- }
-);
diff --git a/platform/core/src/types/TypePropertyConversion.js b/platform/core/src/types/TypePropertyConversion.js
deleted file mode 100644
index 27e74e4b7..000000000
--- a/platform/core/src/types/TypePropertyConversion.js
+++ /dev/null
@@ -1,99 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- function () {
-
- var conversions = {
- number: {
- toModelValue: parseFloat,
- toFormValue: function (modelValue) {
- return (typeof modelValue === 'number')
- ? modelValue.toString(10) : undefined;
- }
- },
- identity: {
- toModelValue: function (v) {
- return v;
- },
- toFormValue: function (v) {
- return v;
- }
- }
- },
- ARRAY_SUFFIX = '[]';
-
- // Utility function to handle arrays of conversions
- function ArrayConversion(conversion) {
- return {
- toModelValue: function (formValue) {
- return formValue && formValue.map(conversion.toModelValue);
- },
- toFormValue: function (modelValue) {
- return modelValue && modelValue.map(conversion.toFormValue);
- }
- };
- }
-
- /**
- * Look up an appropriate conversion between form values and model
- * values, e.g. to numeric values.
- * @constructor
- * @memberof platform/core
- */
- function TypePropertyConversion(name) {
- if (name
- && name.length > ARRAY_SUFFIX.length
- && name.indexOf(ARRAY_SUFFIX, name.length - ARRAY_SUFFIX.length) !== -1) {
- return new ArrayConversion(
- new TypePropertyConversion(
- name.substring(0, name.length - ARRAY_SUFFIX.length)
- )
- );
- } else {
- if (!conversions[name]) {
- throw new Error("Unknown conversion type: " + name);
- }
-
- return conversions[name];
- }
- }
-
- /**
- * Convert a value from its format as read from a form, to a
- * format appropriate to store in a model.
- * @method platform/core.TypePropertyConversion#toModelValue
- * @param {*} formValue value as read from a form
- * @returns {*} value to store in a model
- */
-
- /**
- * Convert a value from its format as stored in a model, to a
- * format appropriate to display in a form.
- * @method platform/core.TypePropertyConversion#toFormValue
- * @param {*} modelValue value as stored in a model
- * @returns {*} value to display within a form
- */
-
- return TypePropertyConversion;
- }
-);
diff --git a/platform/core/src/types/TypeProvider.js b/platform/core/src/types/TypeProvider.js
deleted file mode 100644
index ecf4f550a..000000000
--- a/platform/core/src/types/TypeProvider.js
+++ /dev/null
@@ -1,197 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ['./TypeImpl', './MergeModels'],
- function (TypeImpl, mergeModels) {
-
- /**
- * Provides domain object types that are available/recognized within
- * the system.
- *
- * @interface TypeService
- */
- /**
- * Get a specific type by name.
- *
- * @method TypeService#getType
- * @param {string} key the key (machine-readable identifier)
- * for the type of interest
- * @returns {Type} the type identified by this key
- */
- /**
- * List all known types.
- *
- * @method TypeService#listTypes
- * @returns {Type[]} all known types
- */
-
- var TO_CONCAT = ['inherits', 'capabilities', 'properties', 'features'],
- TO_MERGE = ['model'];
-
- function copyKeys(a, b) {
- Object.keys(b).forEach(function (k) {
- a[k] = b[k];
- });
- }
-
- function removeDuplicates(array) {
- var set = {};
-
- return array ? array.filter(function (element) {
- // Don't filter objects (e.g. property definitions)
- if (element instanceof Object && !(element instanceof String)) {
- return true;
- }
-
- return set[element]
- ? false
- : (set[element] = true);
- }) : array;
- }
-
- // Reduce an array of type definitions to a single type definition,
- // which has merged all properties in order.
- function collapse(typeDefs) {
- var collapsed = typeDefs.reduce(function (a, b) {
- var result = {};
- copyKeys(result, a);
- copyKeys(result, b);
-
- // Special case: Do a merge, e.g. on "model"
- TO_MERGE.forEach(function (k) {
- if (a[k] && b[k]) {
- result[k] = mergeModels(a[k], b[k]);
- }
- });
-
- // Special case: Concatenate certain arrays
- TO_CONCAT.forEach(function (k) {
- if (a[k] || b[k]) {
- result[k] = (a[k] || []).concat(b[k] || []);
- }
- });
-
- return result;
- }, {});
-
- // Remove any duplicates from the collapsed array
- TO_CONCAT.forEach(function (k) {
- if (collapsed[k]) {
- collapsed[k] = removeDuplicates(collapsed[k]);
- }
- });
-
- return collapsed;
- }
-
- /**
- * A type provider provides information about types of domain objects
- * within the running Open MCT instance.
- *
- * @param {Array<TypeDefinition>} types the raw type
- * definitions for this type.
- * @memberof platform/core
- * @constructor
- */
- function TypeProvider(types) {
- var rawTypeDefinitions = types,
- typeDefinitions = (function (typeDefArray) {
- var result = {};
- typeDefArray.forEach(function (typeDef) {
- var k = typeDef.key;
- if (k) {
- result[k] = (result[k] || []).concat(typeDef);
- }
- });
-
- return result;
- }(rawTypeDefinitions));
-
- this.typeMap = {};
- this.typeDefinitions = typeDefinitions;
- this.rawTypeDefinitions = types;
- }
-
- TypeProvider.prototype.listTypes = function () {
- var self = this;
-
- return removeDuplicates(
- this.rawTypeDefinitions.filter(function (def) {
- return def.key;
- }).map(function (def) {
- return def.key;
- }).map(function (key) {
- return self.getType(key);
- })
- );
- };
-
- TypeProvider.prototype.getType = function (key) {
- var typeDefinitions = this.typeDefinitions,
- self = this;
-
- function getUndefinedType() {
- return (self.undefinedType = self.undefinedType || collapse(
- self.rawTypeDefinitions.filter(function (typeDef) {
- return !typeDef.key;
- })
- ));
- }
-
- function asArray(value) {
- return Array.isArray(value) ? value : [value];
- }
-
- function lookupTypeDef(typeKey) {
- function buildTypeDef(typeKeyToBuild) {
- var typeDefs = typeDefinitions[typeKeyToBuild] || [],
- inherits = typeDefs.map(function (typeDef) {
- return asArray(typeDef.inherits || []);
- }).reduce(function (a, b) {
- return a.concat(b);
- }, []),
- def = collapse(
- [getUndefinedType()].concat(
- inherits.map(lookupTypeDef)
- ).concat(typeDefs)
- );
-
- // Always provide a default name
- def.model = def.model || {};
- def.model.name = def.model.name
- || ("Unnamed " + (def.name || "Object"));
-
- return def;
- }
-
- return (self.typeMap[typeKey] =
- self.typeMap[typeKey] || buildTypeDef(typeKey));
- }
-
- return new TypeImpl(lookupTypeDef(key));
- };
-
- return TypeProvider;
- }
-
-);
diff --git a/platform/core/src/views/ViewCapability.js b/platform/core/src/views/ViewCapability.js
deleted file mode 100644
index b80f5cbed..000000000
--- a/platform/core/src/views/ViewCapability.js
+++ /dev/null
@@ -1,58 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Module defining ViewCapability. Created by vwoeltje on 11/10/14.
- */
-define(
- [],
- function () {
-
- /**
- * A `view` capability can be used to retrieve an array of
- * all views (or, more specifically, the declarative metadata
- * thereabout) which are applicable to a specific domain
- * object.
- *
- * @memberof platform/core
- * @implements {Capability}
- * @constructor
- */
- function ViewCapability(viewService, domainObject) {
- this.viewService = viewService;
- this.domainObject = domainObject;
- }
-
- /**
- * Get all view definitions which are applicable to
- * this object.
- * @returns {View[]} an array of view definitions
- * which are applicable to this object.
- * @memberof platform/core.ViewCapability#
- */
- ViewCapability.prototype.invoke = function () {
- return this.viewService.getViews(this.domainObject);
- };
-
- return ViewCapability;
- }
-);
diff --git a/platform/core/src/views/ViewProvider.js b/platform/core/src/views/ViewProvider.js
deleted file mode 100644
index c8c5cf556..000000000
--- a/platform/core/src/views/ViewProvider.js
+++ /dev/null
@@ -1,161 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Module defining ViewProvider. Created by vwoeltje on 11/10/14.
- */
-define(
- [],
- function () {
-
- /**
- * Provides definitions for views that are available for specific
- * domain objects.
- *
- * @interface ViewService
- */
-
- /**
- * Get all views which are applicable to this domain object.
- *
- * @method ViewService#getViews
- * @param {DomainObject} domainObject the domain object to view
- * @returns {View[]} all views which can be used to visualize
- * this domain object.
- */
-
- /**
- * A view provider allows view definitions (defined as extensions)
- * to be read, and takes responsibility for filtering these down
- * to a set that is applicable to specific domain objects. This
- * filtering is parameterized by the extension definitions
- * themselves; specifically:
- *
- * * Definitions with a `needs` property containing an array of
- * strings are only applicable to domain objects which have
- * all those capabilities.
- * * If the view definition has a `delegation` property that
- * is truthy, then domain objects which delegate capabilities
- * from the `needs` property will be treated as having those
- * capabilities for purposes of determining view applicability.
- * * Definitions with a `type` property are only applicable to
- * domain object's whose `type` capability matches or inherits
- * from that type.
- *
- * Views themselves are primarily metadata, such as name, icon and
- * description (to be shown in the UI); they do not contain any
- * information directly applicable to rendering to the DOM, although
- * they do contain sufficient information (such as a `templateUrl`,
- * used in the representation bundle) to retrieve those details.
- * The role of a view provider and of a view capability is to
- * describe what views are available, not how to instantiate them.
- *
- * @memberof platform/core
- * @constructor
- * @param {View[]} an array of view definitions
- * @param $log Angular's logging service
- * @implements {ViewService}
- */
- function ViewProvider(views, $log) {
-
- // Views without defined keys cannot be used in the user
- // interface, and can result in unexpected behavior. These
- // are filtered out using this function.
- function validate(view) {
- var key = view.key;
-
- // Leave a log message to support detection of this issue.
- if (!key) {
- $log.warn([
- "No key specified for view in ",
- (view.bundle || {}).path,
- "; omitting this view."
- ].join(""));
- }
-
- return key;
- }
-
- // Filter out any key-less views
- this.views = views.filter(validate);
- }
-
- ViewProvider.prototype.getViews = function (domainObject) {
- var type = domainObject.useCapability("type");
-
- // Check if an object has all capabilities designated as `needs`
- // for a view. Exposing a capability via delegation is taken to
- // satisfy this filter if `allowDelegation` is true.
- function capabilitiesMatch(domainObj, capabilities, allowDelegation) {
- var delegation = domainObj.getCapability("delegation");
-
- allowDelegation = allowDelegation && (delegation !== undefined);
-
- // Check if an object has (or delegates, if allowed) a
- // capability.
- function hasCapability(c) {
- return domainObj.hasCapability(c)
- || (allowDelegation && delegation.doesDelegateCapability(c));
- }
-
- // For the reduce step below.
- function and(a, b) {
- return a && b;
- }
-
- // Do a bulk `and` operation over all needed capabilities.
- return capabilities.map(hasCapability).reduce(and, true);
- }
-
- // Check if a view and domain object type can be paired;
- // both can restrict the others they accept.
- function viewMatchesType(view, objType) {
- var views = objType && (objType.getDefinition() || {}).views,
- matches = true;
-
- // View is restricted to a certain type
- if (view.type) {
- matches = matches && objType && objType.instanceOf(view.type);
- }
-
- // Type wishes to restrict its specific views
- if (Array.isArray(views)) {
- matches = matches && (views.indexOf(view.key) > -1);
- }
-
- return matches;
- }
-
- // First, filter views by type (matched to domain object type.)
- // Second, filter by matching capabilities.
- return this.views.filter(function (view) {
- return viewMatchesType(view, type) && capabilitiesMatch(
- domainObject,
- view.needs || [],
- view.delegation || false
- );
- });
- };
-
- return ViewProvider;
- }
-);
diff --git a/platform/core/test/actions/ActionAggregatorSpec.js b/platform/core/test/actions/ActionAggregatorSpec.js
deleted file mode 100644
index 371968602..000000000
--- a/platform/core/test/actions/ActionAggregatorSpec.js
+++ /dev/null
@@ -1,73 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * ActionAggregatorSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/actions/ActionAggregator"],
- function (ActionAggregator) {
-
- describe("Action aggregator", function () {
- var mockAggregators,
- aggregator;
-
- function createMockActionProvider(actions, i) {
- var spy = jasmine.createSpyObj("agg" + i, ["getActions"]);
- spy.getActions.and.returnValue(actions);
-
- return spy;
- }
-
- beforeEach(function () {
- mockAggregators = [
- ["a", "b"],
- ["c"],
- ["d", "e", "f"]
- ].map(createMockActionProvider);
- aggregator = new ActionAggregator(mockAggregators);
- });
-
- it("consolidates results from aggregated services", function () {
- expect(aggregator.getActions()).toEqual(
- ["a", "b", "c", "d", "e", "f"]
- );
- });
-
- it("passes context along to all aggregated services", function () {
- var context = { domainObject: "something" };
-
- // Verify precondition
- mockAggregators.forEach(function (mockAgg) {
- expect(mockAgg.getActions).not.toHaveBeenCalled();
- });
-
- aggregator.getActions(context);
-
- // All services should have been called with this context
- mockAggregators.forEach(function (mockAgg) {
- expect(mockAgg.getActions).toHaveBeenCalledWith(context);
- });
- });
- });
- }
-);
diff --git a/platform/core/test/actions/ActionCapabilitySpec.js b/platform/core/test/actions/ActionCapabilitySpec.js
deleted file mode 100644
index 2d7b16e99..000000000
--- a/platform/core/test/actions/ActionCapabilitySpec.js
+++ /dev/null
@@ -1,96 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * ActionCapabilitySpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/actions/ActionCapability"],
- function (ActionCapability) {
-
- describe("The action capability", function () {
- var mockQ,
- mockAction,
- mockActionService,
- mockDomainObject,
- capability;
-
- beforeEach(function () {
- mockAction = jasmine.createSpyObj(
- "action",
- ["perform", "getMetadata"]
- );
- mockActionService = jasmine.createSpyObj(
- "actionService",
- ["getActions"]
- );
- mockQ = jasmine.createSpyObj(
- "$q",
- ["when"]
- );
- mockDomainObject = jasmine.createSpyObj(
- "domainObject",
- ["getId", "getModel", "getCapability", "hasCapability", "useCapability"]
- );
-
- mockActionService.getActions.and.returnValue([mockAction, {}]);
-
- capability = new ActionCapability(
- mockQ,
- mockActionService,
- mockDomainObject
- );
- });
-
- it("retrieves action for domain objects from the action service", function () {
- // Verify precondition
- expect(mockActionService.getActions).not.toHaveBeenCalled();
-
- // Call getActions
- expect(capability.getActions("some key")).toEqual([mockAction, {}]);
-
- // Verify interaction
- expect(mockActionService.getActions).toHaveBeenCalledWith({
- key: "some key",
- domainObject: mockDomainObject
- });
- });
-
- it("promises the result of performed actions", function () {
- var mockPromise = jasmine.createSpyObj("promise", ["then"]);
- mockQ.when.and.returnValue(mockPromise);
- mockAction.perform.and.returnValue("the action's result");
-
- // Verify precondition
- expect(mockAction.perform).not.toHaveBeenCalled();
-
- // Perform via capability
- expect(capability.perform()).toEqual(mockPromise);
-
- // Verify that the action's result is what was wrapped
- expect(mockQ.when).toHaveBeenCalledWith("the action's result");
-
- });
-
- });
- }
-);
diff --git a/platform/core/test/actions/ActionProviderSpec.js b/platform/core/test/actions/ActionProviderSpec.js
deleted file mode 100644
index bbade4d9f..000000000
--- a/platform/core/test/actions/ActionProviderSpec.js
+++ /dev/null
@@ -1,204 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * ActionProviderSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/actions/ActionProvider"],
- function (ActionProvider) {
-
- describe("The action provider", function () {
- var mockLog,
- actions,
- actionProvider;
-
- function SimpleAction() {
- return {
- perform: function () {
- return "simple";
- }
- };
- }
-
- function CategorizedAction() {
- return {
- perform: function () {
- return "categorized";
- }
- };
- }
-
- CategorizedAction.category = "someCategory";
-
- function KeyedAction() {
- return {
- perform: function () {
- return "keyed";
- }
- };
- }
-
- KeyedAction.key = "someKey";
-
- function CategorizedKeyedAction() {
- return {
- perform: function () {
- return "both";
- }
- };
- }
-
- CategorizedKeyedAction.key = "someKey";
- CategorizedKeyedAction.category = "someCategory";
-
- function MetadataAction() {
- return {
- perform: function () {
- return "metadata";
- },
- getMetadata: function () {
- return "custom metadata";
- }
- };
- }
-
- MetadataAction.key = "metadata";
-
- beforeEach(function () {
- mockLog = jasmine.createSpyObj(
- '$log',
- ['error', 'warn', 'info', 'debug']
- );
- actions = [
- SimpleAction,
- CategorizedAction,
- KeyedAction,
- CategorizedKeyedAction,
- MetadataAction
- ];
- actionProvider = new ActionProvider(actions);
- });
-
- it("exposes provided action extensions", function () {
- var provided = actionProvider.getActions();
-
- // Should have gotten all actions
- expect(provided.length).toEqual(actions.length);
-
- // Verify that this was the action we expected
- expect(provided[0].perform()).toEqual("simple");
- });
-
- it("matches provided actions by key", function () {
- var provided = actionProvider.getActions({ key: "someKey" });
-
- // Only two should have matched
- expect(provided.length).toEqual(2);
-
- // Verify that this was the action we expected
- expect(provided[0].perform()).toEqual("keyed");
- });
-
- it("matches provided actions by category", function () {
- var provided = actionProvider.getActions({ category: "someCategory" });
-
- // Only two should have matched
- expect(provided.length).toEqual(2);
-
- // Verify that this was the action we expected
- expect(provided[0].perform()).toEqual("categorized");
- });
-
- it("matches provided actions by both category and key", function () {
- var provided = actionProvider.getActions({
- category: "someCategory",
- key: "someKey"
- });
-
- // Only two should have matched
- expect(provided.length).toEqual(1);
-
- // Verify that this was the action we expected
- expect(provided[0].perform()).toEqual("both");
- });
-
- it("adds a getMetadata method when none is defined", function () {
- var provided = actionProvider.getActions({
- category: "someCategory",
- key: "someKey"
- });
-
- // Should be defined, even though the action didn't define this
- expect(provided[0].getMetadata).toBeDefined();
-
- // Should have static fields, plus context
- expect(provided[0].getMetadata().context).toEqual({
- key: "someKey",
- category: "someCategory"
- });
-
- });
-
- it("does not override defined getMetadata methods", function () {
- var provided = actionProvider.getActions({ key: "metadata" });
- expect(provided[0].getMetadata()).toEqual("custom metadata");
- });
-
- describe("when actions throw errors during instantiation", function () {
- var errorText,
- provided;
-
- beforeEach(function () {
- errorText = "some error text";
-
- function BadAction() {
- throw new Error(errorText);
- }
-
- provided = new ActionProvider(
- [SimpleAction, BadAction],
- mockLog
- ).getActions();
- });
-
- it("logs an error", function () {
- expect(mockLog.error)
- .toHaveBeenCalledWith(jasmine.any(String));
- });
-
- it("reports the error's message", function () {
- expect(
- mockLog.error.calls.mostRecent().args[0].indexOf(errorText)
- ).not.toEqual(-1);
- });
-
- it("still provides valid actions", function () {
- expect(provided.length).toEqual(1);
- expect(provided[0].perform()).toEqual("simple");
- });
-
- });
-
- });
- }
-);
diff --git a/platform/core/test/actions/LoggingActionDecoratorSpec.js b/platform/core/test/actions/LoggingActionDecoratorSpec.js
deleted file mode 100644
index 332b337cb..000000000
--- a/platform/core/test/actions/LoggingActionDecoratorSpec.js
+++ /dev/null
@@ -1,71 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * LoggingActionDecoratorSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/actions/LoggingActionDecorator"],
- function (LoggingActionDecorator) {
-
- describe("The logging action decorator", function () {
- var mockLog,
- mockAction,
- mockActionService,
- decorator;
-
- beforeEach(function () {
- mockAction = jasmine.createSpyObj(
- "action",
- ["perform", "getMetadata"]
- );
- mockActionService = jasmine.createSpyObj(
- "actionService",
- ["getActions"]
- );
- mockLog = jasmine.createSpyObj(
- "$log",
- ["error", "warn", "info", "debug"]
- );
-
- mockActionService.getActions.and.returnValue([mockAction]);
-
- decorator = new LoggingActionDecorator(
- mockLog,
- mockActionService
- );
- });
-
- it("logs when actions are performed", function () {
- // Verify precondition
- expect(mockLog.info).not.toHaveBeenCalled();
-
- // Perform an action, retrieved through the decorator
- decorator.getActions()[0].perform();
-
- // That should have been logged.
- expect(mockLog.info).toHaveBeenCalled();
- });
-
- });
- }
-);
diff --git a/platform/core/test/capabilities/CompositionCapabilitySpec.js b/platform/core/test/capabilities/CompositionCapabilitySpec.js
deleted file mode 100644
index 48a9ac684..000000000
--- a/platform/core/test/capabilities/CompositionCapabilitySpec.js
+++ /dev/null
@@ -1,214 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * CompositionCapabilitySpec. Created by vwoeltje on 11/6/14.
- */
-define(
- [
- "../../src/capabilities/CompositionCapability",
- "../../src/capabilities/ContextualDomainObject"
- ],
- function (CompositionCapability, ContextualDomainObject) {
-
- var DOMAIN_OBJECT_METHODS = [
- "getId",
- "getModel",
- "getCapability",
- "hasCapability",
- "useCapability"
- ];
-
- describe("The composition capability", function () {
- var mockDomainObject,
- mockInjector,
- mockObjectService,
- composition;
-
- // Composition Capability makes use of promise chaining,
- // so support that, but don't introduce complication of
- // native promises.
- function mockPromise(value) {
- return (value || {}).then ? value : {
- then: function (callback) {
- return mockPromise(callback(value));
- }
- };
- }
-
- beforeEach(function () {
- mockDomainObject = jasmine.createSpyObj(
- "domainObject",
- DOMAIN_OBJECT_METHODS
- );
-
- mockObjectService = jasmine.createSpyObj(
- "objectService",
- ["getObjects"]
- );
-
- mockInjector = {
- get: function (name) {
- return (name === "objectService") && mockObjectService;
- }
- };
-
- mockObjectService.getObjects.and.returnValue(mockPromise([]));
-
- composition = new CompositionCapability(
- mockInjector,
- mockDomainObject
- );
- });
-
- it("applies only to models with a composition field", function () {
- expect(CompositionCapability.appliesTo({ composition: [] }))
- .toBeTruthy();
- expect(CompositionCapability.appliesTo({}))
- .toBeFalsy();
- });
-
- it("requests ids found in model's composition from the object service", function () {
- var ids = ["a", "b", "c", "xyz"];
-
- mockDomainObject.getModel.and.returnValue({ composition: ids });
-
- composition.invoke();
-
- expect(mockObjectService.getObjects).toHaveBeenCalledWith(ids);
- });
-
- it("adds a context capability to returned domain objects", function () {
- var result,
- mockChild = jasmine.createSpyObj("child", DOMAIN_OBJECT_METHODS);
-
- mockDomainObject.getModel.and.returnValue({ composition: ["x"] });
- mockObjectService.getObjects.and.returnValue(mockPromise({x: mockChild}));
- mockChild.getCapability.and.returnValue(undefined);
-
- composition.invoke().then(function (c) {
- result = c;
- });
-
- // Should have been added by a wrapper
- expect(result[0].getCapability('context')).toBeDefined();
-
- });
-
- it("allows domain objects to be added", function () {
- var result,
- testModel = { composition: [] },
- mockChild = jasmine.createSpyObj("child", DOMAIN_OBJECT_METHODS);
-
- mockDomainObject.getModel.and.returnValue(testModel);
- mockObjectService.getObjects.and.returnValue(mockPromise({a: mockChild}));
- mockChild.getCapability.and.returnValue(undefined);
- mockChild.getId.and.returnValue('a');
-
- mockDomainObject.useCapability.and.callFake(function (key, mutator) {
- if (key === 'mutation') {
- mutator(testModel);
-
- return mockPromise(true);
- }
- });
-
- composition.add(mockChild).then(function (domainObject) {
- result = domainObject;
- });
-
- expect(testModel.composition).toEqual(['a']);
-
- // Should have returned the added object in its new context
- expect(result.getId()).toEqual('a');
- expect(result.getCapability('context')).toBeDefined();
- expect(result.getCapability('context').getParent())
- .toEqual(mockDomainObject);
- });
-
- it("does not re-add IDs which are already present", function () {
- var result,
- testModel = { composition: ['a'] },
- mockChild = jasmine.createSpyObj("child", DOMAIN_OBJECT_METHODS);
-
- mockDomainObject.getModel.and.returnValue(testModel);
- mockObjectService.getObjects.and.returnValue(mockPromise({a: mockChild}));
- mockChild.getCapability.and.returnValue(undefined);
- mockChild.getId.and.returnValue('a');
-
- mockDomainObject.useCapability.and.callFake(function (key, mutator) {
- if (key === 'mutation') {
- mutator(testModel);
-
- return mockPromise(true);
- }
- });
-
- composition.add(mockChild).then(function (domainObject) {
- result = domainObject;
- });
-
- // Still just 'a'
- expect(testModel.composition).toEqual(['a']);
-
- // Should have returned the added object in its new context
- expect(result.getId()).toEqual('a');
- expect(result.getCapability('context')).toBeDefined();
- expect(result.getCapability('context').getParent())
- .toEqual(mockDomainObject);
- });
-
- it("can add objects at a specified index", function () {
- var result,
- testModel = { composition: ['a', 'b', 'c'] },
- mockChild = jasmine.createSpyObj("child", DOMAIN_OBJECT_METHODS);
-
- mockDomainObject.getModel.and.returnValue(testModel);
- mockObjectService.getObjects.and.returnValue(mockPromise({a: mockChild}));
- mockChild.getCapability.and.returnValue(undefined);
- mockChild.getId.and.returnValue('a');
-
- mockDomainObject.useCapability.and.callFake(function (key, mutator) {
- if (key === 'mutation') {
- mutator(testModel);
-
- return mockPromise(true);
- }
- });
-
- composition.add(mockChild, 1).then(function (domainObject) {
- result = domainObject;
- });
-
- // Still just 'a'
- expect(testModel.composition).toEqual(['b', 'a', 'c']);
-
- // Should have returned the added object in its new context
- expect(result.getId()).toEqual('a');
- expect(result.getCapability('context')).toBeDefined();
- expect(result.getCapability('context').getParent())
- .toEqual(mockDomainObject);
- });
-
- });
- }
-);
diff --git a/platform/core/test/capabilities/ContextCapabilitySpec.js b/platform/core/test/capabilities/ContextCapabilitySpec.js
deleted file mode 100644
index 8afcbd7e1..000000000
--- a/platform/core/test/capabilities/ContextCapabilitySpec.js
+++ /dev/null
@@ -1,79 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * ContextCapability. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/capabilities/ContextCapability"],
- function (ContextCapability) {
-
- var DOMAIN_OBJECT_METHODS = [
- "getId",
- "getModel",
- "getCapability",
- "hasCapability",
- "useCapability"
- ];
-
- describe("The context capability", function () {
- var mockDomainObject,
- mockParent,
- mockGrandparent,
- mockContext,
- context;
-
- beforeEach(function () {
- mockDomainObject = jasmine.createSpyObj("domainObject", DOMAIN_OBJECT_METHODS);
- mockParent = jasmine.createSpyObj("parent", DOMAIN_OBJECT_METHODS);
- mockGrandparent = jasmine.createSpyObj("grandparent", DOMAIN_OBJECT_METHODS);
- mockContext = jasmine.createSpyObj("context", ["getParent", "getRoot", "getPath"]);
-
- mockParent.getCapability.and.returnValue(mockContext);
- mockContext.getParent.and.returnValue(mockGrandparent);
- mockContext.getRoot.and.returnValue(mockGrandparent);
- mockContext.getPath.and.returnValue([mockGrandparent, mockParent]);
-
- context = new ContextCapability(mockParent, mockDomainObject);
- });
-
- it("allows an object's parent to be retrieved", function () {
- expect(context.getParent()).toEqual(mockParent);
- });
-
- it("allows an object's full ancestry to be retrieved", function () {
- expect(context.getPath()).toEqual([mockGrandparent, mockParent, mockDomainObject]);
- });
-
- it("allows the deepest ancestor of an object to be retrieved", function () {
- expect(context.getRoot()).toEqual(mockGrandparent);
- });
-
- it("treats ancestors with no context capability as deepest ancestors", function () {
- mockParent.getCapability.and.returnValue(undefined);
- expect(context.getPath()).toEqual([mockParent, mockDomainObject]);
- expect(context.getRoot()).toEqual(mockParent);
- });
-
- });
- }
-);
diff --git a/platform/core/test/capabilities/ContextualDomainObjectSpec.js b/platform/core/test/capabilities/ContextualDomainObjectSpec.js
deleted file mode 100644
index ced432ff9..000000000
--- a/platform/core/test/capabilities/ContextualDomainObjectSpec.js
+++ /dev/null
@@ -1,78 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * ContextualDomainObjectSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/capabilities/ContextualDomainObject"],
- function (ContextualDomainObject) {
-
- var DOMAIN_OBJECT_METHODS = [
- "getId",
- "getModel",
- "getCapability",
- "hasCapability",
- "useCapability"
- ];
-
- describe("A contextual domain object", function () {
- var mockParent,
- mockDomainObject,
- model,
- contextualDomainObject;
-
- beforeEach(function () {
- mockParent = jasmine.createSpyObj("parent", DOMAIN_OBJECT_METHODS);
- mockDomainObject = jasmine.createSpyObj("parent", DOMAIN_OBJECT_METHODS);
-
- model = { someKey: "some value" };
-
- mockDomainObject.getCapability.and.returnValue("some capability");
- mockDomainObject.getModel.and.returnValue(model);
-
- contextualDomainObject = new ContextualDomainObject(
- mockDomainObject,
- mockParent
- );
- });
-
- it("adds a context capability to a domain object", function () {
- var context = contextualDomainObject.getCapability('context');
-
- // Expect something that looks like a context capability
- expect(context).toBeDefined();
- expect(context.getPath).toBeDefined();
- expect(context.getRoot).toBeDefined();
- expect(context.getParent()).toEqual(mockParent);
- });
-
- it("does not shadow other domain object methods", function () {
- expect(contextualDomainObject.getModel())
- .toEqual(model);
- expect(contextualDomainObject.getCapability("other"))
- .toEqual("some capability");
- });
-
- });
- }
-);
diff --git a/platform/core/test/capabilities/CoreCapabilityProviderSpec.js b/platform/core/test/capabilities/CoreCapabilityProviderSpec.js
deleted file mode 100644
index 2bcf984ac..000000000
--- a/platform/core/test/capabilities/CoreCapabilityProviderSpec.js
+++ /dev/null
@@ -1,112 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * CoreCapabilityProviderSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/capabilities/CoreCapabilityProvider"],
- function (CoreCapabilityProvider) {
-
- describe("The core capability provider", function () {
- var mockLog,
- provider;
-
- function BasicCapability() {
- return;
- }
-
- BasicCapability.key = "basic";
-
- function ApplicableCapability() {
- return;
- }
-
- ApplicableCapability.key = "applicable";
- ApplicableCapability.appliesTo = function (model) {
- return !model.isNotApplicable;
- };
-
- function KeylessCapability() {
- return;
- }
-
- beforeEach(function () {
- KeylessCapability.key = undefined;
-
- mockLog = jasmine.createSpyObj(
- "$log",
- ["error", "warn", "info", "debug"]
- );
-
- provider = new CoreCapabilityProvider([
- BasicCapability,
- ApplicableCapability,
- KeylessCapability
- ], mockLog);
- });
-
- it("returns capabilities for models, from extensions", function () {
- expect(provider.getCapabilities({})).toEqual({
- basic: BasicCapability,
- applicable: ApplicableCapability
- });
- });
-
- it("filters out capabilities which do not apply to models", function () {
- expect(provider.getCapabilities({ isNotApplicable: true })).toEqual({
- basic: BasicCapability
- });
- });
-
- it("logs a warning when capability extensions have not defined keys", function () {
- // Verify precondition
- expect(mockLog.warn).not.toHaveBeenCalled();
-
- provider.getCapabilities({});
-
- expect(mockLog.warn).toHaveBeenCalled();
-
- });
-
- it("does not log a warning when all capability extensions are valid", function () {
- KeylessCapability.key = "someKey";
- provider.getCapabilities({});
- expect(mockLog.warn).not.toHaveBeenCalled();
- });
-
- it("prefers higher-priority capability", function () {
- KeylessCapability.key = BasicCapability.key;
- expect(provider.getCapabilities({}).basic)
- .toEqual(BasicCapability);
- });
-
- // https://github.com/nasa/openmctweb/issues/49
- it("does not log a warning for multiple capabilities with the same key", function () {
- KeylessCapability.key = BasicCapability.key;
- provider.getCapabilities({});
- expect(mockLog.warn).not.toHaveBeenCalled();
- });
-
- });
- }
-);
diff --git a/platform/core/test/capabilities/DelegationCapabilitySpec.js b/platform/core/test/capabilities/DelegationCapabilitySpec.js
deleted file mode 100644
index 109b1eba7..000000000
--- a/platform/core/test/capabilities/DelegationCapabilitySpec.js
+++ /dev/null
@@ -1,110 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * DelegationCapabilitySpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/capabilities/DelegationCapability"],
- function (DelegationCapability) {
-
- describe("The delegation capability", function () {
- var captured,
- typeDef = {},
- type,
- capabilities,
- children = [],
- object = {},
- delegation;
-
- function capture(k) {
- return function (v) {
- captured[k] = v;
- };
- }
-
- function TestDomainObject(caps, id) {
- return {
- getId: function () {
- return id;
- },
- getCapability: function (name) {
- return caps[name];
- },
- useCapability: function (name) {
- return this.getCapability(name).invoke();
- },
- hasCapability: function (name) {
- return this.getCapability(name) !== undefined;
- }
- };
- }
-
- function mockPromise(value) {
- return {
- then: function (callback) {
- return value.then
- ? value : mockPromise(callback(value));
- }
- };
- }
-
- beforeEach(function () {
- captured = {};
- typeDef = {};
- typeDef.delegates = ["foo"];
- type = {
- getDefinition: function () {
- return typeDef;
- }
- };
- children = [];
- capabilities = {
- type: type,
- composition: {
- invoke: function () {
- return mockPromise(children);
- }
- }
- };
- object = new TestDomainObject(capabilities);
-
- delegation = new DelegationCapability({ when: mockPromise }, object);
- });
-
- it("provides a list of children which expose a desired capability", function () {
-
- children = [
- new TestDomainObject({ foo: true }, 'has-capability'),
- new TestDomainObject({ }, 'does-not-have-capability')
- ];
-
- // Look up delegates
- delegation.getDelegates('foo').then(capture('delegates'));
-
- // Expect only the first child to be a delegate
- expect(captured.delegates.length).toEqual(1);
- expect(captured.delegates[0].getId()).toEqual('has-capability');
- });
- });
- }
-);
diff --git a/platform/core/test/capabilities/InstantiationCapabilitySpec.js b/platform/core/test/capabilities/InstantiationCapabilitySpec.js
deleted file mode 100644
index 88bfcb4a3..000000000
--- a/platform/core/test/capabilities/InstantiationCapabilitySpec.js
+++ /dev/null
@@ -1,94 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../../src/capabilities/InstantiationCapability"],
- function (InstantiationCapability) {
-
- describe("The 'instantiation' capability", function () {
- var mockInjector,
- mockIdentifierService,
- mockInstantiate,
- mockIdentifier,
- mockNow,
- mockDomainObject,
- instantiation;
-
- beforeEach(function () {
- mockInjector = jasmine.createSpyObj("$injector", ["get"]);
- mockInstantiate = jasmine.createSpy("instantiate");
- mockIdentifierService = jasmine.createSpyObj(
- 'identifierService',
- ['parse', 'generate']
- );
- mockIdentifier = jasmine.createSpyObj(
- 'identifier',
- ['getSpace', 'getKey', 'getDefinedSpace']
- );
- mockDomainObject = jasmine.createSpyObj(
- 'domainObject',
- ['getId', 'getCapability', 'getModel']
- );
-
- mockInjector.get.and.callFake(function (key) {
- return {
- 'instantiate': mockInstantiate
- }[key];
- });
- mockIdentifierService.parse.and.returnValue(mockIdentifier);
- mockIdentifierService.generate.and.returnValue("some-id");
-
- mockNow = jasmine.createSpy();
- mockNow.and.returnValue(1234321);
-
- instantiation = new InstantiationCapability(
- mockInjector,
- mockIdentifierService,
- mockNow,
- mockDomainObject
- );
- });
-
- it("aliases 'instantiate' as 'invoke'", function () {
- expect(instantiation.invoke).toBe(instantiation.instantiate);
- });
-
- it("uses instantiate and contextualize to create domain objects", function () {
- var mockDomainObj = jasmine.createSpyObj('domainObject', [
- 'getId',
- 'getModel',
- 'getCapability',
- 'useCapability',
- 'hasCapability'
- ]), testModel = { someKey: "some value" };
- mockInstantiate.and.returnValue(mockDomainObj);
- instantiation.instantiate(testModel);
- expect(mockInstantiate)
- .toHaveBeenCalledWith({
- someKey: "some value",
- modified: mockNow()
- }, jasmine.any(String));
- });
-
- });
- }
-);
diff --git a/platform/core/test/capabilities/MetadataCapabilitySpec.js b/platform/core/test/capabilities/MetadataCapabilitySpec.js
deleted file mode 100644
index a94fac607..000000000
--- a/platform/core/test/capabilities/MetadataCapabilitySpec.js
+++ /dev/null
@@ -1,99 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ['../../src/capabilities/MetadataCapability'],
- function (MetadataCapability) {
-
- describe("The metadata capability", function () {
- var mockDomainObject,
- mockType,
- mockProperties,
- testModel,
- metadata;
-
- function getCapability(key) {
- return key === 'type' ? mockType : undefined;
- }
-
- function findValue(properties, name) {
- var i;
- for (i = 0; i < properties.length; i += 1) {
- if (properties[i].name === name) {
- return properties[i].value;
- }
- }
- }
-
- beforeEach(function () {
- mockDomainObject = jasmine.createSpyObj(
- 'domainObject',
- ['getId', 'getCapability', 'useCapability', 'getModel']
- );
- mockType = jasmine.createSpyObj(
- 'type',
- ['getProperties', 'getName']
- );
- mockProperties = ['a', 'b', 'c'].map(function (k) {
- var mockProperty = jasmine.createSpyObj(
- 'property-' + k,
- ['getValue', 'getDefinition']
- );
- mockProperty.getValue.and.returnValue("Value " + k);
- mockProperty.getDefinition.and.returnValue({ name: "Property " + k});
-
- return mockProperty;
- });
- testModel = { name: "" };
-
- mockDomainObject.getId.and.returnValue("Test id");
- mockDomainObject.getModel.and.returnValue(testModel);
- mockDomainObject.getCapability.and.callFake(getCapability);
- mockDomainObject.useCapability.and.callFake(getCapability);
- mockType.getProperties.and.returnValue(mockProperties);
- mockType.getName.and.returnValue("Test type");
-
- metadata = new MetadataCapability(mockDomainObject);
- });
-
- it("reads properties from the domain object model", function () {
- metadata.invoke();
- mockProperties.forEach(function (mockProperty) {
- expect(mockProperty.getValue).toHaveBeenCalledWith(testModel);
- });
- });
-
- it("reports type-specific properties", function () {
- var properties = metadata.invoke();
- expect(findValue(properties, 'Property a')).toEqual("Value a");
- expect(findValue(properties, 'Property b')).toEqual("Value b");
- expect(findValue(properties, 'Property c')).toEqual("Value c");
- });
-
- it("reports generic properties", function () {
- var properties = metadata.invoke();
- expect(findValue(properties, 'Type')).toEqual("Test type");
- });
-
- });
- }
-);
diff --git a/platform/core/test/capabilities/MutationCapabilitySpec.js b/platform/core/test/capabilities/MutationCapabilitySpec.js
deleted file mode 100644
index 05ac4af63..000000000
--- a/platform/core/test/capabilities/MutationCapabilitySpec.js
+++ /dev/null
@@ -1,139 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * MutationCapabilitySpec. Created by vwoeltje on 11/6/14.
- */
-define(
- [
- "../../src/capabilities/MutationCapability",
- "../../src/services/Topic"
- ],
- function (MutationCapability, Topic) {
-
- describe("The mutation capability", function () {
- var testModel,
- topic,
- mockNow,
- domainObject = {
- getId: function () {
- return "test-id";
- },
- getModel: function () {
- return testModel;
- }
- },
- mutation;
-
- beforeEach(function () {
- testModel = { number: 6 };
- topic = new Topic();
- mockNow = jasmine.createSpy('now');
- mockNow.and.returnValue(12321);
- mutation = new MutationCapability(
- topic,
- mockNow,
- domainObject
- );
- });
-
- it("allows mutation of a model", function () {
- mutation.invoke(function (m) {
- m.number = m.number * 7;
- });
- expect(testModel.number).toEqual(42);
- });
-
- it("allows setting a model", function () {
- mutation.invoke(function () {
- return { someKey: "some value" };
- });
- expect(testModel.number).toBeUndefined();
- expect(testModel.someKey).toEqual("some value");
- });
-
- it("allows model mutation to be aborted", function () {
- mutation.invoke(function (m) {
- m.number = m.number * 7;
-
- return false; // Should abort change
- });
- // Number should not have been changed
- expect(testModel.number).toEqual(6);
- });
-
- it("attaches a timestamp on mutation", function () {
- // Verify precondition
- expect(testModel.modified).toBeUndefined();
- mutation.invoke(function (m) {
- m.number = m.number * 7;
- });
- // Should have gotten a timestamp from 'now'
- expect(testModel.modified).toEqual(12321);
- });
-
- it("allows a timestamp to be provided", function () {
- mutation.invoke(function (m) {
- m.number = m.number * 7;
- }, 42);
- // Should have gotten a timestamp from 'now'
- expect(testModel.modified).toEqual(42);
- });
-
- it("notifies listeners of mutation", function () {
- var mockCallback = jasmine.createSpy('callback');
- mutation.listen(mockCallback);
- mutation.invoke(function (m) {
- m.number = 8;
- });
- expect(mockCallback).toHaveBeenCalled();
- expect(mockCallback.calls.mostRecent().args[0].number)
- .toEqual(8);
- });
-
- it("allows listeners to stop listening", function () {
- var mockCallback = jasmine.createSpy('callback');
- mutation.listen(mockCallback)(); // Unlisten immediately
- mutation.invoke(function (m) {
- m.number = 8;
- });
- expect(mockCallback).not.toHaveBeenCalled();
- });
-
- it("shares listeners across instances", function () {
- var mockCallback = jasmine.createSpy('callback'),
- otherMutation = new MutationCapability(
- topic,
- mockNow,
- domainObject
- );
- mutation.listen(mockCallback);
- otherMutation.invoke(function (m) {
- m.number = 8;
- });
- expect(mockCallback).toHaveBeenCalled();
- expect(mockCallback.calls.mostRecent().args[0].number)
- .toEqual(8);
- });
- });
- }
-);
diff --git a/platform/core/test/capabilities/PersistenceCapabilitySpec.js b/platform/core/test/capabilities/PersistenceCapabilitySpec.js
deleted file mode 100644
index b876f8e6e..000000000
--- a/platform/core/test/capabilities/PersistenceCapabilitySpec.js
+++ /dev/null
@@ -1,196 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-/**
- * PersistenceCapabilitySpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/capabilities/PersistenceCapability"],
- function (PersistenceCapability) {
-
- describe("The persistence capability", function () {
- var mockPersistenceService,
- mockIdentifierService,
- mockDomainObject,
- mockIdentifier,
- mockNofificationService,
- mockCacheService,
- mockQ,
- key = "persistence key",
- id = "object identifier",
- model,
- SPACE = "some space",
- persistence,
- mockOpenMCT,
- mockNewStyleDomainObject;
-
- function asPromise(value, doCatch) {
- return (value || {}).then ? value : {
- then: function (callback) {
- return asPromise(callback(value));
- },
- catch: function (callback) {
- //Define a default 'happy' catch, that skips over the
- // catch callback
- return doCatch ? asPromise(callback(value)) : asPromise(value);
- }
- };
- }
-
- beforeEach(function () {
- model = {
- someKey: "some value",
- name: "domain object"
- };
-
- mockPersistenceService = jasmine.createSpyObj(
- "persistenceService",
- ["updateObject", "readObject", "createObject", "deleteObject"]
- );
-
- mockIdentifierService = jasmine.createSpyObj(
- 'identifierService',
- ['parse', 'generate']
- );
- mockIdentifier = jasmine.createSpyObj(
- 'identifier',
- ['getSpace', 'getKey', 'getDefinedSpace']
- );
- mockQ = jasmine.createSpyObj(
- "$q",
- ["reject", "when"]
- );
- mockNofificationService = jasmine.createSpyObj(
- "notificationService",
- ["error"]
- );
- mockCacheService = jasmine.createSpyObj(
- "cacheService",
- ["get", "put", "remove", "all"]
- );
-
- mockDomainObject = {
- getId: function () {
- return id;
- },
- getModel: function () {
- return model;
- },
- useCapability: jasmine.createSpy()
- };
-
- mockNewStyleDomainObject = Object.assign({}, model);
- mockNewStyleDomainObject.identifier = {
- namespace: SPACE,
- key: key
- };
-
- // Simulate mutation capability
- mockDomainObject.useCapability.and.callFake(function (capability, mutator) {
- if (capability === 'mutation') {
- model = mutator(model) || model;
- }
- });
-
- mockOpenMCT = {};
- mockOpenMCT.objects = jasmine.createSpyObj('Object API', ['save']);
-
- mockIdentifierService.parse.and.returnValue(mockIdentifier);
- mockIdentifier.getSpace.and.returnValue(SPACE);
- mockIdentifier.getKey.and.returnValue(key);
- mockQ.when.and.callFake(asPromise);
- persistence = new PersistenceCapability(
- mockCacheService,
- mockPersistenceService,
- mockIdentifierService,
- mockNofificationService,
- mockQ,
- mockOpenMCT,
- mockDomainObject
- );
- });
-
- describe("successful persistence", function () {
- beforeEach(function () {
- mockOpenMCT.objects.save.and.returnValue(Promise.resolve(true));
- });
- it("creates unpersisted objects with the persistence service", function () {
- // Verify precondition; no call made during constructor
- expect(mockOpenMCT.objects.save).not.toHaveBeenCalled();
-
- persistence.persist();
-
- expect(mockOpenMCT.objects.save).toHaveBeenCalledWith(mockNewStyleDomainObject);
- });
-
- it("reports which persistence space an object belongs to", function () {
- expect(persistence.getSpace()).toEqual(SPACE);
- });
-
- it("refreshes the domain object model from persistence", function () {
- var refreshModel = {someOtherKey: "some other value"};
- model.persisted = 1;
- mockPersistenceService.readObject.and.returnValue(asPromise(refreshModel));
- persistence.refresh();
- expect(model).toEqual(refreshModel);
- });
-
- it("does not trigger error notification on successful"
- + " persistence", function () {
- let rejected = false;
-
- return persistence.persist()
- .catch(() => rejected = true)
- .then(() => {
- expect(rejected).toBe(false);
- expect(mockNofificationService.error).not.toHaveBeenCalled();
- });
- });
- });
-
- describe("unsuccessful persistence", function () {
- beforeEach(function () {
- mockOpenMCT.objects.save.and.returnValue(Promise.resolve(false));
- });
- it("rejects on falsey persistence result", function () {
- let rejected = false;
-
- return persistence.persist()
- .catch(() => rejected = true)
- .then(() => {
- expect(rejected).toBe(true);
- });
- });
-
- it("notifies user on persistence failure", function () {
- let rejected = false;
-
- return persistence.persist()
- .catch(() => rejected = true)
- .then(() => {
- expect(rejected).toBe(true);
- expect(mockNofificationService.error).toHaveBeenCalled();
- });
- });
- });
- });
- }
-);
diff --git a/platform/core/test/capabilities/RelationshipCapabilitySpec.js b/platform/core/test/capabilities/RelationshipCapabilitySpec.js
deleted file mode 100644
index 8821714f2..000000000
--- a/platform/core/test/capabilities/RelationshipCapabilitySpec.js
+++ /dev/null
@@ -1,144 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * CompositionCapabilitySpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/capabilities/RelationshipCapability"],
- function (RelationshipCapability) {
-
- var DOMAIN_OBJECT_METHODS = [
- "getId",
- "getModel",
- "getCapability",
- "hasCapability",
- "useCapability"
- ];
-
- describe("The relationship capability", function () {
- var mockDomainObject,
- mockInjector,
- mockObjectService,
- relationship;
-
- // Composition Capability makes use of promise chaining,
- // so support that, but don't introduce complication of
- // native promises.
- function mockPromise(value) {
- return {
- then: function (callback) {
- return mockPromise(callback(value));
- }
- };
- }
-
- beforeEach(function () {
- mockDomainObject = jasmine.createSpyObj(
- "domainObject",
- DOMAIN_OBJECT_METHODS
- );
-
- mockObjectService = jasmine.createSpyObj(
- "objectService",
- ["getObjects"]
- );
-
- mockInjector = {
- get: function (name) {
- return (name === "objectService") && mockObjectService;
- }
- };
-
- mockObjectService.getObjects.and.returnValue(mockPromise([]));
-
- relationship = new RelationshipCapability(
- mockInjector,
- mockDomainObject
- );
- });
-
- it("applies only to models with a 'relationships' field", function () {
- expect(RelationshipCapability.appliesTo({ relationships: {} }))
- .toBeTruthy();
- expect(RelationshipCapability.appliesTo({}))
- .toBeFalsy();
- });
-
- it("requests ids found in model's composition from the object service", function () {
- var ids = ["a", "b", "c", "xyz"];
-
- mockDomainObject.getModel.and.returnValue({ relationships: { xyz: ids } });
-
- relationship.getRelatedObjects('xyz');
-
- expect(mockObjectService.getObjects).toHaveBeenCalledWith(ids);
- });
-
- it("provides a list of relationship types", function () {
- mockDomainObject.getModel.and.returnValue({
- relationships: {
- abc: ['a', 'b'],
- def: "not an array, should be ignored",
- xyz: []
- }
- });
- expect(relationship.listRelationships()).toEqual(['abc', 'xyz']);
- });
-
- it("avoids redundant requests", function () {
- // Lookups can be expensive, so this capability
- // should have some self-caching
- mockDomainObject.getModel
- .and.returnValue({ relationships: { xyz: ['a'] } });
-
- // Call twice; response should be the same object instance
- expect(relationship.getRelatedObjects('xyz'))
- .toBe(relationship.getRelatedObjects('xyz'));
-
- // Should have only made one call
- expect(mockObjectService.getObjects.calls.count())
- .toEqual(1);
- });
-
- it("makes new requests on modification", function () {
- // Lookups can be expensive, so this capability
- // should have some self-caching
- var testModel;
-
- testModel = { relationships: { xyz: ['a'] } };
-
- mockDomainObject.getModel.and.returnValue(testModel);
-
- // Call twice, but as if modification had occurred in between
- relationship.getRelatedObjects('xyz');
- testModel.modified = 123;
- relationship.getRelatedObjects('xyz');
-
- // Should have only made one call
- expect(mockObjectService.getObjects.calls.count())
- .toEqual(2);
- });
-
- });
- }
-);
diff --git a/platform/core/test/identifiers/IdentifierProviderSpec.js b/platform/core/test/identifiers/IdentifierProviderSpec.js
deleted file mode 100644
index a29ccd39b..000000000
--- a/platform/core/test/identifiers/IdentifierProviderSpec.js
+++ /dev/null
@@ -1,56 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../../src/identifiers/IdentifierProvider"],
- function (IdentifierProvider) {
-
- describe("IdentifierProvider", function () {
- var defaultSpace,
- provider;
-
- beforeEach(function () {
- defaultSpace = "some-default-space";
- provider = new IdentifierProvider(defaultSpace);
- });
-
- it("generates unique identifiers", function () {
- expect(provider.generate())
- .not.toEqual(provider.generate());
- });
-
- it("allows spaces to be specified for generated identifiers", function () {
- var specificSpace = "some-specific-space",
- id = provider.generate(specificSpace);
- expect(id).toEqual(jasmine.any(String));
- expect(provider.parse(id).getDefinedSpace())
- .toEqual(specificSpace);
- });
-
- it("parses identifiers using the default space", function () {
- expect(provider.parse("some-unprefixed-id").getSpace())
- .toEqual(defaultSpace);
- });
-
- });
- }
-);
diff --git a/platform/core/test/identifiers/IdentifierSpec.js b/platform/core/test/identifiers/IdentifierSpec.js
deleted file mode 100644
index 2025d97da..000000000
--- a/platform/core/test/identifiers/IdentifierSpec.js
+++ /dev/null
@@ -1,80 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../../src/identifiers/Identifier"],
- function (Identifier) {
-
- describe("A parsed domain object identifier", function () {
- var id,
- defaultSpace,
- identifier;
-
- beforeEach(function () {
- defaultSpace = "someDefaultSpace";
- });
-
- describe("when space is encoded", function () {
- var idSpace, idKey;
-
- beforeEach(function () {
- idSpace = "a-specific-space";
- idKey = "a-specific-key";
- id = idSpace + ":" + idKey;
- identifier = new Identifier(id, defaultSpace);
- });
-
- it("provides the encoded space", function () {
- expect(identifier.getSpace()).toEqual(idSpace);
- });
-
- it("provides the key within that space", function () {
- expect(identifier.getKey()).toEqual(idKey);
- });
-
- it("provides the defined space", function () {
- expect(identifier.getDefinedSpace()).toEqual(idSpace);
- });
- });
-
- describe("when space is not encoded", function () {
- beforeEach(function () {
- id = "a-generic-id";
- identifier = new Identifier(id, defaultSpace);
- });
-
- it("provides the default space", function () {
- expect(identifier.getSpace()).toEqual(defaultSpace);
- });
-
- it("provides the id as the key", function () {
- expect(identifier.getKey()).toEqual(id);
- });
-
- it("provides no defined space", function () {
- expect(identifier.getDefinedSpace()).toEqual(undefined);
- });
- });
-
- });
- }
-);
diff --git a/platform/core/test/models/CachingModelDecoratorSpec.js b/platform/core/test/models/CachingModelDecoratorSpec.js
deleted file mode 100644
index 1fcf4b3ac..000000000
--- a/platform/core/test/models/CachingModelDecoratorSpec.js
+++ /dev/null
@@ -1,157 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [
- "../../src/models/CachingModelDecorator",
- "../../src/models/ModelCacheService"
- ],
- function (CachingModelDecorator, ModelCacheService) {
-
- xdescribe("The caching model decorator", function () {
- var mockModelService,
- mockCallback,
- testModels,
- decorator;
-
- function asPromise(value) {
- return (value || {}).then ? value : {
- then: function (callback) {
- return asPromise(callback(value));
- }
- };
- }
-
- function fakePromise() {
- var chains = [],
- callbacks = [];
-
- return {
- then: function (callback) {
- var next = fakePromise();
- callbacks.push(callback);
- chains.push(next);
-
- return next;
- },
- resolve: function (value) {
- callbacks.forEach(function (cb, i) {
- chains[i].resolve(cb(value));
- });
- }
- };
- }
-
- beforeEach(function () {
- mockCallback = jasmine.createSpy();
- mockModelService = jasmine.createSpyObj('modelService', ['getModels']);
- testModels = {
- a: { someKey: "some value" },
- b: { someOtherKey: "some other value" }
- };
- mockModelService.getModels.and.returnValue(asPromise(testModels));
- decorator = new CachingModelDecorator(
- new ModelCacheService(),
- mockModelService
- );
- });
-
- it("loads models from its wrapped model service", function () {
- decorator.getModels(['a', 'b']).then(mockCallback);
- expect(mockCallback).toHaveBeenCalledWith(testModels);
- });
-
- it("does not try to reload cached models", function () {
- mockModelService.getModels.and.returnValue(asPromise({ a: testModels.a }));
- decorator.getModels(['a']);
- mockModelService.getModels.and.returnValue(asPromise(testModels));
- decorator.getModels(['a', 'b']);
- expect(mockModelService.getModels).not.toHaveBeenCalledWith(['a', 'b']);
- expect(mockModelService.getModels.calls.mostRecent().args[0]).toEqual(['b']);
- });
-
- it("does not call its wrapped model service if not needed", function () {
- decorator.getModels(['a', 'b']);
- expect(mockModelService.getModels.calls.count()).toEqual(1);
- decorator.getModels(['a', 'b']).then(mockCallback);
- expect(mockModelService.getModels.calls.count()).toEqual(1);
- // Verify that we still got back our models, even though
- // no new call to the wrapped service was made
- expect(mockCallback).toHaveBeenCalledWith(testModels);
- });
-
- it("ensures a single object instance, even for multiple concurrent calls", function () {
- var promiseA, promiseB;
-
- promiseA = fakePromise();
- promiseB = fakePromise();
-
- // Issue two calls before those promises resolve
- mockModelService.getModels.and.returnValue(promiseA);
- decorator.getModels(['a']);
- mockModelService.getModels.and.returnValue(promiseB);
- decorator.getModels(['a']).then(mockCallback);
-
- // Then resolve those promises. Note that we're whiteboxing here
- // to figure out which promises to resolve (that is, we know that
- // two thens are chained after each getModels)
- promiseA.resolve(testModels);
- promiseB.resolve({
- a: { someNewKey: "some other value" }
- });
-
- // Ensure that we have a pointer-identical instance
- expect(mockCallback.calls.mostRecent().args[0].a)
- .toEqual({ someNewKey: "some other value" });
- expect(mockCallback.calls.mostRecent().args[0].a)
- .toBe(testModels.a);
- });
-
- it("is robust against updating with undefined values", function () {
- var promiseA, promiseB;
-
- promiseA = fakePromise();
- promiseB = fakePromise();
-
- // Issue two calls before those promises resolve
- mockModelService.getModels.and.returnValue(promiseA);
- decorator.getModels(['a']);
- mockModelService.getModels.and.returnValue(promiseB);
- decorator.getModels(['a']).then(mockCallback);
-
- // Some model providers might erroneously add undefined values
- // under requested keys, so handle that
- promiseA.resolve({
- a: undefined
- });
- promiseB.resolve({
- a: { someNewKey: "some other value" }
- });
-
- // Should still have gotten the model
- expect(mockCallback.calls.mostRecent().args[0].a)
- .toEqual({ someNewKey: "some other value" });
- });
-
- });
- }
-);
diff --git a/platform/core/test/models/ModelAggregatorSpec.js b/platform/core/test/models/ModelAggregatorSpec.js
deleted file mode 100644
index 3f9423491..000000000
--- a/platform/core/test/models/ModelAggregatorSpec.js
+++ /dev/null
@@ -1,84 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * ModelAggregatorSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/models/ModelAggregator"],
- function (ModelAggregator) {
-
- describe("The model aggregator", function () {
- var mockQ,
- mockProviders,
- modelList = [
- {
- "a": { someKey: "some value" },
- "b": undefined
- },
- {
- "b": { someOtherKey: "some other value" },
- "a": undefined
- }
- ],
- aggregator;
-
- beforeEach(function () {
- mockQ = jasmine.createSpyObj("$q", ["all"]);
- mockProviders = modelList.map(function (models, i) {
- var mockProvider = jasmine.createSpyObj(
- "mockProvider" + i,
- ["getModels"]
- );
- mockProvider.getModels.and.returnValue(models);
-
- return mockProvider;
- });
-
- mockQ.all.and.returnValue({
- then: function (c) {
- return c(modelList);
- }
- });
-
- aggregator = new ModelAggregator(mockQ, mockProviders);
- });
-
- it("aggregates results promised by multiple providers", function () {
- expect(aggregator.getModels(["a", "b"])).toEqual({
- "a": { someKey: "some value" },
- "b": { someOtherKey: "some other value" }
- });
- });
-
- it("passes ids to all aggregated providers", function () {
- aggregator.getModels(["a", "b"]);
-
- mockProviders.forEach(function (mockProvider) {
- expect(mockProvider.getModels)
- .toHaveBeenCalledWith(["a", "b"]);
- });
- });
-
- });
- }
-);
diff --git a/platform/core/test/models/ModelCacheServiceSpec.js b/platform/core/test/models/ModelCacheServiceSpec.js
deleted file mode 100644
index 134146e79..000000000
--- a/platform/core/test/models/ModelCacheServiceSpec.js
+++ /dev/null
@@ -1,68 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(['../../src/models/ModelCacheService'], function (ModelCacheService) {
- describe("ModelCacheService", function () {
- var testIds,
- testModels,
- cacheService;
-
- beforeEach(function () {
- testIds = ['a', 'b', 'c', 'd'];
- testModels = testIds.reduce(function (models, id) {
- models[id] = { someKey: "some value for " + id };
-
- return models;
- }, {});
- cacheService = new ModelCacheService();
- });
-
- describe("when populated with models", function () {
- beforeEach(function () {
- testIds.forEach(function (id) {
- cacheService.put(id, testModels[id]);
- });
- });
-
- it("indicates that it has these models", function () {
- testIds.forEach(function (id) {
- expect(cacheService.has(id)).toBe(true);
- });
- });
-
- it("provides all of these models", function () {
- expect(cacheService.all()).toEqual(testModels);
- });
-
- it("allows models to be retrieved", function () {
- testIds.forEach(function (id) {
- expect(cacheService.get(id)).toEqual(testModels[id]);
- });
- });
-
- it("allows models to be removed", function () {
- cacheService.remove('a');
- expect(cacheService.has('a')).toBe(false);
- });
- });
- });
-});
diff --git a/platform/core/test/models/PersistedModelProviderSpec.js b/platform/core/test/models/PersistedModelProviderSpec.js
deleted file mode 100644
index 3aca93544..000000000
--- a/platform/core/test/models/PersistedModelProviderSpec.js
+++ /dev/null
@@ -1,173 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * PersistedModelProviderSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/models/PersistedModelProvider"],
- function (PersistedModelProvider) {
-
- describe("The persisted model provider", function () {
- var mockQ,
- mockPersistenceService,
- SPACE = "space0",
- modTimes,
- mockNow,
- provider;
-
- function mockPromise(value) {
- return (value || {}).then ? value : {
- then: function (callback) {
- return mockPromise(callback(value));
- },
- testValue: value
- };
- }
-
- function mockAll(mockPromises) {
- return mockPromise(mockPromises.map(function (p) {
- return p.testValue;
- }));
- }
-
- beforeEach(function () {
- modTimes = {};
- mockQ = {
- when: mockPromise,
- all: mockAll
- };
- mockPersistenceService = jasmine.createSpyObj(
- 'persistenceService',
- [
- 'createObject',
- 'readObject',
- 'updateObject',
- 'deleteObject',
- 'listSpaces',
- 'listObjects'
- ]
- );
- mockNow = jasmine.createSpy("now");
-
- mockPersistenceService.readObject
- .and.callFake(function (space, id) {
- return mockPromise({
- space: space,
- id: id,
- modified: (modTimes[space] || {})[id],
- persisted: 0
- });
- });
- mockPersistenceService.listSpaces
- .and.returnValue(mockPromise([SPACE]));
-
- provider = new PersistedModelProvider(
- mockPersistenceService,
- mockQ,
- mockNow,
- SPACE
- );
- });
-
- it("reads object models from persistence", function () {
- var models;
-
- provider.getModels(["a", "x", "zz"]).then(function (m) {
- models = m;
- });
-
- expect(models).toEqual({
- a: {
- space: SPACE,
- id: "a",
- persisted: 0,
- modified: undefined
- },
- x: {
- space: SPACE,
- id: "x",
- persisted: 0,
- modified: undefined
- },
- zz: {
- space: SPACE,
- id: "zz",
- persisted: 0,
- modified: undefined
- }
- });
- });
-
- it("ensures that persisted timestamps are present", function () {
- var mockCallback = jasmine.createSpy("callback"),
- testModels = {
- a: {
- modified: 123,
- persisted: 1984,
- name: "A"
- },
- b: {
- persisted: 1977,
- name: "B"
- },
- c: {
- modified: 42,
- name: "C"
- },
- d: { name: "D" }
- };
-
- mockPersistenceService.readObject.and.callFake(
- function (space, id) {
- return mockPromise(testModels[id]);
- }
- );
- mockNow.and.returnValue(12321);
-
- provider.getModels(Object.keys(testModels)).then(mockCallback);
-
- expect(mockCallback).toHaveBeenCalledWith({
- a: {
- modified: 123,
- persisted: 1984,
- name: "A"
- },
- b: {
- persisted: 1977,
- name: "B"
- },
- c: {
- modified: 42,
- persisted: 42,
- name: "C"
- },
- d: {
- persisted: 12321,
- name: "D"
- }
- });
- });
-
- });
- }
-);
diff --git a/platform/core/test/models/StaticModelProviderSpec.js b/platform/core/test/models/StaticModelProviderSpec.js
deleted file mode 100644
index 8f2dd59f6..000000000
--- a/platform/core/test/models/StaticModelProviderSpec.js
+++ /dev/null
@@ -1,109 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * StaticModelProviderSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/models/StaticModelProvider"],
- function (StaticModelProvider) {
-
- describe("The static model provider", function () {
- var models = [
- {
- "id": "a",
- "model": {
- "name": "Thing A",
- "someProperty": "Some Value A"
- }
- },
- {
- "id": "b",
- "model": {
- "name": "Thing B",
- "someProperty": "Some Value B"
- }
- }
- ],
- mockLog,
- mockQ,
- provider;
-
- beforeEach(function () {
- mockQ = jasmine.createSpyObj("$q", ["when"]);
- mockLog = jasmine.createSpyObj("$log", ["error", "warn", "info", "debug"]);
- provider = new StaticModelProvider(models, mockQ, mockLog);
- });
-
- it("provides models from extension declarations", function () {
- var mockPromise = {
- then: function () {
- return;
- }
- };
- mockQ.when.and.returnValue(mockPromise);
-
- // Verify that we got the promise as the return value
- expect(provider.getModels(["a", "b"])).toEqual(mockPromise);
-
- // Verify that the promise has the desired models
- expect(mockQ.when.calls.count()).toEqual(1);
- expect(mockQ.when.calls.mostRecent().args[0].a.name).toEqual("Thing A");
- expect(mockQ.when.calls.mostRecent().args[0].a.someProperty).toEqual("Some Value A");
- expect(mockQ.when.calls.mostRecent().args[0].b.name).toEqual("Thing B");
- expect(mockQ.when.calls.mostRecent().args[0].b.someProperty).toEqual("Some Value B");
- });
-
- it("does not provide models which are not in extension declarations", function () {
- provider.getModels(["c"]);
-
- // Verify that the promise has the desired models
- expect(mockQ.when.calls.count()).toEqual(1);
- expect(mockQ.when.calls.mostRecent().args[0].c).toBeUndefined();
- });
-
- it("logs a warning when model definitions are malformed", function () {
- // Verify precondition
- expect(mockLog.warn).not.toHaveBeenCalled();
-
- // Shouldn't fail with an exception
- expect(new StaticModelProvider([
- { "bad": "no id" },
- { "id": "...but no model..." },
- { "model": "...and no id..." },
- {
- "id": -40,
- "model": {}
- },
- {
- "model": "should be an object",
- "id": "x"
- }
- ], mockQ, mockLog)).toBeDefined();
-
- // Should show warnings
- expect(mockLog.warn.calls.count()).toEqual(5);
- });
-
- });
- }
-);
diff --git a/platform/core/test/objects/DomainObjectProviderSpec.js b/platform/core/test/objects/DomainObjectProviderSpec.js
deleted file mode 100644
index ffab540d4..000000000
--- a/platform/core/test/objects/DomainObjectProviderSpec.js
+++ /dev/null
@@ -1,86 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * DomainObjectProviderSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- [
- "../../src/objects/DomainObjectProvider",
- "../../src/objects/DomainObjectImpl"
- ],
- function (DomainObjectProvider, DomainObjectImpl) {
-
- describe("The domain object provider", function () {
- var mockModelService,
- mockInstantiate,
- provider;
-
- function mockPromise(value) {
- return (value && value.then) ? value : {
- then: function (callback) {
- return mockPromise(callback(value));
- },
- // Provide a synchronous way to get a value out
- // of this phony promise.
- testValue: value
- };
- }
-
- beforeEach(function () {
- mockModelService = jasmine.createSpyObj(
- "modelService",
- ["getModels"]
- );
- mockInstantiate = jasmine.createSpy("instantiate");
-
- mockInstantiate.and.callFake(function (model, id) {
- return new DomainObjectImpl(id, model, {});
- });
-
- provider = new DomainObjectProvider(
- mockModelService,
- mockInstantiate
- );
- });
-
- it("requests models from the model service", function () {
- var ids = ["a", "b", "c"];
- mockModelService.getModels.and.returnValue(mockPromise({}));
- provider.getObjects(ids);
- expect(mockModelService.getModels).toHaveBeenCalledWith(ids);
- });
-
- it("instantiates objects with provided models", function () {
- var ids = ["a", "b", "c"],
- model = { someKey: "some value"},
- result;
- mockModelService.getModels.and.returnValue(mockPromise({ a: model }));
- result = provider.getObjects(ids).testValue;
- expect(mockInstantiate).toHaveBeenCalledWith(model, 'a');
- expect(result.a.getId()).toEqual("a");
- expect(result.a.getModel()).toEqual(model);
- });
-
- });
- }
-);
diff --git a/platform/core/test/objects/DomainObjectSpec.js b/platform/core/test/objects/DomainObjectSpec.js
deleted file mode 100644
index b024edb03..000000000
--- a/platform/core/test/objects/DomainObjectSpec.js
+++ /dev/null
@@ -1,87 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * DomainObjectSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/objects/DomainObjectImpl"],
- function (DomainObject) {
-
- describe("A domain object", function () {
- var testId = "test id",
- testModel = { someKey: "some value"},
- testCapabilities = {
- "static": "some static capability",
- "dynamic": function (domainObject) {
- return "Dynamically generated for "
- + domainObject.getId();
- },
- "invokable": {
- invoke: function (arg) {
- return "invoked with " + arg;
- }
- }
- },
- domainObject;
-
- beforeEach(function () {
- domainObject = new DomainObject(
- testId,
- testModel,
- testCapabilities
- );
- });
-
- it("reports its id", function () {
- expect(domainObject.getId()).toEqual(testId);
- });
-
- it("reports its model", function () {
- expect(domainObject.getModel()).toEqual(testModel);
- });
-
- it("reports static capabilities", function () {
- expect(domainObject.getCapability("static"))
- .toEqual("some static capability");
- });
-
- it("instantiates dynamic capabilities", function () {
- expect(domainObject.getCapability("dynamic"))
- .toEqual("Dynamically generated for test id");
- });
-
- it("allows for checking for the presence of capabilities", function () {
- Object.keys(testCapabilities).forEach(function (capability) {
- expect(domainObject.hasCapability(capability)).toBeTruthy();
- });
- expect(domainObject.hasCapability("somethingElse")).toBeFalsy();
- });
-
- it("allows for shorthand capability invocation", function () {
- expect(domainObject.useCapability("invokable", "a specific value"))
- .toEqual("invoked with a specific value");
- });
-
- });
- }
-);
diff --git a/platform/core/test/services/InstantiateSpec.js b/platform/core/test/services/InstantiateSpec.js
deleted file mode 100644
index ffd2e281d..000000000
--- a/platform/core/test/services/InstantiateSpec.js
+++ /dev/null
@@ -1,108 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../../src/services/Instantiate"],
- function (Instantiate) {
-
- describe("The 'instantiate' service", function () {
-
- var mockCapabilityService,
- mockIdentifierService,
- mockCapabilityConstructor,
- mockCapabilityInstance,
- mockCacheService,
- idCounter,
- testModel,
- instantiate,
- domainObject;
-
- beforeEach(function () {
- idCounter = 0;
-
- mockCapabilityService = jasmine.createSpyObj(
- 'capabilityService',
- ['getCapabilities']
- );
- mockIdentifierService = jasmine.createSpyObj(
- 'identifierService',
- ['parse', 'generate']
- );
- mockCapabilityConstructor = jasmine.createSpy('capability');
- mockCapabilityInstance = {};
- mockCapabilityService.getCapabilities.and.returnValue({
- something: mockCapabilityConstructor
- });
- mockCapabilityConstructor.and.returnValue(mockCapabilityInstance);
-
- mockIdentifierService.generate.and.callFake(function (space) {
- return (space ? (space + ":") : "")
- + "some-id-" + (idCounter += 1);
- });
-
- mockCacheService = jasmine.createSpyObj(
- 'cacheService',
- ['get', 'put', 'remove', 'all']
- );
-
- testModel = { someKey: "some value" };
-
- instantiate = new Instantiate(
- mockCapabilityService,
- mockIdentifierService,
- mockCacheService
- );
- domainObject = instantiate(testModel);
- });
-
- it("loads capabilities from the capability service", function () {
- expect(mockCapabilityService.getCapabilities)
- .toHaveBeenCalledWith(testModel);
- });
-
- it("exposes loaded capabilities from the created object", function () {
- expect(domainObject.getCapability('something'))
- .toBe(mockCapabilityInstance);
- expect(mockCapabilityConstructor)
- .toHaveBeenCalledWith(domainObject);
- });
-
- it("exposes the provided model", function () {
- expect(domainObject.getModel()).toEqual(testModel);
- });
-
- it("provides unique identifiers", function () {
- expect(domainObject.getId()).toEqual(jasmine.any(String));
- expect(instantiate(testModel).getId())
- .not.toEqual(domainObject.getId());
- });
-
- it("caches the instantiated model", function () {
- expect(mockCacheService.put).toHaveBeenCalledWith(
- domainObject.getId(),
- testModel
- );
- });
- });
-
- }
-);
diff --git a/platform/core/test/services/ThrottleSpec.js b/platform/core/test/services/ThrottleSpec.js
deleted file mode 100644
index d53b57d6d..000000000
--- a/platform/core/test/services/ThrottleSpec.js
+++ /dev/null
@@ -1,71 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../../src/services/Throttle"],
- function (Throttle) {
-
- describe("The 'throttle' service", function () {
- var throttle,
- mockTimeout,
- mockFn,
- mockPromise;
-
- beforeEach(function () {
- mockTimeout = jasmine.createSpy("$timeout");
- mockPromise = jasmine.createSpyObj("promise", ["then"]);
- mockFn = jasmine.createSpy("fn");
- mockTimeout.and.returnValue(mockPromise);
- throttle = new Throttle(mockTimeout);
- });
-
- it("provides functions which run on a timeout", function () {
- var throttled = throttle(mockFn);
- // Verify precondition: Not called at throttle-time
- expect(mockTimeout).not.toHaveBeenCalled();
- expect(throttled()).toEqual(mockPromise);
- expect(mockFn).not.toHaveBeenCalled();
- expect(mockTimeout)
- .toHaveBeenCalledWith(jasmine.any(Function), 0, false);
- });
-
- it("schedules only one timeout at a time", function () {
- var throttled = throttle(mockFn);
- throttled();
- throttled();
- throttled();
- expect(mockTimeout.calls.count()).toEqual(1);
- });
-
- it("schedules additional invocations after resolution", function () {
- var throttled = throttle(mockFn);
- throttled();
- mockTimeout.calls.mostRecent().args[0](); // Resolve timeout
- throttled();
- mockTimeout.calls.mostRecent().args[0]();
- throttled();
- mockTimeout.calls.mostRecent().args[0]();
- expect(mockTimeout.calls.count()).toEqual(3);
- });
- });
- }
-);
diff --git a/platform/core/test/services/TopicSpec.js b/platform/core/test/services/TopicSpec.js
deleted file mode 100644
index 2bbd86dc1..000000000
--- a/platform/core/test/services/TopicSpec.js
+++ /dev/null
@@ -1,88 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../../src/services/Topic"],
- function (Topic) {
-
- describe("The 'topic' service", function () {
- var topic,
- mockLog,
- testMessage,
- mockCallback;
-
- beforeEach(function () {
- testMessage = { someKey: "some value"};
- mockLog = jasmine.createSpyObj(
- '$log',
- ['error', 'warn', 'info', 'debug']
- );
- mockCallback = jasmine.createSpy('callback');
- topic = new Topic(mockLog);
- });
-
- it("notifies listeners on a topic", function () {
- topic("abc").listen(mockCallback);
- topic("abc").notify(testMessage);
- expect(mockCallback).toHaveBeenCalledWith(testMessage);
- });
-
- it("does not notify listeners across topics", function () {
- topic("abc").listen(mockCallback);
- topic("xyz").notify(testMessage);
- expect(mockCallback).not.toHaveBeenCalledWith(testMessage);
- });
-
- it("does not notify listeners after unlistening", function () {
- topic("abc").listen(mockCallback)(); // Unlisten immediately
- topic("abc").notify(testMessage);
- expect(mockCallback).not.toHaveBeenCalledWith(testMessage);
- });
-
- it("provides anonymous private topics", function () {
- var t1 = topic(), t2 = topic();
-
- t1.listen(mockCallback);
- t2.notify(testMessage);
- expect(mockCallback).not.toHaveBeenCalledWith(testMessage);
- t1.notify(testMessage);
- expect(mockCallback).toHaveBeenCalledWith(testMessage);
- });
-
- it("is robust against errors thrown by listeners", function () {
- var mockBadCallback = jasmine.createSpy("bad-callback"),
- t = topic();
-
- mockBadCallback.and.callFake(function () {
- throw new Error("I'm afraid I can't do that.");
- });
-
- t.listen(mockBadCallback);
- t.listen(mockCallback);
-
- t.notify(testMessage);
- expect(mockCallback).toHaveBeenCalledWith(testMessage);
- });
-
- });
- }
-);
diff --git a/platform/core/test/types/MergeModelsSpec.js b/platform/core/test/types/MergeModelsSpec.js
deleted file mode 100644
index 47276a32a..000000000
--- a/platform/core/test/types/MergeModelsSpec.js
+++ /dev/null
@@ -1,63 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * MergeModelsSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/types/MergeModels"],
- function (mergeModels) {
-
- describe("Model merger", function () {
- it("merges models", function () {
- expect(mergeModels(
- {
- "a": "property a",
- "b": [1, 2, 3],
- "c": {
- x: 42,
- z: [0]
- },
- "d": "should be ignored"
- },
- {
- "b": [4],
- "c": {
- y: "property y",
- z: ["h"]
- },
- "d": "property d"
- }
- )).toEqual({
- "a": "property a",
- "b": [1, 2, 3, 4],
- "c": {
- x: 42,
- y: "property y",
- z: [0, "h"]
- },
- "d": "property d"
- });
- });
- });
- }
-);
diff --git a/platform/core/test/types/TypeCapabilitySpec.js b/platform/core/test/types/TypeCapabilitySpec.js
deleted file mode 100644
index aa76c8668..000000000
--- a/platform/core/test/types/TypeCapabilitySpec.js
+++ /dev/null
@@ -1,66 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * TypeCapabilitySpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/types/TypeCapability"],
- function (TypeCapability) {
-
- describe("The type capability", function () {
- var mockTypeService,
- mockDomainObject,
- mockType,
- type;
-
- beforeEach(function () {
- mockTypeService = jasmine.createSpyObj(
- "typeService",
- ["getType"]
- );
- mockDomainObject = jasmine.createSpyObj(
- "domainObject",
- ["getId", "getModel", "getCapability"]
- );
- mockType = {
- someKey: "some value",
- someOtherProperty: "some other property",
- aThirdProperty: "a third property"
- };
-
- mockTypeService.getType.and.returnValue(mockType);
- mockDomainObject.getModel.and.returnValue({type: "mockType"});
-
- type = new TypeCapability(mockTypeService, mockDomainObject);
- });
-
- it("looks up an object's type from type service", function () {
- expect(type.someKey).toEqual(mockType.someKey);
- expect(type.someOtherProperty).toEqual(mockType.someOtherProperty);
- expect(type.aThirdProperty).toEqual(mockType.aThirdProperty);
- expect(mockTypeService.getType).toHaveBeenCalledWith("mockType");
- });
-
- });
- }
-);
diff --git a/platform/core/test/types/TypeImplSpec.js b/platform/core/test/types/TypeImplSpec.js
deleted file mode 100644
index 8a64f3994..000000000
--- a/platform/core/test/types/TypeImplSpec.js
+++ /dev/null
@@ -1,117 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ['../../src/types/TypeImpl'],
- function (TypeImpl) {
-
- describe("Type definition wrapper", function () {
- var testTypeDef,
- type;
-
- beforeEach(function () {
- testTypeDef = {
- key: 'test-type',
- name: 'Test Type',
- description: 'A type, for testing',
- cssClass: 'icon-telemetry-panel',
- inherits: ['test-parent-1', 'test-parent-2'],
- features: ['test-feature-1'],
- properties: [{}],
- model: {someKey: "some value"}
- };
- type = new TypeImpl(testTypeDef);
- });
-
- it("exposes key from definition", function () {
- expect(type.getKey()).toEqual('test-type');
- });
-
- it("exposes name from definition", function () {
- expect(type.getName()).toEqual('Test Type');
- });
-
- it("exposes description from definition", function () {
- expect(type.getDescription()).toEqual('A type, for testing');
- });
-
- it("exposes CSS class from definition", function () {
- expect(type.getCssClass()).toEqual('icon-telemetry-panel');
- });
-
- it("exposes its underlying type definition", function () {
- expect(type.getDefinition()).toEqual(testTypeDef);
- });
-
- it("supports instance-of checks by type key", function () {
- expect(type.instanceOf('test-parent-1')).toBeTruthy();
- expect(type.instanceOf('test-parent-2')).toBeTruthy();
- expect(type.instanceOf('some-other-type')).toBeFalsy();
- });
-
- it("supports instance-of checks by specific type key", function () {
- expect(type.instanceOf('test-type')).toBeTruthy();
- });
-
- it("supports instance-of checks by type object", function () {
- expect(type.instanceOf({
- getKey: function () {
- return 'test-parent-1';
- }
- })).toBeTruthy();
- expect(type.instanceOf({
- getKey: function () {
- return 'some-other-type';
- }
- })).toBeFalsy();
- });
-
- it("correctly recognizes instance-of checks upon itself", function () {
- expect(type.instanceOf(type)).toBeTruthy();
- });
-
- it("recognizes that all types are instances of the undefined type", function () {
- expect(type.instanceOf()).toBeTruthy();
- expect(type.instanceOf({ getKey: function () {} })).toBeTruthy();
- });
-
- it("allows features to be exposed", function () {
- expect(type.hasFeature('test-feature-1')).toBeTruthy();
- expect(type.hasFeature('test-feature-2')).toBeFalsy();
- });
-
- it("provides an initial model, if defined", function () {
- expect(type.getInitialModel().someKey).toEqual("some value");
- });
-
- it("provides a fresh initial model each time", function () {
- var model = type.getInitialModel();
- model.someKey = "some other value";
- expect(type.getInitialModel().someKey).toEqual("some value");
- });
-
- it("provides type properties", function () {
- expect(type.getProperties().length).toEqual(1);
- });
- });
- }
-);
diff --git a/platform/core/test/types/TypePropertyConversionSpec.js b/platform/core/test/types/TypePropertyConversionSpec.js
deleted file mode 100644
index c62e1047f..000000000
--- a/platform/core/test/types/TypePropertyConversionSpec.js
+++ /dev/null
@@ -1,65 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ['../../src/types/TypePropertyConversion'],
- function (TypePropertyConversion) {
-
- describe("Type property conversion", function () {
-
- it("allows non-conversion when parameter is 'identity'", function () {
- var conversion = new TypePropertyConversion("identity");
- [42, "42", { a: 42 }].forEach(function (v) {
- expect(conversion.toFormValue(v)).toBe(v);
- expect(conversion.toModelValue(v)).toBe(v);
- });
- });
-
- it("allows numeric conversion", function () {
- var conversion = new TypePropertyConversion("number");
- expect(conversion.toFormValue(42)).toBe("42");
- expect(conversion.toModelValue("42")).toBe(42);
- });
-
- it("supports array conversions", function () {
- var conversion = new TypePropertyConversion("number[]");
- expect(conversion.toFormValue([42, 44]).length).toEqual(2);
- expect(conversion.toFormValue([42, 44])[0]).toBe("42");
- expect(conversion.toModelValue(["11", "42"])[1]).toBe(42);
- });
-
- it("throws exceptions on unrecognized conversions", function () {
- var caught = false;
-
- try {
- // eslint-disable-next-line
- new TypePropertyConversion("some-unknown-conversion");
- } catch (e) {
- caught = true;
- }
-
- expect(caught).toBeTruthy();
- });
-
- });
- }
-);
diff --git a/platform/core/test/types/TypePropertySpec.js b/platform/core/test/types/TypePropertySpec.js
deleted file mode 100644
index 4fb32a2e7..000000000
--- a/platform/core/test/types/TypePropertySpec.js
+++ /dev/null
@@ -1,129 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ['../../src/types/TypeProperty'],
- function (TypeProperty) {
-
- describe("Type property", function () {
-
- it("allows retrieval of its definition", function () {
- var definition = {
- key: "hello",
- someOtherKey: "hm?"
- };
- expect(
- new TypeProperty(definition).getDefinition()
- ).toEqual(definition);
- });
-
- it("sets properties in object models", function () {
- var definition = {
- key: "someKey",
- property: "someProperty"
- },
- model = {},
- property = new TypeProperty(definition);
- property.setValue(model, "some value");
- expect(model.someProperty).toEqual("some value");
- });
-
- it("gets properties from object models", function () {
- var definition = {
- key: "someKey",
- property: "someProperty"
- },
- model = { someProperty: "some value"},
- property = new TypeProperty(definition);
- expect(property.getValue(model)).toEqual("some value");
- });
-
- it("sets properties by path", function () {
- var definition = {
- key: "someKey",
- property: ["some", "property"]
- },
- model = {},
- property = new TypeProperty(definition);
- property.setValue(model, "some value");
- expect(model.some.property).toEqual("some value");
- });
-
- it("gets properties by path", function () {
- var definition = {
- key: "someKey",
- property: ["some", "property"]
- },
- model = { some: { property: "some value" } },
- property = new TypeProperty(definition);
- expect(property.getValue(model)).toEqual("some value");
- });
-
- it("stops looking for properties when a path is invalid", function () {
- var definition = {
- key: "someKey",
- property: ["some", "property"]
- },
- property = new TypeProperty(definition);
- expect(property.getValue(undefined)).toBeUndefined();
- });
-
- it("gives undefined for empty paths", function () {
- var definition = {
- key: "someKey",
- property: []
- },
- model = { some: { property: "some value" } },
- property = new TypeProperty(definition);
- expect(property.getValue(model)).toBeUndefined();
- });
-
- it("provides empty arrays for values that are array-like", function () {
- var definition = {
- property: "someProperty",
- items: [{}, {}, {}]
- },
- model = {},
- property = new TypeProperty(definition);
- expect(property.getValue(model))
- .toEqual([undefined, undefined, undefined]);
- });
-
- it("detects and ignores empty arrays on setValue", function () {
- var definition = {
- property: "someProperty",
- items: [{}, {}, {}]
- },
- model = {},
- property = new TypeProperty(definition);
-
- property.setValue(model, [undefined, undefined, undefined]);
- expect(model.someProperty).toBeUndefined();
-
- // Verify that this only happens when all are undefined
- property.setValue(model, [undefined, "x", 42]);
- expect(model.someProperty).toEqual([undefined, "x", 42]);
- });
-
- });
- }
-);
diff --git a/platform/core/test/types/TypeProviderSpec.js b/platform/core/test/types/TypeProviderSpec.js
deleted file mode 100644
index d3e293fde..000000000
--- a/platform/core/test/types/TypeProviderSpec.js
+++ /dev/null
@@ -1,147 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ['../../src/types/TypeProvider'],
- function (TypeProvider) {
-
- describe("Type provider", function () {
-
- var captured = {},
- testTypeDefinitions = [
- {
- key: 'basic',
- cssClass: "icon-magnify-in",
- name: "Basic Type"
- },
- {
- key: 'multi1',
- cssClass: "icon-trash",
- description: "Multi1 Description",
- capabilities: ['a1', 'b1']
- },
- {
- key: 'multi2',
- cssClass: "icon-magnify-out",
- capabilities: ['a2', 'b2', 'c2']
- },
- {
- key: 'single-subtype',
- inherits: 'basic',
- name: "Basic Subtype",
- description: "A test subtype"
- },
- {
- key: 'multi-subtype',
- inherits: ['multi1', 'multi2'],
- name: "Multi-parent Subtype",
- capabilities: ['a3']
- },
- {
- name: "Default"
- }
- ],
- provider;
-
- beforeEach(function () {
- captured = {};
- provider = new TypeProvider(testTypeDefinitions);
- });
-
- it("looks up non-inherited types by name", function () {
- captured.type = provider.getType('basic');
-
- expect(captured.type.getCssClass()).toEqual("icon-magnify-in");
- expect(captured.type.getName()).toEqual("Basic Type");
- expect(captured.type.getDescription()).toBeUndefined();
- });
-
- it("supports single inheritance", function () {
- captured.type = provider.getType('single-subtype');
-
- expect(captured.type.getCssClass()).toEqual("icon-magnify-in");
- expect(captured.type.getName()).toEqual("Basic Subtype");
- expect(captured.type.getDescription()).toEqual("A test subtype");
- });
-
- it("supports multiple inheritance", function () {
- captured.type = provider.getType('multi-subtype');
-
- expect(captured.type.getCssClass()).toEqual("icon-magnify-out");
- expect(captured.type.getName()).toEqual("Multi-parent Subtype");
- expect(captured.type.getDescription()).toEqual("Multi1 Description");
- });
-
- it("concatenates capabilities in order", function () {
- captured.type = provider.getType('multi-subtype');
-
- expect(captured.type.getDefinition().capabilities).toEqual(
- ['a1', 'b1', 'a2', 'b2', 'c2', 'a3']
- );
- });
-
- it("allows lookup of the undefined type", function () {
- captured.type = provider.getType(undefined);
-
- expect(captured.type.getName()).toEqual("Default");
- });
-
- it("concatenates capabilities of all undefined types", function () {
- captured.type = new TypeProvider(
- testTypeDefinitions.concat([
- { capabilities: ['a', 'b', 'c'] },
- { capabilities: ['x', 'y', 'z'] }
- ])
- ).getType(undefined);
-
- expect(captured.type.getDefinition().capabilities).toEqual(
- ['a', 'b', 'c', 'x', 'y', 'z']
- );
-
- });
-
- it("includes capabilities from undefined type in all types", function () {
- captured.type = new TypeProvider(
- testTypeDefinitions.concat([
- { capabilities: ['a', 'b', 'c'] },
- { capabilities: ['x', 'y', 'z'] }
- ])
- ).getType('multi-subtype');
-
- expect(captured.type.getDefinition().capabilities).toEqual(
- ['a', 'b', 'c', 'x', 'y', 'z', 'a1', 'b1', 'a2', 'b2', 'c2', 'a3']
- );
- });
-
- it("allows types to be listed", function () {
- captured.types = provider.listTypes();
-
- expect(captured.types.length).toEqual(
- testTypeDefinitions.filter(function (t) {
- return t.key;
- }).length
- );
- });
-
- });
- }
-);
diff --git a/platform/core/test/views/ViewCapabilitySpec.js b/platform/core/test/views/ViewCapabilitySpec.js
deleted file mode 100644
index e6085fc41..000000000
--- a/platform/core/test/views/ViewCapabilitySpec.js
+++ /dev/null
@@ -1,58 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * ViewCapabilitySpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/views/ViewCapability"],
- function (ViewCapability) {
-
- describe("A view capability", function () {
- var mockViewService,
- mockDomainObject,
- views = [{key: "someView"}],
- view;
-
- beforeEach(function () {
- mockViewService = jasmine.createSpyObj(
- "viewService",
- ["getViews"]
- );
- mockDomainObject = jasmine.createSpyObj(
- "domainObject",
- ["getId", "getModel", "getCapability"]
- );
- mockViewService.getViews.and.returnValue(views);
- view = new ViewCapability(mockViewService, mockDomainObject);
- });
-
- it("issues invocations to the view service", function () {
- expect(view.invoke()).toEqual(views);
- expect(mockViewService.getViews).toHaveBeenCalledWith(
- mockDomainObject
- );
- });
-
- });
- }
-);
diff --git a/platform/core/test/views/ViewProviderSpec.js b/platform/core/test/views/ViewProviderSpec.js
deleted file mode 100644
index 8682c8afc..000000000
--- a/platform/core/test/views/ViewProviderSpec.js
+++ /dev/null
@@ -1,167 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * ViewProviderSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/views/ViewProvider"],
- function (ViewProvider) {
-
- describe("The view provider", function () {
- var viewA = {
- key: "a"
- },
- viewB = {
- key: "b",
- needs: ["someCapability"]
- },
- viewC = {
- key: "c",
- needs: ["someCapability"],
- delegation: true
- },
- capabilities = {},
- delegates = {},
- delegation,
- mockDomainObject = {},
- mockLog,
- provider;
-
- beforeEach(function () {
- // Simulate the expected API
- mockDomainObject.hasCapability = function (c) {
- return capabilities[c] !== undefined;
- };
-
- mockDomainObject.getCapability = function (c) {
- return capabilities[c];
- };
-
- mockDomainObject.useCapability = function (c, v) {
- return capabilities[c] && capabilities[c].invoke(v);
- };
-
- mockLog = jasmine.createSpyObj("$log", ["warn", "info", "debug"]);
-
- capabilities = {};
- delegates = {};
-
- delegation = {
- doesDelegateCapability: function (c) {
- return delegates[c] !== undefined;
- }
- };
-
- provider = new ViewProvider([viewA, viewB, viewC], mockLog);
- });
-
- it("reports views provided as extensions", function () {
- capabilities.someCapability = true;
- expect(provider.getViews(mockDomainObject))
- .toEqual([viewA, viewB, viewC]);
- });
-
- it("filters views by needed capabilities", function () {
- //capabilities.someCapability = true;
- expect(provider.getViews(mockDomainObject))
- .toEqual([viewA]);
- });
-
- it("allows delegation of needed capabilities when specified", function () {
- //capabilities.someCapability = true;
- capabilities.delegation = delegation;
- delegates.someCapability = true;
- expect(provider.getViews(mockDomainObject))
- .toEqual([viewA, viewC]);
- });
-
- it("warns if keys are omitted from views", function () {
- // Verify that initial construction issued no warning
- expect(mockLog.warn).not.toHaveBeenCalled();
- // Recreate with no keys; that view should be filtered out
- expect(
- new ViewProvider(
- [viewA, { some: "bad view" }],
- mockLog
- ).getViews(mockDomainObject)
- ).toEqual([viewA]);
- // We should have also received a warning, to support debugging
- expect(mockLog.warn).toHaveBeenCalledWith(jasmine.any(String));
- });
-
- it("restricts typed views to matching types", function () {
- var testType = "testType",
- testView = {
- key: "x",
- type: testType
- },
- viewProvider = new ViewProvider([testView], mockLog);
-
- // Include a "type" capability
- capabilities.type = jasmine.createSpyObj(
- "type",
- ["instanceOf", "invoke", "getDefinition"]
- );
- capabilities.type.invoke.and.returnValue(capabilities.type);
-
- // Should be included when types match
- capabilities.type.instanceOf.and.returnValue(true);
- expect(viewProvider.getViews(mockDomainObject))
- .toEqual([testView]);
- expect(capabilities.type.instanceOf)
- .toHaveBeenCalledWith(testType);
-
- // ...but not when they don't
- capabilities.type.instanceOf.and.returnValue(false);
- expect(viewProvider.getViews(mockDomainObject))
- .toEqual([]);
-
- });
-
- it("enforces view restrictions from types", function () {
- var testView = { key: "x" },
- viewProvider = new ViewProvider([testView], mockLog);
-
- // Include a "type" capability
- capabilities.type = jasmine.createSpyObj(
- "type",
- ["instanceOf", "invoke", "getDefinition"]
- );
- capabilities.type.invoke.and.returnValue(capabilities.type);
-
- // Should be included when view keys match
- capabilities.type.getDefinition
- .and.returnValue({ views: [testView.key]});
- expect(viewProvider.getViews(mockDomainObject))
- .toEqual([testView]);
-
- // ...but not when they don't
- capabilities.type.getDefinition
- .and.returnValue({ views: ["somethingElse"]});
- expect(viewProvider.getViews(mockDomainObject))
- .toEqual([]);
- });
-
- });
- }
-);
diff --git a/platform/data/README.md b/platform/data/README.md
deleted file mode 100644
index dfcf85e2c..000000000
--- a/platform/data/README.md
+++ /dev/null
@@ -1,2 +0,0 @@
-This bundle is responsible for introducing a reusable infrastructure
-and set of APIs for using time-series data in Open MCT.
diff --git a/platform/entanglement/README.md b/platform/entanglement/README.md
deleted file mode 100644
index aaf517dec..000000000
--- a/platform/entanglement/README.md
+++ /dev/null
@@ -1,24 +0,0 @@
-# Entanglement
-
-Entanglement is the process of moving, copying, and linking domain objects
-in such a way that their relationships are impossible to discern.
-
-This bundle provides move, copy, and link functionality. Achieving a state of
-entanglement is left up to the end user.
-
-
-## Services implement logic
-
-Each method (move, copy, link) is implemented as a service, and each service
-provides two functions: `validate` and `perform`.
-
-`validate(object, parentCandidate)` returns true if the `object` can be
-move/copy/linked into the `parentCandidate`'s composition.
-
-`perform(object, parentObject)` move/copy/links the `object` into the
-`parentObject`'s composition.
-
-## Actions implement user interactions
-
-Actions are used to expose move/copy/link to the user. They prompt for input
-where necessary, and complete the actions.
diff --git a/platform/entanglement/bundle.js b/platform/entanglement/bundle.js
deleted file mode 100644
index f4475d9a2..000000000
--- a/platform/entanglement/bundle.js
+++ /dev/null
@@ -1,124 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
- "./src/actions/SetPrimaryLocationAction",
- "./src/services/LocatingCreationDecorator",
- "./src/services/LocatingObjectDecorator",
- "./src/policies/CopyPolicy",
- "./src/policies/CrossSpacePolicy",
- "./src/capabilities/LocationCapability",
- "./src/services/CopyService",
- "./src/services/LocationService"
-], function (
- SetPrimaryLocationAction,
- LocatingCreationDecorator,
- LocatingObjectDecorator,
- CopyPolicy,
- CrossSpacePolicy,
- LocationCapability,
- CopyService,
- LocationService
-) {
-
- return {
- name: "platform/entanglement",
- definition: {
- "name": "Entanglement",
- "description": "Tools to assist you in entangling the world of WARP.",
- "configuration": {},
- "extensions": {
- "actions": [
- {
- "key": "locate",
- "name": "Set Primary Location",
- "description": "Set a domain object's primary location.",
- "cssClass": "",
- "category": "contextual",
- "implementation": SetPrimaryLocationAction
- }
- ],
- "components": [
- {
- "type": "decorator",
- "provides": "creationService",
- "implementation": LocatingCreationDecorator
- },
- {
- "type": "decorator",
- "provides": "objectService",
- "implementation": LocatingObjectDecorator,
- "depends": [
- "$q",
- "$log"
- ]
- }
- ],
- "policies": [
- {
- "category": "action",
- "implementation": CrossSpacePolicy
- },
- {
- "category": "action",
- "implementation": CopyPolicy
- }
- ],
- "capabilities": [
- {
- "key": "location",
- "name": "Location Capability",
- "description": "Provides a capability for retrieving the location of an object based upon it's context.",
- "implementation": LocationCapability,
- "depends": [
- "$q",
- "$injector"
- ]
- }
- ],
- "services": [
- {
- "key": "copyService",
- "name": "Copy Service",
- "description": "Provides a service for copying objects",
- "implementation": CopyService,
- "depends": [
- "$q",
- "policyService",
- "openmct"
- ]
- },
- {
- "key": "locationService",
- "name": "Location Service",
- "description": "Provides a service for prompting a user for locations.",
- "implementation": LocationService,
- "depends": [
- "dialogService"
- ]
- }
- ],
- "licenses": []
- }
- }
- };
-});
diff --git a/platform/entanglement/src/actions/AbstractComposeAction.js b/platform/entanglement/src/actions/AbstractComposeAction.js
deleted file mode 100644
index 2549a3b95..000000000
--- a/platform/entanglement/src/actions/AbstractComposeAction.js
+++ /dev/null
@@ -1,163 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ['./CancelError'],
- function (CancelError) {
- var CANCEL_MESSAGE = "User cancelled location selection.";
-
- /**
- * Common interface exposed by services which support move, copy,
- * and link actions.
- * @interface platform/entanglement.AbstractComposeService
- * @private
- */
- /**
- * Change the composition of the specified objects. Note that this
- * should only be invoked after successfully validating.
- *
- * @param {DomainObject} domainObject the domain object to
- * move, copy, or link.
- * @param {DomainObject} parent the domain object whose composition
- * will be changed to contain the domainObject (or its duplicate)
- * @returns {Promise} A promise that is fulfilled when the
- * duplicate operation has completed.
- * @method platform/entanglement.AbstractComposeService#perform
- */
- /**
- * Check if this composition change is valid for these objects.
- *
- * @param {DomainObject} domainObject the domain object to
- * move, copy, or link.
- * @param {DomainObject} parent the domain object whose composition
- * will be changed to contain the domainObject (or its duplicate)
- * @returns {boolean} true if this composition change is allowed
- * @method platform/entanglement.AbstractComposeService#validate
- */
-
- /**
- * Template class for Move, Copy, and Link actions.
- *
- * @implements {Action}
- * @constructor
- * @private
- * @memberof platform/entanglement
- * @param {PolicyService} policyService the policy service to use to
- * verify that variants of this action are allowed
- * @param {platform/entanglement.LocationService} locationService a
- * service to request destinations from the user
- * @param {platform/entanglement.AbstractComposeService} composeService
- * a service which will handle actual changes to composition
- * @param {ActionContext} the context in which the action will be performed
- * @param {string} verb the verb to display for the action (e.g. "Move")
- * @param {string} [suffix] a string to display in the dialog title;
- * default is "to a new location"
- */
- function AbstractComposeAction(
- policyService,
- locationService,
- composeService,
- context,
- verb,
- suffix
- ) {
- if (context.selectedObject) {
- this.newParent = context.domainObject;
- this.object = context.selectedObject;
- } else {
- this.object = context.domainObject;
- }
-
- this.currentParent = this.object
- .getCapability('context')
- .getParent();
-
- this.context = context;
- this.policyService = policyService;
- this.locationService = locationService;
- this.composeService = composeService;
- this.verb = verb || "Compose";
- this.suffix = suffix || "To a New Location";
- }
-
- AbstractComposeAction.prototype.cloneContext = function () {
- var clone = {}, original = this.context;
- Object.keys(original).forEach(function (k) {
- clone[k] = original[k];
- });
-
- return clone;
- };
-
- AbstractComposeAction.prototype.perform = function () {
- var dialogTitle,
- label,
- validateLocation,
- self = this,
- locationService = this.locationService,
- composeService = this.composeService,
- currentParent = this.currentParent,
- newParent = this.newParent,
- object = this.object;
-
- if (newParent) {
- return composeService.perform(object, newParent);
- }
-
- dialogTitle = [this.verb, object.getModel().name, this.suffix]
- .join(" ");
-
- label = this.verb + " To";
-
- validateLocation = function (newParentObj) {
- var newContext = self.cloneContext();
- newContext.selectedObject = object;
- newContext.domainObject = newParentObj;
-
- return composeService.validate(object, newParentObj)
- && self.policyService.allow("action", self, newContext);
- };
-
- return locationService.getLocationFromUser(
- dialogTitle,
- label,
- validateLocation,
- currentParent
- ).then(function (newParentObj) {
- return composeService.perform(object, newParentObj);
- }, function () {
- return Promise.reject(new CancelError(CANCEL_MESSAGE));
- }.bind(this));
- };
-
- AbstractComposeAction.appliesTo = function (context) {
- var applicableObject =
- context.selectedObject || context.domainObject;
-
- return Boolean(applicableObject
- && applicableObject.hasCapability('context'));
- };
-
- return AbstractComposeAction;
- }
-);
-
diff --git a/platform/entanglement/src/actions/SetPrimaryLocationAction.js b/platform/entanglement/src/actions/SetPrimaryLocationAction.js
deleted file mode 100644
index a5c4a257d..000000000
--- a/platform/entanglement/src/actions/SetPrimaryLocationAction.js
+++ /dev/null
@@ -1,66 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- function () {
-
- /**
- * Implements the "Set Primary Location" action, which sets a
- * location property for objects to match their contextual
- * location.
- *
- * @implements {Action}
- * @constructor
- * @private
- * @memberof platform/entanglement
- * @param {ActionContext} context the context in which the action
- * will be performed
- */
- function SetPrimaryLocationAction(context) {
- this.domainObject = context.domainObject;
- }
-
- SetPrimaryLocationAction.prototype.perform = function () {
- var location = this.domainObject.getCapability('location');
-
- return location.setPrimaryLocation(
- location.getContextualLocation()
- );
- };
-
- SetPrimaryLocationAction.appliesTo = function (context, view, openmct) {
- let domainObject = (context || {}).domainObject;
-
- if (!domainObject || (domainObject.model && domainObject.model.locked)) {
- return false;
- }
-
- let isPersistable = openmct.objects.isPersistable(domainObject.id);
-
- return isPersistable && domainObject.hasCapability("location")
- && (domainObject.getModel().location === undefined);
- };
-
- return SetPrimaryLocationAction;
- }
-);
-
diff --git a/platform/entanglement/src/capabilities/LocationCapability.js b/platform/entanglement/src/capabilities/LocationCapability.js
deleted file mode 100644
index 6f45ed8bb..000000000
--- a/platform/entanglement/src/capabilities/LocationCapability.js
+++ /dev/null
@@ -1,128 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- function () {
-
- /**
- * The location capability allows a domain object to know its current
- * parent, and also know its original parent. When a domain object's
- * current parent is its original parent, the object is considered an
- * original, otherwise it's a link.
- *
- * @constructor
- */
- function LocationCapability($q, $injector, domainObject) {
- this.domainObject = domainObject;
- this.$q = $q;
- this.$injector = $injector;
-
- return this;
- }
-
- /**
- * Get an instance of this domain object in its original location.
- *
- * @returns {Promise.<DomainObject>} a promise for the original
- * instance of this domain object
- */
- LocationCapability.prototype.getOriginal = function () {
- var id;
-
- if (this.isOriginal()) {
- return this.$q.when(this.domainObject);
- }
-
- id = this.domainObject.getId();
-
- this.objectService =
- this.objectService || this.$injector.get("objectService");
-
- // Assume that an object will be correctly contextualized when
- // loaded directly from the object service; this is true
- // so long as LocatingObjectDecorator is present, and that
- // decorator is also contained in this bundle.
- return this.objectService.getObjects([id])
- .then(function (objects) {
- return objects[id];
- });
- };
-
- /**
- * Set the primary location (the parent id) of the current domain
- * object.
- *
- * @param {String} location the primary location to persist.
- * @returns {Promise} a promise that is resolved when the operation
- * completes.
- */
- LocationCapability.prototype.setPrimaryLocation = function (location) {
- return this.domainObject.useCapability(
- 'mutation',
- function (model) {
- model.location = location;
- }
- );
- };
-
- /**
- * Returns the contextual location of the current domain object. Only
- * valid for domain objects that have a context capability.
- *
- * @returns {String} the contextual location of the object; the id of
- * its parent.
- */
- LocationCapability.prototype.getContextualLocation = function () {
- var context = this.domainObject.getCapability("context");
-
- if (!context) {
- return;
- }
-
- return context.getParent().getId();
- };
-
- /**
- * Returns true if the domainObject is a link, false if it's an
- * original.
- *
- * @returns {Boolean}
- */
- LocationCapability.prototype.isLink = function () {
- var model = this.domainObject.getModel();
-
- return model.location !== this.getContextualLocation();
- };
-
- /**
- * Returns true if the domainObject is an original, false if it's a
- * link.
- *
- * @returns {Boolean}
- */
- LocationCapability.prototype.isOriginal = function () {
- return !this.isLink();
- };
-
- return LocationCapability;
- }
-);
diff --git a/platform/entanglement/src/policies/CrossSpacePolicy.js b/platform/entanglement/src/policies/CrossSpacePolicy.js
deleted file mode 100644
index 04759f94e..000000000
--- a/platform/entanglement/src/policies/CrossSpacePolicy.js
+++ /dev/null
@@ -1,69 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- var DISALLOWED_ACTIONS = ["move"];
-
- /**
- * This policy prevents performing move/copy/link actions across
- * different persistence spaces (e.g. linking to an object in
- * a private space from an object in a public space.)
- * @memberof {platform/entanglement}
- * @constructor
- * @implements {Policy}
- */
- function CrossSpacePolicy() {
- }
-
- function lookupSpace(domainObject) {
- var persistence = domainObject
- && domainObject.getCapability("persistence");
-
- return persistence && persistence.getSpace();
- }
-
- function isCrossSpace(context) {
- var domainObject = context.domainObject,
- selectedObject = context.selectedObject;
-
- return selectedObject !== undefined
- && domainObject !== undefined
- && lookupSpace(domainObject) !== lookupSpace(selectedObject);
- }
-
- CrossSpacePolicy.prototype.allow = function (action, context) {
- var key = action.getMetadata().key;
-
- if (DISALLOWED_ACTIONS.indexOf(key) !== -1) {
- return !isCrossSpace(context);
- }
-
- return true;
- };
-
- return CrossSpacePolicy;
-
- }
-);
diff --git a/platform/entanglement/src/services/CopyService.js b/platform/entanglement/src/services/CopyService.js
deleted file mode 100644
index f2ff8294b..000000000
--- a/platform/entanglement/src/services/CopyService.js
+++ /dev/null
@@ -1,109 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["./CopyTask"],
- function (CopyTask) {
-
- /**
- * CopyService provides an interface for deep copying objects from one
- * location to another. It also provides a method for determining if
- * an object can be copied to a specific location.
- * @constructor
- * @memberof platform/entanglement
- * @implements {platform/entanglement.AbstractComposeService}
- */
- function CopyService($q, policyService, openmct) {
- this.$q = $q;
- this.policyService = policyService;
- this.openmct = openmct;
- }
-
- CopyService.prototype.validate = function (object, parentCandidate) {
- if (!parentCandidate || !parentCandidate.getId) {
- return false;
- }
-
- if (parentCandidate.getId() === object.getId()) {
- return false;
- }
-
- return this.openmct.composition.checkPolicy(parentCandidate.useCapability('adapter'), object.useCapability('adapter'));
- };
-
- /**
- * A function used to check if a domain object should be cloned
- * or not.
- * @callback platform/entanglement.CopyService~filter
- * @param {DomainObject} domainObject the object to be cloned
- * @returns {boolean} true if the object should be cloned; false
- * if it should be linked
- */
-
- /**
- * Creates a duplicate of the object tree starting at domainObject to
- * the new parent specified.
- *
- * Any domain objects which cannot be created will not be cloned;
- * instead, these will appear as links. If a filtering function
- * is provided, any objects which fail that check will also be
- * linked instead of cloned
- *
- * @param {DomainObject} domainObject the object to duplicate
- * @param {DomainObject} parent the destination for the clone
- * @param {platform/entanglement.CopyService~filter} [filter]
- * an optional function used to filter out objects from
- * the cloning process
- * @returns a promise that will be completed with the clone of
- * domainObject when the duplication is successful.
- */
- CopyService.prototype.perform = function (domainObject, parent, filter) {
- var policyService = this.policyService;
-
- // Combines caller-provided filter (if any) with the
- // baseline behavior of respecting creation policy.
- function filterWithPolicy(domainObj) {
- return (!filter || filter(domainObj))
- && policyService.allow(
- "creation",
- domainObj.getCapability("type")
- );
- }
-
- if (this.validate(domainObject, parent)) {
- return new CopyTask(
- domainObject,
- parent,
- filterWithPolicy,
- this.$q
- ).perform();
- } else {
- throw new Error(
- "Tried to copy objects without validating first."
- );
- }
- };
-
- return CopyService;
- }
-);
-
diff --git a/platform/entanglement/src/services/CopyTask.js b/platform/entanglement/src/services/CopyTask.js
deleted file mode 100644
index 326ee176b..000000000
--- a/platform/entanglement/src/services/CopyTask.js
+++ /dev/null
@@ -1,276 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- /**
- * This class encapsulates the process of copying a domain object
- * and all of its children.
- *
- * @param {DomainObject} domainObject The object to copy
- * @param {DomainObject} parent The new location of the cloned object tree
- * @param {platform/entanglement.CopyService~filter} filter
- * a function used to filter out objects from
- * the cloning process
- * @param $q Angular's $q, for promises
- * @constructor
- */
- function CopyTask(domainObject, parent, filter, $q) {
- this.domainObject = domainObject;
- this.parent = parent;
- this.firstClone = undefined;
- this.$q = $q;
- this.deferred = undefined;
- this.filter = filter;
- this.persisted = 0;
- this.clones = [];
- this.idMap = {};
- }
-
- function composeChild(child, parent, setLocation) {
- //Once copied, associate each cloned
- // composee with its parent clone
-
- parent.getModel().composition.push(child.getId());
-
- //If a location is not specified, set it.
- if (setLocation && child.getModel().location === undefined) {
- child.getModel().location = parent.getId();
- }
-
- return child;
- }
-
- function cloneObjectModel(objectModel) {
- var clone = JSON.parse(JSON.stringify(objectModel));
-
- /**
- * Reset certain fields.
- */
- //If has a composition, set it to an empty array. Will be
- // recomposed later with the ids of its cloned children.
- if (clone.composition) {
- //Important to set it to an empty array here, otherwise
- // hasCapability("composition") returns false;
- clone.composition = [];
- }
-
- delete clone.persisted;
- delete clone.modified;
- delete clone.location;
-
- return clone;
- }
-
- /**
- * Will persist a list of {@link objectClones}. It will persist all
- * simultaneously, irrespective of order in the list. This may
- * result in automatic request batching by the browser.
- */
- function persistObjects(self) {
- return self.$q.all(self.clones.map(function (clone) {
- return clone.getCapability("persistence").persist().then(function () {
- self.deferred.notify({
- phase: "copying",
- totalObjects: self.clones.length,
- processed: ++self.persisted
- });
- });
- })).then(function () {
- return self;
- });
- }
-
- /**
- * Will add a list of clones to the specified parent's composition
- */
- function addClonesToParent(self) {
- return self.parent.getCapability("composition")
- .add(self.firstClone)
- .then(function (addedClone) {
- return self.parent.getCapability("persistence").persist()
- .then(function () {
- return addedClone;
- });
- });
- }
-
- /**
- * Update identifiers in a cloned object model (or part of
- * a cloned object model) to reflect new identifiers after
- * copying.
- * @private
- */
- CopyTask.prototype.rewriteIdentifiers = function (obj, idMap) {
- function lookupValue(value) {
- return (typeof value === 'string' && idMap[value]) || value;
- }
-
- if (Array.isArray(obj)) {
- obj.forEach(function (value, index) {
- obj[index] = lookupValue(value);
- this.rewriteIdentifiers(obj[index], idMap);
- }, this);
- } else if (obj && typeof obj === 'object') {
- Object.keys(obj).forEach(function (key) {
- var value = obj[key];
- obj[key] = lookupValue(value);
- if (idMap[key]) {
- delete obj[key];
- obj[idMap[key]] = value;
- }
-
- this.rewriteIdentifiers(value, idMap);
- }, this);
- }
- };
-
- /**
- * Given an array of objects composed by a parent, clone them, then
- * add them to the parent.
- * @private
- * @returns {*}
- */
- CopyTask.prototype.copyComposees = function (composees, clonedParent, originalParent) {
- var self = this,
- idMap = {};
-
- return (composees || []).reduce(function (promise, originalComposee) {
- //If the composee is composed of other
- // objects, chain a promise..
- return promise.then(function () {
- // ...to recursively copy it (and its children)
- return self.copy(originalComposee, originalParent).then(function (clonedComposee) {
- //Map the original composee's ID to that of its
- // clone so that we can replace any references to it
- // in the parent
- idMap[originalComposee.getId()] = clonedComposee.getId();
-
- //Compose the child within its parent. Cloned
- // objects will need to also have their location
- // set, however linked objects will not.
- return composeChild(clonedComposee, clonedParent, clonedComposee !== originalComposee);
- });
- });
- }, self.$q.when(undefined)
- ).then(function () {
- //Replace any references in the cloned parent to
- // contained objects that have been composed with the
- // Ids of the clones
- self.rewriteIdentifiers(clonedParent.getModel(), idMap);
-
- //Add the clone to the list of clones that will
- //be returned by this function
- self.clones.push(clonedParent);
-
- return clonedParent;
- });
- };
-
- /**
- * A recursive function that will perform a bottom-up copy of
- * the object tree with originalObject at the root. Recurses to
- * the farthest leaf, then works its way back up again,
- * cloning objects, and composing them with their child clones
- * as it goes
- * @private
- * @returns {DomainObject} If the type of the original object allows for
- * duplication, then a duplicate of the object, otherwise the object
- * itself (to allow linking to non duplicatable objects).
- */
- CopyTask.prototype.copy = function (originalObject) {
- var self = this,
- clone;
-
- //Check if the type of the object being copied allows for
- // creation of new instances. If it does not, then a link to the
- // original will be created instead.
- if (this.filter(originalObject)) {
- //create a new clone of the original object. Use the
- // creation capability of the targetParent to create the
- // new clone. This will ensure that the correct persistence
- // space is used.
- clone = this.parent.useCapability("instantiation", cloneObjectModel(originalObject.getModel()));
-
- //Iterate through child tree
- return this.$q.when(originalObject.useCapability('composition')).then(function (composees) {
- self.deferred.notify({phase: "preparing"});
-
- //Duplicate the object's children, and their children, and
- // so on down to the leaf nodes of the tree.
- //If it is a link, don't both with children
- return self.copyComposees(composees, clone, originalObject);
- });
- } else {
- //Creating a link, no need to iterate children
- return self.$q.when(originalObject);
- }
-
- };
-
- /**
- * Will build a graph of an object and all of its child objects in
- * memory
- * @private
- * @param domainObject The original object to be copied
- * @param parent The parent of the original object to be copied
- * @returns {Promise} resolved with an array of clones of the models
- * of the object tree being copied. Copying is done in a bottom-up
- * fashion, so that the last member in the array is a clone of the model
- * object being copied. The clones are all full composed with
- * references to their own children.
- */
- CopyTask.prototype.buildCopyPlan = function () {
- var self = this;
-
- return this.copy(self.domainObject, self.parent).then(function (domainObjectClone) {
- if (domainObjectClone !== self.domainObject) {
- domainObjectClone.getModel().location = self.parent.getId();
- }
-
- self.firstClone = domainObjectClone;
-
- return self;
- });
- };
-
- /**
- * Execute the copy task with the objects provided in the constructor.
- * @returns {promise} Which will resolve with a clone of the object
- * once complete.
- */
- CopyTask.prototype.perform = function () {
- this.deferred = this.$q.defer();
-
- this.buildCopyPlan()
- .then(persistObjects)
- .then(addClonesToParent)
- .then(this.deferred.resolve, this.deferred.reject);
-
- return this.deferred.promise;
- };
-
- return CopyTask;
- }
-);
diff --git a/platform/entanglement/src/services/LocatingCreationDecorator.js b/platform/entanglement/src/services/LocatingCreationDecorator.js
deleted file mode 100644
index 36cfae1c0..000000000
--- a/platform/entanglement/src/services/LocatingCreationDecorator.js
+++ /dev/null
@@ -1,47 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- function () {
-
- /**
- * Adds a `location` property to newly-created domain objects.
- * @constructor
- * @augments {platform/commonUI/browse.CreationService}
- * @memberof platform/entanglement
- */
- function LocatingCreationDecorator(creationService) {
- this.creationService = creationService;
- }
-
- LocatingCreationDecorator.prototype.createObject = function (model, parent) {
- if (parent && parent.getId) {
- model.location = parent.getId();
- }
-
- return this.creationService.createObject(model, parent);
- };
-
- return LocatingCreationDecorator;
- }
-);
-
diff --git a/platform/entanglement/src/services/LocatingObjectDecorator.js b/platform/entanglement/src/services/LocatingObjectDecorator.js
deleted file mode 100644
index ecdcec64e..000000000
--- a/platform/entanglement/src/services/LocatingObjectDecorator.js
+++ /dev/null
@@ -1,95 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ['../../../core/src/capabilities/ContextualDomainObject'],
- function (ContextualDomainObject) {
-
- /**
- * Ensures that domain objects are loaded with a context capability
- * that reflects their location.
- * @constructor
- * @implements {ObjectService}
- * @memberof platform/entanglement
- */
- function LocatingObjectDecorator($q, $log, objectService) {
- this.$log = $log;
- this.objectService = objectService;
- this.$q = $q;
- }
-
- LocatingObjectDecorator.prototype.getObjects = function (ids, abortSignal) {
- var $q = this.$q,
- $log = this.$log,
- objectService = this.objectService,
- result = {};
-
- // Load a single object using location to establish a context
- function loadObjectInContext(id, exclude) {
- function attachContext(objects) {
- var domainObject = (objects || {})[id],
- model = domainObject && domainObject.getModel(),
- location = (model || {}).location;
-
- // If no location is defined, we can't look up a context.
- if (!location) {
- return domainObject;
- }
-
- // Avoid looping indefinitely on cyclical locations
- if (exclude[id]) {
- $log.warn([
- "LocatingObjectDecorator detected a cycle",
- "while attempted to define a context for",
- id + ";",
- "no context will be added and unexpected behavior",
- "may follow."
- ].join(" "));
-
- return domainObject;
- }
-
- // Record that we've visited this ID to detect cycles.
- exclude[id] = true;
-
- // Do the recursive step to get the parent...
- return loadObjectInContext(location, exclude)
- .then(function (parent) {
- // ...and then contextualize with it!
- return new ContextualDomainObject(domainObject, parent);
- });
- }
-
- return objectService.getObjects([id], abortSignal).then(attachContext);
- }
-
- ids.forEach(function (id) {
- result[id] = loadObjectInContext(id, {});
- });
-
- return $q.all(result);
- };
-
- return LocatingObjectDecorator;
- }
-);
-
diff --git a/platform/entanglement/src/services/LocationService.js b/platform/entanglement/src/services/LocationService.js
deleted file mode 100644
index 4490c4c42..000000000
--- a/platform/entanglement/src/services/LocationService.js
+++ /dev/null
@@ -1,90 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * This bundle implements actions which control the location of objects
- * (move, copy, link.)
- * @namespace platform/entanglement
- */
-define(
- function () {
-
- /**
- * The LocationService allows for easily prompting the user for a
- * location in the root tree.
- * @constructor
- * @memberof platform/entanglement
- */
- function LocationService(dialogService) {
- return {
- /** Prompt the user to select a location. Returns a promise
- * that is resolved with a domainObject representing the
- * location selected by the user.
- *
- * @param {string} title - title of location dialog
- * @param {string} label - label for location input field
- * @param {function} validate - function that validates
- * selections.
- * @param {domainObject} initialLocation - tree location to
- * display at start
- * @returns {Promise} promise for a domain object.
- * @memberof platform/entanglement.LocationService#
- */
- getLocationFromUser: function (title, label, validate, initialLocation) {
- var formStructure,
- formState;
-
- formStructure = {
- sections: [
- {
- name: 'Location',
- cssClass: "grows",
- rows: [
- {
- name: label,
- control: "locator",
- validate: validate,
- key: 'location'
- }
- ]
- }
- ],
- name: title
- };
-
- formState = {
- location: initialLocation
- };
-
- return dialogService
- .getUserInput(formStructure, formState)
- .then(function (userFormState) {
- return userFormState.location;
- });
- }
- };
- }
-
- return LocationService;
- }
-);
-
diff --git a/platform/entanglement/test/ControlledPromise.js b/platform/entanglement/test/ControlledPromise.js
deleted file mode 100644
index 11eb3bfb1..000000000
--- a/platform/entanglement/test/ControlledPromise.js
+++ /dev/null
@@ -1,97 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- function () {
-
- /**
- * An instrumented promise implementation for better control of promises
- * during tests.
- *
- */
- function ControlledPromise() {
- this.resolveHandlers = [];
- this.rejectHandlers = [];
- spyOn(this, 'then').and.callThrough();
- }
-
- /**
- * Resolve the promise, passing the supplied value to all resolve
- * handlers.
- */
- ControlledPromise.prototype.resolve = function (value) {
- this.resolveHandlers.forEach(function (handler) {
- handler(value);
- });
- };
-
- /**
- * Reject the promise, passing the supplied value to all rejection
- * handlers.
- */
- ControlledPromise.prototype.reject = function (value) {
- this.rejectHandlers.forEach(function (handler) {
- handler(value);
- });
- };
-
- /**
- * Standard promise.then, returns a promise that support chaining.
- * TODO: Need to support resolve/reject handlers that return promises.
- */
- ControlledPromise.prototype.then = function (onResolve, onReject) {
- var returnPromise = new ControlledPromise();
-
- if (onResolve) {
- this.resolveHandlers.push(function (resolveWith) {
- var chainResult = onResolve(resolveWith);
- if (chainResult && chainResult.then) {
- // chainResult is a promise, resolve when it resolves.
- chainResult.then(function (pipedResult) {
- return returnPromise.resolve(pipedResult);
- });
- } else {
- returnPromise.resolve(chainResult);
- }
- });
- }
-
- if (onReject) {
- this.rejectHandlers.push(function (rejectWith) {
- var chainResult = onReject(rejectWith);
- if (chainResult && chainResult.then) {
- chainResult.then(function (pipedResult) {
- returnPromise.reject(pipedResult);
- });
- } else {
- returnPromise.reject(chainResult);
- }
- });
- }
-
- return returnPromise;
- };
-
- return ControlledPromise;
-
- }
-);
diff --git a/platform/entanglement/test/DomainObjectFactory.js b/platform/entanglement/test/DomainObjectFactory.js
deleted file mode 100644
index 834646a5c..000000000
--- a/platform/entanglement/test/DomainObjectFactory.js
+++ /dev/null
@@ -1,160 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- function () {
-
- /**
- * @typedef DomainObjectConfig
- * @type {object}
- * @property {string} [name] a name for the underlying jasmine spy
- * object mockDomainObject. Used as
- * @property {string} [id] initial id value for the domainOBject.
- * @property {object} [model] initial values for the object's model.
- * @property {object} [capabilities] an object containing
- * capability definitions.
- */
-
- var configObjectProps = ['model', 'capabilities'];
-
- /**
- * Internal function for ensuring an object is an instance of a
- * DomainObjectConfig.
- */
- function ensureValidConfigObject(config) {
- if (!config || !config.hasOwnProperty) {
- config = {};
- }
-
- if (!config.name) {
- config.name = 'domainObject';
- }
-
- configObjectProps.forEach(function (prop) {
- if (!config[prop] || !config[prop].hasOwnProperty) {
- config[prop] = {};
- }
- });
-
- return config;
- }
-
- /**
- * Defines a factory function which takes a `config` object and returns
- * a mock domainObject. The config object is an easy way to provide
- * initial properties for the domainObject-- they can be changed at any
- * time by directly modifying the domainObject's properties.
- *
- * @param {Object} [config] initial configuration for a domain object.
- * @returns {Object} mockDomainObject
- */
- function domainObjectFactory(config) {
- config = ensureValidConfigObject(config);
-
- var domainObject = jasmine.createSpyObj(config.name, [
- 'getId',
- 'getModel',
- 'getCapability',
- 'hasCapability',
- 'useCapability'
- ]);
-
- domainObject.model = JSON.parse(JSON.stringify(config.model));
- domainObject.capabilities = config.capabilities;
- domainObject.id = config.id;
-
- /**
- * getId: Returns `domainObject.id`.
- *
- * @returns {string} id
- */
- domainObject.getId.and.callFake(function () {
- return domainObject.id;
- });
-
- /**
- * getModel: Returns `domainObject.model`.
- *
- * @returns {object} model
- */
- domainObject.getModel.and.callFake(function () {
- return domainObject.model;
- });
-
- /**
- * getCapability: returns a `capability` object defined in
- * domainObject.capabilities. Returns undefined if capability
- * does not exist.
- *
- * @param {string} capability name of the capability to return.
- * @returns {*} capability object
- */
- domainObject.getCapability.and.callFake(function (capability) {
- if (Object.prototype.hasOwnProperty.call(config.capabilities, capability)) {
- return config.capabilities[capability];
- }
- });
-
- /**
- * hasCapability: return true if domainObject.capabilities has a
- * property named `capability`, otherwise returns false.
- *
- * @param {string} capability name of the capability to test for
- * existence of.
- * @returns {boolean}
- */
- domainObject.hasCapability.and.callFake(function (capability) {
- return Object.prototype.hasOwnProperty.call(config.capabilities, capability);
- });
-
- /**
- * useCapability: find a capability in domainObject.capabilities
- * and call that capabilities' invoke method. If the capability
- * does not have an invoke method, will throw an error.
- *
- * @param {string} capability name of a capability to invoke.
- * @param {...*} params to pass to the capability's `invoke` method.
- * @returns {*} result whatever was returned by `invoke`.
- */
- domainObject.useCapability.and.callFake(function (capability) {
- if (Object.prototype.hasOwnProperty.call(config.capabilities, capability)) {
- if (!config.capabilities[capability].invoke) {
- throw new Error(
- capability + ' missing invoke function.'
- );
- }
-
- var passThroughArgs = [].slice.call(arguments, 1);
-
- return config
- .capabilities[capability]
- .invoke
- .apply(null, passThroughArgs);
- }
- });
-
- return domainObject;
- }
-
- return domainObjectFactory;
- }
-);
diff --git a/platform/entanglement/test/actions/AbstractComposeActionSpec.js b/platform/entanglement/test/actions/AbstractComposeActionSpec.js
deleted file mode 100644
index e55ad9360..000000000
--- a/platform/entanglement/test/actions/AbstractComposeActionSpec.js
+++ /dev/null
@@ -1,227 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [
- '../../src/actions/AbstractComposeAction',
- '../services/MockCopyService',
- '../DomainObjectFactory'
- ],
- function (AbstractComposeAction, MockCopyService, domainObjectFactory) {
-
- describe("Move/copy/link Actions", function () {
-
- var action,
- policyService,
- locationService,
- locationServicePromise,
- composeService,
- context,
- selectedObject,
- selectedObjectContextCapability,
- currentParent,
- newParent;
-
- beforeEach(function () {
- policyService = jasmine.createSpyObj(
- 'policyService',
- ['allow']
- );
-
- selectedObjectContextCapability = jasmine.createSpyObj(
- 'selectedObjectContextCapability',
- [
- 'getParent'
- ]
- );
-
- selectedObject = domainObjectFactory({
- name: 'selectedObject',
- model: {
- name: 'selectedObject'
- },
- capabilities: {
- context: selectedObjectContextCapability
- }
- });
-
- currentParent = domainObjectFactory({
- name: 'currentParent'
- });
-
- selectedObjectContextCapability
- .getParent
- .and.returnValue(currentParent);
-
- newParent = domainObjectFactory({
- name: 'newParent'
- });
-
- locationService = jasmine.createSpyObj(
- 'locationService',
- [
- 'getLocationFromUser'
- ]
- );
-
- locationServicePromise = jasmine.createSpyObj(
- 'locationServicePromise',
- [
- 'then'
- ]
- );
-
- policyService.allow.and.returnValue(true);
-
- locationService
- .getLocationFromUser
- .and.returnValue(locationServicePromise);
-
- composeService = new MockCopyService();
- });
-
- it("are only applicable to domain objects with a context", function () {
- var noContextObject = domainObjectFactory({
- name: 'selectedObject',
- model: { name: 'selectedObject' },
- capabilities: {}
- });
-
- expect(AbstractComposeAction.appliesTo({
- selectedObject: selectedObject
- })).toBe(true);
- expect(AbstractComposeAction.appliesTo({
- domainObject: selectedObject
- })).toBe(true);
-
- expect(AbstractComposeAction.appliesTo({
- selectedObject: noContextObject
- })).toBe(false);
- expect(AbstractComposeAction.appliesTo({
- domainObject: noContextObject
- })).toBe(false);
- });
-
- describe("with context from context-action", function () {
- beforeEach(function () {
- context = {
- domainObject: selectedObject
- };
-
- action = new AbstractComposeAction(
- policyService,
- locationService,
- composeService,
- context,
- "Compose"
- );
- });
-
- it("initializes happily", function () {
- expect(action).toBeDefined();
- });
-
- describe("when performed it", function () {
- beforeEach(function () {
- action.perform();
- });
-
- it("prompts for location", function () {
- expect(locationService.getLocationFromUser)
- .toHaveBeenCalledWith(
- "Compose selectedObject To a New Location",
- "Compose To",
- jasmine.any(Function),
- currentParent
- );
- });
-
- it("waits for location and handles cancellation by user", function () {
- expect(locationServicePromise.then)
- .toHaveBeenCalledWith(jasmine.any(Function), jasmine.any(Function));
- });
-
- it("copies object to selected location", function () {
- locationServicePromise
- .then
- .calls.mostRecent()
- .args[0](newParent);
-
- expect(composeService.perform)
- .toHaveBeenCalledWith(selectedObject, newParent);
- });
-
- describe("provides a validator which", function () {
- var validator;
-
- beforeEach(function () {
- validator = locationService.getLocationFromUser
- .calls.mostRecent().args[2];
- composeService.validate.and.returnValue(true);
- policyService.allow.and.returnValue(true);
- });
-
- it("is sensitive to policy", function () {
- expect(validator()).toBe(true);
- policyService.allow.and.returnValue(false);
- expect(validator()).toBe(false);
- });
-
- it("is sensitive to service-specific validation", function () {
- expect(validator()).toBe(true);
- composeService.validate.and.returnValue(false);
- expect(validator()).toBe(false);
- });
-
- });
- });
- });
-
- describe("with context from drag-drop", function () {
- beforeEach(function () {
- context = {
- selectedObject: selectedObject,
- domainObject: newParent
- };
-
- action = new AbstractComposeAction(
- policyService,
- locationService,
- composeService,
- context,
- "Compose"
- );
- });
-
- it("initializes happily", function () {
- expect(action).toBeDefined();
- });
-
- it("performs copy immediately", function () {
- action.perform();
- expect(composeService.perform)
- .toHaveBeenCalledWith(selectedObject, newParent);
- });
- });
- });
- }
-);
diff --git a/platform/entanglement/test/actions/SetPrimaryLocationActionSpec.js b/platform/entanglement/test/actions/SetPrimaryLocationActionSpec.js
deleted file mode 100644
index 317ceb7e3..000000000
--- a/platform/entanglement/test/actions/SetPrimaryLocationActionSpec.js
+++ /dev/null
@@ -1,88 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [
- '../../src/actions/SetPrimaryLocationAction',
- '../DomainObjectFactory'
- ],
- function (SetPrimaryLocation, domainObjectFactory) {
-
- describe("The 'set primary location' action", function () {
- var testContext,
- testModel,
- testId,
- mockLocationCapability,
- openmct;
-
- beforeEach(function () {
- openmct = {
- objects: {
- isPersistable: jasmine.createSpy('isPersistable')
- }
- };
- testId = "some-id";
- testModel = { name: "some name" };
-
- mockLocationCapability = jasmine.createSpyObj(
- 'location',
- ['setPrimaryLocation', 'getContextualLocation']
- );
-
- openmct.objects.isPersistable.and.returnValue(true);
- mockLocationCapability.getContextualLocation.and.returnValue(testId);
-
- testContext = {
- domainObject: domainObjectFactory({
- capabilities: {
- location: mockLocationCapability
- },
- model: testModel
- })
- };
- });
-
- it("is applicable to objects with no location specified", function () {
- expect(SetPrimaryLocation.appliesTo(testContext, undefined, openmct))
- .toBe(true);
- testContext.domainObject.getModel.and.returnValue({
- location: "something",
- name: "some name"
- });
- expect(SetPrimaryLocation.appliesTo(testContext, undefined, openmct))
- .toBe(false);
- });
-
- it("checks object persistability", function () {
- SetPrimaryLocation.appliesTo(testContext, undefined, openmct);
- expect(openmct.objects.isPersistable).toHaveBeenCalled();
- });
-
- it("sets the location contextually when performed", function () {
- new SetPrimaryLocation(testContext).perform();
- expect(mockLocationCapability.setPrimaryLocation)
- .toHaveBeenCalledWith(testId);
- });
-
- });
- }
-);
diff --git a/platform/entanglement/test/capabilities/LocationCapabilitySpec.js b/platform/entanglement/test/capabilities/LocationCapabilitySpec.js
deleted file mode 100644
index 071a6acd0..000000000
--- a/platform/entanglement/test/capabilities/LocationCapabilitySpec.js
+++ /dev/null
@@ -1,163 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [
- '../../src/capabilities/LocationCapability',
- '../DomainObjectFactory',
- '../ControlledPromise'
- ],
- function (LocationCapability, domainObjectFactory, ControlledPromise) {
-
- describe("LocationCapability", function () {
-
- describe("instantiated with domain object", function () {
- var locationCapability,
- mutationPromise,
- mockQ,
- mockInjector,
- mockObjectService,
- domainObject;
-
- beforeEach(function () {
- domainObject = domainObjectFactory({
- id: "testObject",
- capabilities: {
- context: {
- getParent: function () {
- return domainObjectFactory({id: 'root'});
- }
- },
- mutation: jasmine.createSpyObj(
- 'mutationCapability',
- ['invoke']
- )
- }
- });
-
- mockQ = jasmine.createSpyObj("$q", ["when"]);
- mockInjector = jasmine.createSpyObj("$injector", ["get"]);
- mockObjectService =
- jasmine.createSpyObj("objectService", ["getObjects"]);
-
- mutationPromise = new ControlledPromise();
- domainObject.capabilities.mutation.invoke.and.callFake(
- function (mutator) {
- return mutationPromise.then(function () {
- mutator(domainObject.model);
- });
- }
- );
-
- locationCapability = new LocationCapability(
- mockQ,
- mockInjector,
- domainObject
- );
- });
-
- it("returns contextual location", function () {
- expect(locationCapability.getContextualLocation())
- .toBe('root');
- });
-
- it("knows when the object is an original", function () {
- domainObject.model.location = 'root';
- expect(locationCapability.isOriginal()).toBe(true);
- expect(locationCapability.isLink()).toBe(false);
- });
-
- it("knows when the object is a link.", function () {
- domainObject.model.location = 'different-root';
- expect(locationCapability.isLink()).toBe(true);
- expect(locationCapability.isOriginal()).toBe(false);
- });
-
- it("can mutate location", function () {
- var result = locationCapability
- .setPrimaryLocation('root'),
- whenComplete = jasmine.createSpy('whenComplete');
-
- result.then(whenComplete);
-
- expect(domainObject.model.location).not.toBeDefined();
- mutationPromise.resolve();
- expect(domainObject.model.location).toBe('root');
-
- expect(whenComplete).toHaveBeenCalled();
- });
-
- describe("when used to load an original instance", function () {
- var objectPromise,
- qPromise,
- originalObjects,
- mockCallback;
-
- function resolvePromises() {
- if (mockQ.when.calls.count() > 0) {
- qPromise.resolve(mockQ.when.calls.mostRecent().args[0]);
- }
-
- if (mockObjectService.getObjects.calls.count() > 0) {
- objectPromise.resolve(originalObjects);
- }
- }
-
- beforeEach(function () {
- objectPromise = new ControlledPromise();
- qPromise = new ControlledPromise();
- originalObjects = {
- testObject: domainObjectFactory()
- };
-
- mockInjector.get.and.callFake(function (key) {
- return key === 'objectService' && mockObjectService;
- });
- mockObjectService.getObjects.and.returnValue(objectPromise);
- mockQ.when.and.returnValue(qPromise);
-
- mockCallback = jasmine.createSpy('callback');
- });
-
- it("provides originals directly", function () {
- domainObject.model.location = 'root';
- locationCapability.getOriginal().then(mockCallback);
- expect(mockCallback).not.toHaveBeenCalled();
- resolvePromises();
- expect(mockCallback)
- .toHaveBeenCalledWith(domainObject);
- });
-
- it("loads from the object service for links", function () {
- domainObject.model.location = 'some-other-root';
- locationCapability.getOriginal().then(mockCallback);
- expect(mockCallback).not.toHaveBeenCalled();
- resolvePromises();
- expect(mockCallback)
- .toHaveBeenCalledWith(originalObjects.testObject);
- });
- });
-
- });
- });
- }
-);
diff --git a/platform/entanglement/test/policies/CopyPolicySpec.js b/platform/entanglement/test/policies/CopyPolicySpec.js
deleted file mode 100644
index cb08f718e..000000000
--- a/platform/entanglement/test/policies/CopyPolicySpec.js
+++ /dev/null
@@ -1,92 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
- '../../src/policies/CopyPolicy',
- '../DomainObjectFactory'
-], function (CopyPolicy, domainObjectFactory) {
-
- describe("CopyPolicy", function () {
- var testMetadata,
- testContext,
- mockDomainObject,
- mockType,
- mockAction,
- policy;
-
- beforeEach(function () {
- mockType =
- jasmine.createSpyObj('type', ['hasFeature']);
-
- testMetadata = {};
-
- mockDomainObject = domainObjectFactory({
- capabilities: { type: mockType }
- });
-
- mockType.hasFeature.and.callFake(function (feature) {
- return feature === 'creation';
- });
-
- mockAction = jasmine.createSpyObj('action', ['getMetadata']);
- mockAction.getMetadata.and.returnValue(testMetadata);
-
- testContext = { domainObject: mockDomainObject };
-
- policy = new CopyPolicy();
- });
-
- describe("for copy actions", function () {
- beforeEach(function () {
- testMetadata.key = 'copy';
- });
-
- describe("when an object is non-creatable", function () {
- beforeEach(function () {
- mockType.hasFeature.and.returnValue(false);
- });
-
- it("disallows the action", function () {
- expect(policy.allow(mockAction, testContext)).toBe(false);
- });
- });
-
- describe("when an object is creatable", function () {
- it("allows the action", function () {
- expect(policy.allow(mockAction, testContext)).toBe(true);
- });
- });
- });
-
- describe("for other actions", function () {
- beforeEach(function () {
- testMetadata.key = 'foo';
- });
-
- it("simply allows the action", function () {
- expect(policy.allow(mockAction, testContext)).toBe(true);
- mockType.hasFeature.and.returnValue(false);
- expect(policy.allow(mockAction, testContext)).toBe(true);
- });
- });
- });
-});
diff --git a/platform/entanglement/test/policies/CrossSpacePolicySpec.js b/platform/entanglement/test/policies/CrossSpacePolicySpec.js
deleted file mode 100644
index c8b70e515..000000000
--- a/platform/entanglement/test/policies/CrossSpacePolicySpec.js
+++ /dev/null
@@ -1,117 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [
- '../../src/policies/CrossSpacePolicy',
- '../DomainObjectFactory'
- ],
- function (CrossSpacePolicy, domainObjectFactory) {
-
- describe("CrossSpacePolicy", function () {
- var mockAction,
- testActionMetadata,
- sameSpaceContext,
- crossSpaceContext,
- policy;
-
- function makeObject(space) {
- var mockPersistence = jasmine.createSpyObj(
- 'persistence',
- ['getSpace']
- );
- mockPersistence.getSpace.and.returnValue(space);
-
- return domainObjectFactory({
- id: space + ":foo",
- model: {},
- capabilities: { persistence: mockPersistence }
- });
- }
-
- beforeEach(function () {
- testActionMetadata = {};
-
- // Policy should only call passive methods, so
- // only define those in mocks.
- mockAction = jasmine.createSpyObj(
- 'action',
- ['getMetadata']
- );
- mockAction.getMetadata.and.returnValue(testActionMetadata);
-
- sameSpaceContext = {
- domainObject: makeObject('a'),
- selectedObject: makeObject('a')
- };
- crossSpaceContext = {
- domainObject: makeObject('a'),
- selectedObject: makeObject('b')
- };
-
- policy = new CrossSpacePolicy();
- });
-
- describe("for move actions", function () {
- beforeEach(function () {
- testActionMetadata.key = 'move';
- });
-
- it("allows same-space changes", function () {
- expect(policy.allow(mockAction, sameSpaceContext))
- .toBe(true);
- });
-
- it("disallows cross-space changes", function () {
- expect(policy.allow(mockAction, crossSpaceContext))
- .toBe(false);
- });
-
- it("allows actions with no selectedObject", function () {
- expect(policy.allow(mockAction, {
- domainObject: makeObject('a')
- })).toBe(true);
- });
- });
-
- describe("for other actions", function () {
- beforeEach(function () {
- testActionMetadata.key = "some-other-action";
- });
-
- it("allows same-space and cross-space changes", function () {
- expect(policy.allow(mockAction, crossSpaceContext))
- .toBe(true);
- expect(policy.allow(mockAction, sameSpaceContext))
- .toBe(true);
- });
-
- it("allows actions with no selectedObject", function () {
- expect(policy.allow(mockAction, {
- domainObject: makeObject('a')
- })).toBe(true);
- });
- });
-
- });
- }
-);
diff --git a/platform/entanglement/test/services/CopyServiceSpec.js b/platform/entanglement/test/services/CopyServiceSpec.js
deleted file mode 100644
index 45bef982b..000000000
--- a/platform/entanglement/test/services/CopyServiceSpec.js
+++ /dev/null
@@ -1,479 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [
- '../../src/services/CopyService',
- '../DomainObjectFactory'
- ],
- function (CopyService, domainObjectFactory) {
-
- function synchronousPromise(value) {
- if (value && value.then) {
- return value;
- }
-
- var promise = {
- then: function (callback) {
- return synchronousPromise(callback(value));
- }
- };
- spyOn(promise, 'then').and.callThrough();
-
- return promise;
- }
-
- xdescribe("CopyService", function () {
- var policyService;
-
- beforeEach(function () {
- policyService = jasmine.createSpyObj(
- 'policyService',
- ['allow']
- );
- });
-
- describe("validate", function () {
-
- var copyService,
- object,
- parentCandidate,
- validate;
-
- beforeEach(function () {
- copyService = new CopyService(
- null,
- policyService
- );
- object = domainObjectFactory({
- name: 'object',
- capabilities: {
- type: { type: 'object' }
- }
- });
- parentCandidate = domainObjectFactory({
- name: 'parentCandidate',
- capabilities: {
- type: { type: 'parentCandidate' }
- }
- });
- validate = function () {
- return copyService.validate(object, parentCandidate);
- };
- });
-
- it("does not allow invalid parentCandidate", function () {
- parentCandidate = undefined;
- expect(validate()).toBe(false);
- parentCandidate = {};
- expect(validate()).toBe(false);
- });
-
- it("does not allow copying into source object", function () {
- object.id = parentCandidate.id = 'abc';
- expect(validate()).toBe(false);
- });
-
- describe("defers to policyService", function () {
- beforeEach(function () {
- object.id = 'a';
- parentCandidate.id = 'b';
- });
-
- it("calls policy service with correct args", function () {
- validate();
- expect(policyService.allow).toHaveBeenCalledWith(
- "composition",
- parentCandidate,
- object
- );
- });
-
- it("and returns false", function () {
- policyService.allow.and.returnValue(false);
- expect(validate()).toBe(false);
- });
-
- it("and returns true", function () {
- policyService.allow.and.returnValue(true);
- expect(validate()).toBe(true);
- });
- });
- });
-
- describe("perform", function () {
-
- var mockQ,
- mockDeferred,
- copyService,
- object,
- newParent,
- copyResult,
- copyFinished,
- persistObjectPromise,
- persistenceCapability,
- instantiationCapability,
- compositionCapability,
- locationCapability,
- resolvedValue;
-
- beforeEach(function () {
- policyService.allow.and.returnValue(true);
-
- persistObjectPromise = synchronousPromise(undefined);
-
- instantiationCapability = jasmine.createSpyObj(
- "instantiation",
- ["invoke"]
- );
-
- persistenceCapability = jasmine.createSpyObj(
- "persistenceCapability",
- ["persist", "getSpace"]
- );
- persistenceCapability.persist.and.returnValue(persistObjectPromise);
-
- compositionCapability = jasmine.createSpyObj(
- 'compositionCapability',
- ['invoke', 'add']
- );
- compositionCapability.add.and.callFake(synchronousPromise);
-
- locationCapability = jasmine.createSpyObj(
- 'locationCapability',
- ['isLink']
- );
- locationCapability.isLink.and.returnValue(false);
-
- mockDeferred = jasmine.createSpyObj(
- 'mockDeferred',
- ['notify', 'resolve', 'reject']
- );
- mockDeferred.notify.and.callFake(function () {});
- mockDeferred.resolve.and.callFake(function (value) {
- resolvedValue = value;
- });
- mockDeferred.promise = {
- then: function (callback) {
- return synchronousPromise(callback(resolvedValue));
- }
- };
-
- mockQ = jasmine.createSpyObj(
- 'mockQ',
- ['when', 'all', 'reject', 'defer']
- );
- mockQ.reject.and.returnValue(synchronousPromise(undefined));
- mockQ.when.and.callFake(synchronousPromise);
- mockQ.all.and.callFake(function (promises) {
- var result = {};
- Object.keys(promises).forEach(function (k) {
- promises[k].then(function (v) {
- result[k] = v;
- });
- });
-
- return synchronousPromise(result);
- });
- mockQ.defer.and.returnValue(mockDeferred);
-
- });
-
- describe("on domain object without composition", function () {
- beforeEach(function () {
- var objectCopy;
-
- newParent = domainObjectFactory({
- name: 'newParent',
- id: '456',
- model: {
- composition: []
- },
- capabilities: {
- instantiation: instantiationCapability,
- persistence: persistenceCapability,
- composition: compositionCapability
- }
- });
-
- object = domainObjectFactory({
- name: 'object',
- id: 'abc',
- model: {
- name: 'some object',
- location: '456',
- someOtherAttribute: 'some other value',
- embeddedObjectAttribute: {
- name: 'Some embedded object'
- }
- },
- capabilities: {
- persistence: persistenceCapability
- }
- });
-
- objectCopy = domainObjectFactory({
- name: 'object',
- id: 'abc.copy.fdgdfgdf',
- capabilities: {
- persistence: persistenceCapability,
- location: locationCapability
- }
- });
-
- instantiationCapability.invoke.and.callFake(
- function (model) {
- objectCopy.model = model;
-
- return objectCopy;
- }
- );
-
- copyService = new CopyService(mockQ, policyService);
- copyResult = copyService.perform(object, newParent);
- copyFinished = jasmine.createSpy('copyFinished');
- copyResult.then(copyFinished);
- });
-
- it("uses persistence capability", function () {
- expect(persistenceCapability.persist)
- .toHaveBeenCalled();
- });
-
- it("deep clones object model", function () {
- var newModel = copyFinished.calls.all()[0].args[0].getModel();
- expect(newModel).toEqual(object.model);
- expect(newModel).not.toBe(object.model);
- });
-
- it("returns a promise", function () {
- expect(copyResult).toBeDefined();
- expect(copyFinished).toHaveBeenCalled();
- });
-
- });
-
- describe("on domainObject with composition", function () {
- var childObject,
- objectClone,
- childObjectClone;
-
- beforeEach(function () {
- var invocationCount = 0,
- objectClones;
-
- instantiationCapability.invoke.and.callFake(
- function (model) {
- var cloneToReturn = objectClones[invocationCount++];
- cloneToReturn.model = model;
-
- return cloneToReturn;
- }
- );
-
- newParent = domainObjectFactory({
- name: 'newParent',
- id: '456',
- model: {
- composition: []
- },
- capabilities: {
- instantiation: instantiationCapability,
- persistence: persistenceCapability,
- composition: compositionCapability
- }
- });
-
- childObject = domainObjectFactory({
- name: 'childObject',
- id: 'def',
- model: {
- name: 'a child object',
- location: 'abc'
- },
- capabilities: {
- persistence: persistenceCapability,
- location: locationCapability
- }
- });
-
- childObjectClone = domainObjectFactory({
- name: 'childObject',
- id: 'def.clone',
- capabilities: {
- persistence: persistenceCapability,
- location: locationCapability
- }
- });
-
- compositionCapability
- .invoke
- .and.returnValue(synchronousPromise([childObject]));
-
- object = domainObjectFactory({
- name: 'some object',
- id: 'abc',
- model: {
- name: 'some object',
- composition: ['def'],
- location: 'testLocation'
- },
- capabilities: {
- instantiation: instantiationCapability,
- composition: compositionCapability,
- location: locationCapability,
- persistence: persistenceCapability
- }
- });
-
- objectClone = domainObjectFactory({
- name: 'some object',
- id: 'abc.clone',
- capabilities: {
- instantiation: instantiationCapability,
- composition: compositionCapability,
- location: locationCapability,
- persistence: persistenceCapability
- }
- });
-
- objectClones = [objectClone, childObjectClone];
-
- copyService = new CopyService(mockQ, policyService);
- });
-
- describe("the cloning process", function () {
- beforeEach(function () {
- copyResult = copyService.perform(object, newParent);
- copyFinished = jasmine.createSpy('copyFinished');
- copyResult.then(copyFinished);
- });
-
- it("returns a promise", function () {
- expect(copyResult.then).toBeDefined();
- expect(copyFinished).toHaveBeenCalled();
- });
-
- it("returns a promise", function () {
- expect(copyResult.then).toBeDefined();
- expect(copyFinished).toHaveBeenCalled();
- });
-
- it ("correctly locates cloned objects", function () {
- expect(childObjectClone.getModel().location).toEqual(objectClone.getId());
- });
- });
-
- describe("when cloning non-creatable objects", function () {
- beforeEach(function () {
- policyService.allow.and.callFake(function (category) {
- //Return false for 'creation' policy
- return category !== 'creation';
- });
-
- copyResult = copyService.perform(object, newParent);
- copyFinished = jasmine.createSpy('copyFinished');
- copyResult.then(copyFinished);
- });
- it ("creates link instead of clone", function () {
- var copiedObject = copyFinished.calls.all()[0].args[0];
- expect(copiedObject).toBe(object);
- expect(compositionCapability.add)
- .toHaveBeenCalledWith(copiedObject);
- });
- });
-
- describe("when provided a filtering function", function () {
- beforeEach(function () {
- copyFinished = jasmine.createSpy('copyFinished');
- });
-
- function accept() {
- return true;
- }
-
- function reject() {
- return false;
- }
-
- it("does not create new instances of objects "
- + "rejected by the filter", function () {
- copyService.perform(object, newParent, reject)
- .then(copyFinished);
- expect(copyFinished.calls.mostRecent().args[0])
- .toBe(object);
- });
-
- it("does create new instances of objects "
- + "accepted by the filter", function () {
- copyService.perform(object, newParent, accept)
- .then(copyFinished);
- expect(copyFinished.calls.mostRecent().args[0])
- .not.toBe(object);
- });
- });
- });
-
- describe("on invalid inputs", function () {
- beforeEach(function () {
- object = domainObjectFactory({
- name: 'object',
- capabilities: {
- type: { type: 'object' },
- location: locationCapability,
- persistence: persistenceCapability
- }
- });
-
- newParent = domainObjectFactory({
- name: 'parentCandidate',
- capabilities: {
- type: { type: 'parentCandidate' },
- instantiation: instantiationCapability,
- composition: compositionCapability,
- persistence: persistenceCapability
- }
- });
-
- instantiationCapability.invoke.and.returnValue(object);
- });
-
- it("throws an error", function () {
- var service =
- new CopyService(mockQ, policyService);
-
- function perform() {
- service.perform(object, newParent);
- }
-
- spyOn(service, "validate");
- service.validate.and.returnValue(true);
- expect(perform).not.toThrow();
- service.validate.and.returnValue(false);
- expect(perform).toThrow();
- });
- });
-
- });
- });
- }
-);
diff --git a/platform/entanglement/test/services/CopyTaskSpec.js b/platform/entanglement/test/services/CopyTaskSpec.js
deleted file mode 100644
index 2bc0fc258..000000000
--- a/platform/entanglement/test/services/CopyTaskSpec.js
+++ /dev/null
@@ -1,267 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [
- '../../src/services/CopyTask',
- '../DomainObjectFactory'
- ],
- function (CopyTask, domainObjectFactory) {
-
- var ID_A = "some-string-with-vaguely-uuidish-uniqueness",
- ID_B = "some-other-similarly-unique-string";
-
- function synchronousPromise(value) {
- return (value && value.then) ? value : {
- then: function (callback) {
- return synchronousPromise(callback(value));
- }
- };
- }
-
- describe("CopyTask", function () {
- var mockDomainObject,
- mockParentObject,
- mockFilter,
- mockQ,
- mockDeferred,
- testModel,
- counter,
- cloneIds,
- task;
-
- function makeMockCapabilities(childIds) {
- var mockCapabilities = {
- persistence: jasmine.createSpyObj(
- 'persistence',
- ['persist']
- ),
- composition: jasmine.createSpyObj(
- 'composition',
- ['add', 'invoke']
- ),
- instantiation: jasmine.createSpyObj(
- 'instantiation',
- ['instantiate', 'invoke']
- )
- },
- mockChildren = (childIds || []).map(function (id) {
- return domainObjectFactory({
- id: id,
- capabilities: makeMockCapabilities([]),
- model: { originalId: id }
- });
- });
-
- mockCapabilities.persistence.persist
- .and.returnValue(synchronousPromise(true));
- mockCapabilities.composition.add.and.callFake(function (obj) {
- return synchronousPromise(obj);
- });
- mockCapabilities.composition.invoke
- .and.returnValue(synchronousPromise(mockChildren));
- mockCapabilities.instantiation.invoke
- .and.callFake(function (model) {
- var id = "some-id-" + counter;
- cloneIds[model.originalId] = id;
- counter += 1;
-
- return domainObjectFactory({
- id: id,
- model: model,
- capabilities: makeMockCapabilities()
- });
- });
-
- return mockCapabilities;
- }
-
- beforeEach(function () {
- counter = 0;
- cloneIds = {};
-
- testModel = {
- composition: [ID_A, ID_B],
- someObj: {},
- someArr: [ID_A, ID_B],
- objArr: [{"id": ID_A}, {"id": ID_B}],
- singleElementArr: [ID_A]
- };
- testModel.someObj[ID_A] = "some value";
- testModel.someObj.someProperty = ID_B;
-
- mockDomainObject = domainObjectFactory({
- capabilities: makeMockCapabilities(testModel.composition),
- model: testModel
- });
- mockParentObject = domainObjectFactory({
- capabilities: makeMockCapabilities()
- });
- mockFilter = jasmine.createSpy('filter');
- mockQ = jasmine.createSpyObj('$q', ['when', 'defer', 'all']);
- mockDeferred = jasmine.createSpyObj(
- 'deferred',
- ['notify', 'resolve', 'reject']
- );
-
- mockFilter.and.returnValue(true);
-
- mockQ.when.and.callFake(synchronousPromise);
- mockQ.defer.and.returnValue(mockDeferred);
- mockQ.all.and.callFake(function (promises) {
- return synchronousPromise(promises.map(function (promise) {
- var value;
- promise.then(function (v) {
- value = v;
- });
-
- return value;
- }));
- });
-
- mockDeferred.resolve.and.callFake(function (value) {
- mockDeferred.promise = synchronousPromise(value);
- });
-
- });
-
- describe("produces models which", function () {
- var model;
-
- beforeEach(function () {
- task = new CopyTask(
- mockDomainObject,
- mockParentObject,
- mockFilter,
- mockQ
- );
-
- task.perform().then(function (clone) {
- model = clone.getModel();
- });
- });
-
- it("contain rewritten identifiers in arrays", function () {
- expect(model.someArr)
- .toEqual(testModel.someArr.map(function (id) {
- return cloneIds[id];
- }));
- });
-
- it("contain rewritten identifiers in properties", function () {
- expect(model.someObj.someProperty)
- .toEqual(cloneIds[testModel.someObj.someProperty]);
- });
-
- it("contain rewritten identifiers in property names", function () {
- expect(model.someObj[cloneIds[ID_A]])
- .toEqual(testModel.someObj[ID_A]);
- });
-
- it("contain rewritten identifiers in single-element arrays", function () {
- expect(model.singleElementArr)
- .toEqual(testModel.singleElementArr.map(function (id) {
- return cloneIds[id];
- }));
- });
- });
-
- describe("copies object trees with multiple references to the"
- + " same object", function () {
- var mockDomainObjectB,
- mockComposingObject,
- composingObjectModel,
- domainObjectClone,
- domainObjectBClone;
-
- beforeEach(function () {
- mockDomainObjectB = domainObjectFactory({
- capabilities: makeMockCapabilities(testModel.composition),
- model: testModel
- });
- composingObjectModel = {
- name: 'mockComposingObject',
- composition: [mockDomainObject.getId(), mockDomainObjectB.getId()]
- };
- mockComposingObject = domainObjectFactory({
- capabilities: makeMockCapabilities(composingObjectModel.composition),
- model: composingObjectModel
- });
-
- mockComposingObject.capabilities.composition.invoke.and.returnValue([mockDomainObject, mockDomainObjectB]);
- task = new CopyTask(
- mockComposingObject,
- mockParentObject,
- mockFilter,
- mockQ
- );
-
- task.perform();
- domainObjectClone = task.clones[2];
- domainObjectBClone = task.clones[5];
- });
-
- /**
- * mockDomainObject and mockDomainObjectB have the same
- * model with references to children ID_A and ID_B. Expect
- * that after duplication the references should differ
- * because they are each now referencing different child
- * objects. This tests the issue reported in #428
- */
- it(" and correctly updates child identifiers in models ", function () {
- var childA_ID = task.clones[0].getId(),
- childB_ID = task.clones[1].getId(),
- childC_ID = task.clones[3].getId(),
- childD_ID = task.clones[4].getId();
-
- expect(domainObjectClone.model.someArr[0]).not.toBe(domainObjectBClone.model.someArr[0]);
- expect(domainObjectClone.model.someArr[0]).toBe(childA_ID);
- expect(domainObjectBClone.model.someArr[0]).toBe(childC_ID);
- expect(domainObjectClone.model.someArr[1]).not.toBe(domainObjectBClone.model.someArr[1]);
- expect(domainObjectClone.model.someArr[1]).toBe(childB_ID);
- expect(domainObjectBClone.model.someArr[1]).toBe(childD_ID);
- expect(domainObjectClone.model.someObj.someProperty).not.toBe(domainObjectBClone.model.someObj.someProperty);
- expect(domainObjectClone.model.someObj.someProperty).toBe(childB_ID);
- expect(domainObjectBClone.model.someObj.someProperty).toBe(childD_ID);
-
- });
-
- /**
- * This a bug found in testathon when testing issue #428
- */
- it(" and correctly updates child identifiers in object"
- + " arrays within models ", function () {
- var childA_ID = task.clones[0].getId(),
- childB_ID = task.clones[1].getId();
-
- expect(domainObjectClone.model.objArr[0].id).not.toBe(ID_A);
- expect(domainObjectClone.model.objArr[0].id).toBe(childA_ID);
- expect(domainObjectClone.model.objArr[1].id).not.toBe(ID_B);
- expect(domainObjectClone.model.objArr[1].id).toBe(childB_ID);
-
- });
- });
-
- });
-
- }
-);
diff --git a/platform/entanglement/test/services/LocatingCreationDecoratorSpec.js b/platform/entanglement/test/services/LocatingCreationDecoratorSpec.js
deleted file mode 100644
index c9ee60ea3..000000000
--- a/platform/entanglement/test/services/LocatingCreationDecoratorSpec.js
+++ /dev/null
@@ -1,76 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [
- '../../src/services/LocatingCreationDecorator'
- ],
- function (LocatingCreationDecorator) {
-
- describe("LocatingCreationDecorator", function () {
- var mockCreationService,
- mockPromise,
- mockParent,
- decorator;
-
- beforeEach(function () {
- mockCreationService = jasmine.createSpyObj(
- 'creationService',
- ['createObject']
- );
- mockPromise = jasmine.createSpyObj(
- 'promise',
- ['then']
- );
- mockParent = jasmine.createSpyObj(
- 'domainObject',
- ['getCapability', 'getId', 'getModel', 'hasCapability', 'useCapability']
- );
- mockCreationService.createObject.and.returnValue(mockPromise);
- mockParent.getId.and.returnValue('test-id');
- decorator = new LocatingCreationDecorator(mockCreationService);
- });
-
- it("delegates to its decorated service", function () {
- expect(decorator.createObject(
- { someKey: "some value" },
- mockParent
- )).toEqual(mockPromise); // promise returned by decoratee
- });
-
- it("adds a location property", function () {
- decorator.createObject(
- { someKey: "some value" },
- mockParent
- );
- expect(mockCreationService.createObject).toHaveBeenCalledWith(
- {
- someKey: "some value",
- location: "test-id" // Parent's identifier
- },
- mockParent
- );
- });
-
- });
- }
-);
diff --git a/platform/entanglement/test/services/LocatingObjectDecoratorSpec.js b/platform/entanglement/test/services/LocatingObjectDecoratorSpec.js
deleted file mode 100644
index 53cb493ec..000000000
--- a/platform/entanglement/test/services/LocatingObjectDecoratorSpec.js
+++ /dev/null
@@ -1,131 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [
- '../../src/services/LocatingObjectDecorator',
- '../../../core/src/capabilities/ContextualDomainObject'
- ],
- function (LocatingObjectDecorator, ContextualDomainObject) {
-
- describe("LocatingObjectDecorator", function () {
- var mockQ,
- mockLog,
- mockObjectService,
- mockCallback,
- testObjects,
- testModels,
- decorator;
-
- function testPromise(v) {
- return (v || {}).then ? v : {
- then: function (callback) {
- return testPromise(callback(v));
- }
- };
- }
-
- beforeEach(function () {
- // A <- B <- C
- // D <-> E, to verify cycle detection
- testModels = {
- a: { name: "A" },
- b: {
- name: "B",
- location: "a"
- },
- c: {
- name: "C",
- location: "b"
- },
- d: {
- name: "D",
- location: "e"
- },
- e: {
- name: "E",
- location: "d"
- }
- };
- testObjects = {};
-
- mockQ = jasmine.createSpyObj("$q", ["when", "all"]);
- mockLog =
- jasmine.createSpyObj("$log", ["error", "warn", "info", "debug"]);
- mockObjectService =
- jasmine.createSpyObj("objectService", ["getObjects"]);
-
- mockQ.when.and.callFake(testPromise);
- mockQ.all.and.callFake(function (promises) {
- var result = {};
- Object.keys(promises).forEach(function (k) {
- promises[k].then(function (v) {
- result[k] = v;
- });
- });
-
- return testPromise(result);
- });
-
- mockObjectService.getObjects.and.returnValue(testPromise(testObjects));
-
- mockCallback = jasmine.createSpy("callback");
-
- Object.keys(testModels).forEach(function (id) {
- testObjects[id] = jasmine.createSpyObj(
- "domainObject-" + id,
- ["getId", "getModel", "getCapability"]
- );
- testObjects[id].getId.and.returnValue(id);
- testObjects[id].getModel.and.returnValue(testModels[id]);
- });
-
- decorator = new LocatingObjectDecorator(
- mockQ,
- mockLog,
- mockObjectService
- );
- });
-
- it("contextualizes domain objects", function () {
- decorator.getObjects(['b', 'c']).then(mockCallback);
- expect(mockCallback).toHaveBeenCalled();
-
- var callbackObj = mockCallback.calls.mostRecent().args[0];
- expect(testObjects.b.getCapability('context')).not.toBeDefined();
- expect(testObjects.c.getCapability('context')).not.toBeDefined();
- expect(callbackObj.b.getCapability('context')).toBeDefined();
- expect(callbackObj.c.getCapability('context')).toBeDefined();
- });
-
- it("warns on cycle detection", function () {
- // Base case, no cycle, no warning
- decorator.getObjects(['a', 'b', 'c']);
- expect(mockLog.warn).not.toHaveBeenCalled();
-
- decorator.getObjects(['e']);
- expect(mockLog.warn).toHaveBeenCalled();
- });
-
- });
- }
-);
diff --git a/platform/entanglement/test/services/LocationServiceSpec.js b/platform/entanglement/test/services/LocationServiceSpec.js
deleted file mode 100644
index 5ab5eecf4..000000000
--- a/platform/entanglement/test/services/LocationServiceSpec.js
+++ /dev/null
@@ -1,151 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [
- '../../src/services/LocationService'
- ],
- function (LocationService) {
-
- describe("LocationService", function () {
- var dialogService,
- locationService,
- dialogServicePromise,
- chainedPromise;
-
- beforeEach(function () {
- dialogService = jasmine.createSpyObj(
- 'dialogService',
- ['getUserInput']
- );
- dialogServicePromise = jasmine.createSpyObj(
- 'dialogServicePromise',
- ['then']
- );
- chainedPromise = jasmine.createSpyObj(
- 'chainedPromise',
- ['then']
- );
- dialogServicePromise.then.and.returnValue(chainedPromise);
- dialogService.getUserInput.and.returnValue(dialogServicePromise);
- locationService = new LocationService(dialogService);
- });
-
- describe("getLocationFromUser", function () {
- var title,
- label,
- validate,
- initialLocation,
- locationResult,
- formStructure,
- formState;
-
- beforeEach(function () {
- title = "Get a location to do something";
- label = "a location";
- validate = function () {
- return true;
- };
-
- initialLocation = { key: "a key" };
- locationResult = locationService.getLocationFromUser(
- title,
- label,
- validate,
- initialLocation
- );
- formStructure = dialogService
- .getUserInput
- .calls.mostRecent()
- .args[0];
- formState = dialogService
- .getUserInput
- .calls.mostRecent()
- .args[1];
- });
-
- it("calls through to dialogService", function () {
- expect(dialogService.getUserInput).toHaveBeenCalledWith(
- jasmine.any(Object),
- jasmine.any(Object)
- );
- expect(formStructure.name).toBe(title);
- });
-
- it("returns a promise", function () {
- expect(locationResult.then).toBeDefined();
- });
-
- describe("formStructure", function () {
- var locationSection,
- inputRow;
-
- beforeEach(function () {
- locationSection = formStructure.sections[0];
- inputRow = locationSection.rows[0];
- });
-
- it("has a location section", function () {
- expect(locationSection).toBeDefined();
- expect(locationSection.name).toBe('Location');
- });
-
- it("has a input row", function () {
- expect(inputRow.control).toBe('locator');
- expect(inputRow.key).toBe('location');
- expect(inputRow.name).toBe(label);
- expect(inputRow.validate).toBe(validate);
- });
- });
-
- describe("formState", function () {
- it("has an initial location", function () {
- expect(formState.location).toBe(initialLocation);
- });
- });
-
- describe("resolution of dialog service promise", function () {
- var resolution,
- resolver,
- dialogResult,
- selectedLocation;
-
- beforeEach(function () {
- resolver =
- dialogServicePromise.then.calls.mostRecent().args[0];
-
- selectedLocation = { key: "i'm a location key" };
- dialogResult = {
- location: selectedLocation
- };
-
- resolution = resolver(dialogResult);
- });
-
- it("returns selectedLocation", function () {
- expect(resolution).toBe(selectedLocation);
- });
- });
- });
- });
- }
-);
diff --git a/platform/entanglement/test/services/MockCopyService.js b/platform/entanglement/test/services/MockCopyService.js
deleted file mode 100644
index 51c4a9d06..000000000
--- a/platform/entanglement/test/services/MockCopyService.js
+++ /dev/null
@@ -1,96 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- function () {
-
- /**
- * MockCopyService provides the same interface as the copyService,
- * returning promises where it would normally do so. At it's core,
- * it is a jasmine spy object, but it also tracks the promises it
- * returns and provides shortcut methods for resolving those promises
- * synchronously.
- *
- * Usage:
- *
- * ```javascript
- * var copyService = new MockCopyService();
- *
- * // validate is a standard jasmine spy.
- * copyService.validate.and.returnValue(true);
- * var isValid = copyService.validate(object, parentCandidate);
- * expect(isValid).toBe(true);
- *
- * // perform returns promises and tracks them.
- * var whenCopied = jasmine.createSpy('whenCopied');
- * copyService.perform(object, parentObject).then(whenCopied);
- * expect(whenCopied).not.toHaveBeenCalled();
- * copyService.perform.calls.mostRecent().resolve('someArg');
- * expect(whenCopied).toHaveBeenCalledWith('someArg');
- * ```
- */
- function MockCopyService() {
- // track most recent call of a function,
- // perform automatically returns
- var mockCopyService = jasmine.createSpyObj(
- 'MockCopyService',
- [
- 'validate',
- 'perform'
- ]
- );
-
- mockCopyService.perform.and.callFake(() => {
- var performPromise,
- callExtensions,
- spy;
-
- performPromise = jasmine.createSpyObj(
- 'performPromise',
- ['then']
- );
-
- callExtensions = {
- promise: performPromise,
- resolve: function (resolveWith) {
- performPromise.then.calls.all().forEach(function (call) {
- call.args[0](resolveWith);
- });
- }
- };
-
- spy = mockCopyService.perform;
-
- Object.keys(callExtensions).forEach(function (key) {
- spy.calls.mostRecent()[key] = callExtensions[key];
- spy.calls.all()[spy.calls.count() - 1][key] = callExtensions[key];
- });
-
- return performPromise;
- });
-
- return mockCopyService;
- }
-
- return MockCopyService;
- }
-);
diff --git a/platform/entanglement/test/services/MockLinkService.js b/platform/entanglement/test/services/MockLinkService.js
deleted file mode 100644
index 27cb60f46..000000000
--- a/platform/entanglement/test/services/MockLinkService.js
+++ /dev/null
@@ -1,86 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [
- '../ControlledPromise'
- ],
- function (ControlledPromise) {
-
- /**
- * MockLinkService provides the same interface as the linkService,
- * returning promises where it would normally do so. At it's core,
- * it is a jasmine spy object, but it also tracks the promises it
- * returns and provides shortcut methods for resolving those promises
- * synchronously.
- *
- * Usage:
- *
- * ```javascript
- * var linkService = new MockLinkService();
- *
- * // validate is a standard jasmine spy.
- * linkService.validate.and.returnValue(true);
- * var isValid = linkService.validate(object, parentObject);
- * expect(isValid).toBe(true);
- *
- * // perform returns promises and tracks them.
- * var whenLinked = jasmine.createSpy('whenLinked');
- * linkService.perform(object, parentObject).then(whenLinked);
- * expect(whenLinked).not.toHaveBeenCalled();
- * linkService.perform.calls.mostRecent().promise.resolve('someArg');
- * expect(whenLinked).toHaveBeenCalledWith('someArg');
- * ```
- */
- function MockLinkService() {
- // track most recent call of a function,
- // perform automatically returns
- var mockLinkService = jasmine.createSpyObj(
- 'MockLinkService',
- [
- 'validate',
- 'perform'
- ]
- );
-
- mockLinkService.perform.and.callFake(object => {
- var performPromise = new ControlledPromise();
-
- mockLinkService.perform.calls.mostRecent().promise = performPromise;
- mockLinkService.perform.calls.all()[mockLinkService.perform.calls.count() - 1].promise =
- performPromise;
-
- return performPromise.then(function (overrideObject) {
- if (overrideObject) {
- return overrideObject;
- }
-
- return object;
- });
- });
-
- return mockLinkService;
- }
-
- return MockLinkService;
- }
-);
diff --git a/platform/exporters/ExportService.js b/platform/exporters/ExportService.js
deleted file mode 100644
index 76ba17902..000000000
--- a/platform/exporters/ExportService.js
+++ /dev/null
@@ -1,92 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * @namespace platform/exporters
- */
-define(['csv'], function (CSV) {
-
- /**
- * Callback used to initiate saving files from the export service;
- * typical implementation is
- * [FileSaver.js](https://github.com/eligrey/FileSaver.js/).
- * @callback platform/exporters.ExportService~saveAs
- * @param {Blob} blob the contents of the file to export
- * @param {string} filename the name of the file to export
- */
-
- /**
- * The `exportService` provides a means to initiate downloads of
- * structured data in the CSV format.
- * @param {platform/exporters.ExportService~saveAs} saveAs function
- * used to initiate saving files
- * @constructor
- * @memberof platform/exporters
- */
- function ExportService(saveAs) {
- this.saveAs = saveAs;
- }
-
- /**
- * Export a set of data as comma-separated values. Triggers a download
- * using the function provided when the ExportService was instantiated.
- *
- * @param {Object[]} rows an array of objects containing key-value pairs,
- * where keys are header names, and values are values
- * @param {ExportOptions} [options] additional parameters for the file
- * export
- */
- ExportService.prototype.exportCSV = function (rows, options) {
- var headers = (options && options.headers)
- || (Object.keys((rows[0] || {})).sort()),
- filename = (options && options.filename) || "export.csv",
- csvText = new CSV(rows, { header: headers }).encode(),
- blob = new Blob([csvText], { type: "text/csv" });
- this.saveAs(blob, filename);
- };
-
- /**
- * Export an object as a JSON file. Triggers a download using the function
- * provided when the ExportService was instantiated.
- *
- * @param {Object} obj an object to be exported as JSON
- * @param {ExportOptions} [options] additional parameters for the file
- * export
- */
- ExportService.prototype.exportJSON = function (obj, options) {
- var filename = (options && options.filename) || "test-export.json";
- var jsonText = JSON.stringify(obj);
- var blob = new Blob([jsonText], {type: "application/json"});
- this.saveAs(blob, filename);
- };
- /**
- * Additional parameters for file export.
- * @typedef ExportOptions
- * @property {string} filename the name of the file to write
- * @property {string[]} headers column header names, both as they
- * should appear in the output and as they should be
- * used to look up values from the data set. Defaults
- * to the keys in the first object in the data set.
- */
-
- return ExportService;
-});
diff --git a/platform/exporters/ExportServiceSpec.js b/platform/exporters/ExportServiceSpec.js
deleted file mode 100644
index f21e88071..000000000
--- a/platform/exporters/ExportServiceSpec.js
+++ /dev/null
@@ -1,159 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["./ExportService", "csv"],
- function (ExportService, CSV) {
-
- describe("ExportService", function () {
- var mockSaveAs,
- testRows,
- csvContents,
- readCSVPromise,
- exportService;
-
- beforeEach(function () {
- var resolveFunction;
- csvContents = undefined;
- testRows = [
- {
- a: 1,
- b: 2,
- c: 3
- },
- {
- a: 4,
- b: 5,
- c: 6
- },
- {
- a: 7,
- b: 8,
- c: 9
- }
- ];
- mockSaveAs = jasmine.createSpy('saveAs');
- readCSVPromise = new Promise(function (resolve) {
- resolveFunction = resolve;
- });
- mockSaveAs.and.callFake(function (blob) {
- var reader = new FileReader();
- reader.onloadend = function () {
- csvContents = new CSV(reader.result).parse();
- resolveFunction();
- };
-
- reader.readAsText(blob);
- });
- exportService = new ExportService(mockSaveAs);
- });
-
- describe("#exportCSV(rows)", function () {
- beforeEach(function () {
- exportService.exportCSV(testRows);
-
- return readCSVPromise;
- });
-
- it("triggers saving of a file", function () {
- expect(mockSaveAs).toHaveBeenCalledWith(
- jasmine.any(Blob),
- jasmine.any(String)
- );
- });
-
- it("includes headers from the data set", function () {
- expect(csvContents[0])
- .toEqual(Object.keys(testRows[0]).sort());
- });
-
- it("includes data from the data set", function () {
- var headers = csvContents[0],
- expectedData = testRows.map(function (row) {
- return headers.map(function (key) {
- return String(row[key]);
- });
- });
- // Everything after header should be data
- expect(csvContents.slice(1)).toEqual(expectedData);
- });
- });
-
- describe("#exportCSV(rows, options.headers)", function () {
- var testHeaders;
-
- beforeEach(function () {
- testHeaders = ['a', 'b'];
- exportService
- .exportCSV(testRows, { headers: testHeaders });
-
- return readCSVPromise;
- });
-
- it("triggers saving of a file", function () {
- expect(mockSaveAs).toHaveBeenCalledWith(
- jasmine.any(Blob),
- jasmine.any(String)
- );
- });
-
- it("includes only the specified headers", function () {
- expect(csvContents[0])
- .toEqual(testHeaders);
- expect(csvContents[0])
- .not.toEqual(Object.keys(testRows[0]).sort());
- });
-
- it("includes a subset data from the data set", function () {
- var headers = testHeaders,
- expectedData = testRows.map(function (row) {
- return headers.map(function (key) {
- return String(row[key]);
- });
- });
- expect(csvContents.slice(1)).toEqual(expectedData);
- });
- });
-
- describe("#exportCSV(rows, options.filename)", function () {
- var testFilename;
-
- beforeEach(function () {
- testFilename = "some-test-filename.csv";
- exportService
- .exportCSV(testRows, { filename: testFilename });
-
- return readCSVPromise;
- });
-
- it("saves a file with the specified name", function () {
- expect(mockSaveAs).toHaveBeenCalledWith(
- jasmine.any(Blob),
- testFilename
- );
- });
- });
-
- });
-
- }
-);
diff --git a/platform/exporters/bundle.js b/platform/exporters/bundle.js
deleted file mode 100644
index 55b3e8645..000000000
--- a/platform/exporters/bundle.js
+++ /dev/null
@@ -1,65 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
- "./ExportService",
- "saveAs"
-], function (ExportService, saveAs) {
-
- return {
- name: "platform/exporters",
- definition: {
- extensions: {
- services: [
- {
- key: "exportService",
- implementation: function () {
- return new ExportService(saveAs.saveAs);
- }
- }
- ],
- licenses: [
- {
- "name": "CSV.js",
- "version": "3.6.4",
- "author": "Kash Nouroozi",
- "description": "Encoder for CSV (comma separated values) export",
- "website": "https://github.com/knrz/CSV.js",
- "copyright": "Copyright (c) 2014 Kash Nouroozi",
- "license": "license-mit",
- "link": "https://github.com/knrz/CSV.js/blob/3.6.4/LICENSE"
- },
- {
- "name": "FileSaver.js",
- "version": "0.0.2",
- "author": "Eli Grey",
- "description": "File download initiator (for file exports)",
- "website": "https://github.com/eligrey/FileSaver.js/",
- "copyright": "Copyright © 2015 Eli Grey.",
- "license": "license-mit",
- "link": "https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md"
- }
- ]
- }
- }
- };
-});
diff --git a/platform/features/README.md b/platform/features/README.md
deleted file mode 100644
index c839461c4..000000000
--- a/platform/features/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-This directory contains bundles which represent specific user-facing
-features of Open MCT, such as plots and other visualizations.
-Bundles in this directory should be effectively optional.
diff --git a/platform/features/pages/bundle.js b/platform/features/pages/bundle.js
deleted file mode 100644
index b2c631b32..000000000
--- a/platform/features/pages/bundle.js
+++ /dev/null
@@ -1,77 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
- "./src/EmbeddedPageController",
- "./res/iframe.html"
-], function (
- EmbeddedPageController,
- iframeTemplate
-) {
-
- return {
- name: "platform/features/pages",
- definition: {
- "extensions": {
- "types": [
- {
- "key": "example.page",
- "name": "Web Page",
- "cssClass": "icon-page",
- "description": "Embed a web page or web-based image in a resizeable window component. Can be added to Display Layouts. Note that the URL being embedded must allow iframing.",
- "priority": 50,
- "features": [
- "creation"
- ],
- "properties": [
- {
- "key": "url",
- "name": "URL",
- "control": "textfield",
- "required": true,
- "cssClass": "l-input-lg"
- }
- ]
- }
- ],
- "views": [
- {
- "template": iframeTemplate,
- "name": "Page",
- "type": "example.page",
- "key": "example.page",
- "editable": false
- }
- ],
- "controllers": [
- {
- "key": "EmbeddedPageController",
- "implementation": EmbeddedPageController,
- "depends": [
- "$sce"
- ]
- }
- ]
- }
- }
- };
-});
diff --git a/platform/features/static-markup/bundle.js b/platform/features/static-markup/bundle.js
deleted file mode 100644
index ceddc93ac..000000000
--- a/platform/features/static-markup/bundle.js
+++ /dev/null
@@ -1,56 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
-
- "./res/markup.html"
-], function (
- markupTemplate
-) {
-
- return {
- name: "platform/features/static-markup",
- definition: {
- "extensions": {
- "types": [
- {
- "key": "static.markup",
- "name": "Static Markup",
- "cssClass": "icon-pencil",
- "description": "Static markup sandbox",
- "features": [
- "creation"
- ]
- }
- ],
- "views": [
- {
- "template": markupTemplate,
- "name": "Static Markup",
- "type": "static.markup",
- "key": "static.markup"
- }
- ]
- }
- }
- };
-});
diff --git a/platform/features/static-markup/res/markup.html b/platform/features/static-markup/res/markup.html
deleted file mode 100644
index 72d3c9c44..000000000
--- a/platform/features/static-markup/res/markup.html
+++ /dev/null
@@ -1,24 +0,0 @@
-<h1>Static Markup Sandbox</h1>
-
-<h2>Plot limits</h2>
-<div ng-init="limits=[
- {type: 'upr', severity: 'red', top: 0, bottom: 90},
- {type: 'upr', severity: 'yellow', top: 10, bottom: 80},
- {type: 'lwr', severity: 'yellow', top: 70, bottom: 20},
- {type: 'lwr', severity: 'red', top: 80, bottom: 0}
- ]"></div>
-<div style="width: 1000px; height: 500px">
- <div class="gl-plot" style="height: 100%;">
- <div class="gl-plot-display-area">
- <div
- ng-repeat="limit in limits"
- ng-show="1"
- class="t-limit l-limit s-limit-{{limit.type}}-{{limit.severity}}"
- style="top: {{limit.top}}%; bottom: {{limit.bottom}}%"
- ></div>
- </div>
- </div>
-</div>
-
-<h2>Animation</h2>
-<div class="pulse" style="background: #cc0000; color: #fff; padding: 10px;">This should pulse</div>
diff --git a/platform/framework/README.md b/platform/framework/README.md
deleted file mode 100644
index 5b5fbc8bc..000000000
--- a/platform/framework/README.md
+++ /dev/null
@@ -1,192 +0,0 @@
-Framework-level components for Open MCT. This is Angular and Require,
-with an extra layer to mediate between them and act as an extension
-mechanism to allow plug-ins to be introduced declaratively.
-
-# Usage
-
-This section needs to be written. For now, refer to implementation notes and
-examples in `example/builtins`, `example/extensions`, and `example/composite`.
-
-## Circular dependencies
-
-The framework layer (like Angular itself) does not support circular
-dependencies among extensions. Generally, circular dependencies can be
-avoided by refactoring; for instance, a dependency-less intermediary can be
-added by two parties which depend upon one another, and both can depend upon
-this intermediary while one abandons its dependency to the other (the
-intermediary must then provide the functionality that was needed in the
-abandoned dependency.)
-
-In some cases this refactoring is non-obvious or ineffective (for instance,
-when a service component depends upon the whole.) In these cases, Angular's
-`$injector` may be used to break the declaration-time dependency, by allowing
-retrieval of the dependency at use-time instead. (This is essentially the
-same solution as above, where `$injector` acts as an application-global
-generalized intermediary.)
-
-# Implementation Notes
-
-The framework layer is responsible for performing a four-stage initialization
-process. These stages are:
-
-1. __Loading definitions.__ JSON declarations are loaded for all bundles which
- will constitute the application, and wrapped in a useful API for subsequent
- stages. _Sources in `src/load`_
-2. __Resolving extensions.__ Any scripts which provide implementations for
- extensions exposed by bundles are loaded, using Require.
- _Sources in `src/resolve`_
-3. __Registering extensions.__ Resolved extensions are registered with Angular,
- such that they can be used by the application at run-time. This stage
- includes both registration of Angular built-ins (directives, controllers,
- routes, constants, and services) as well as registration of non-Angular
- extensions. _Sources in `src/register`_
-4. __Bootstrapping.__ JSON declarations are loaded for all bundles which
- will constitute the application, and wrapped in a useful API for subsequent
- stages. _Sources in `src/bootstrap`_
-
-Additionally, the framework layer takes responsibility for initializing
-other application state. Currently this simply means adding Promise to the
-global namespace if it is not defined.
-
-## Load stage
-
-Using Angular's `$http`, the list of installed bundles is loaded from
-`bundles.json`; then, each bundle's declaration (its path + `bundle.json`)
-is loaded. These are wrapped by `Bundle` objects, and the extensions they
-expose are wrapped by `Extension` objects; this is only to provide a
-useful API for subsequent stages.
-
-A bundle is a set of related extensions; an extension is an individual
-unit of the application that is meant to be used by other pieces of the
-application.
-
-## Resolution stage
-
-Some, but not all, individual extensions have corresponding scripts.
-These are referred to by the `implementation` field in their extension
-definition. The implementation name should not include the bundle path,
-or the name of the source folder; these will be pre-pended by the framework
-during this stage. The implementation name should include a `.js` extension.
-
-Bundles may utilize third-party libraries, and may wish to expose these such
-that other bundles may use them. Require JS may need special configuration
-to recognize and utilize third-party libraries, and when exposing a
-third-party library it may be desirable to do so under a short name
-(to avoid long relative paths.) Such configuration is performed during the
-resolution stage, immediately before implementations are loaded. Any
-`configuration` properties from a bundle's definition (`bundle.json`) will
-be used to perform this configuration; these `configuration` should take
-the same form as needed to populate a
-[`require.config`](http://requirejs.org/docs/api.html#config) call.
-At present, only `shim` and `paths` options are supported; any `paths` will
-be prepended with the bundle's library path (the bundle's `lib` folder, by
-default; this directory name can be overridden by specifying a `libraries`
-property in `bundles.json`.)
-
-An extension is resolved by loading its implementing script, if one has been
-declared. If none is declared, the extension's raw definition is used
-instead. To ensure that extensions look similar regardless of whether or
-not an implementation is present, all key-value pairs from the definition
-are copied to the loaded implementation (if one has been loaded.)
-
-## Registration stage
-
-Following implementation resolution, extensions are registered by Angular.
-How this registration occurs depends on whether or not there is built in
-support for the category of extension being registered.
-
-* For _built-in_ extension types (recognized by Angular), these are
- registered with the application module. These categories are `directives`,
- `controllers`, `services`, `constants`, and `routes`.
-* For _composite services_, extensions of category `components` are passed
- to the service compositor, which builds up a dependency graph among
- the components such that their fully-wired whole is exposed as a single
- service.
-* For _general extensions_, the resolved extensions are assembled into a
- list, with Angular-level dependencies are declared, and the full set
- is exposed as a single Angular "service."
-
-### Priority order
-
-Within each category, registration occurs in priority order. An extension's
-priority may be specified as a `priority` property in its extension
-definition; this may be a number, or a symbolic string. Extensions are
-registered in reverse numeric order (highest-priority first), and symbolic
-strings are mapped to the numeric values as follows:
-
-* `fallback`: Negative infinity. Used for extensions that are not intended
- for use (that is, they are meant to be overridden) but are present as an
- option of last resort.
-* `default`: -100. Used for extensions that are expected to be overridden, but
- need a useful default.
-* `none`: 0. Also used if no priority is specified, or if an unknown or
- malformed priority is specified.
-* `optional`: 100. Used for extensions that are meant to be used, but may be
- overridden.
-* `preferred`: 1000. Used for extensions that are specifically intended to
- be used, but still may be overridden in principle.
-* `mandatory`: Positive infinity. Used when an extension should definitely
- not be overridden.
-
-These symbolic names are chosen to reflect usage where many extensions may
-satisfy a given usage, but only one may be used; in this case, as a
-convention it should be the lowest-ordered (highest-priority) extensions
-available. In other cases, a full set (or multi-element subset) of
-extensions may be desired, with a specific ordering; in these cases, it
-is preferable to specify priority numerically when declaring extensions,
-and to understand that extensions will be sorted according to these
-conventions when using them.
-
-### Composite services
-
-Composite services are assumed to follow a provider-aggregator-decorator
-pattern where:
-
-* _Providers_ have dependencies as usual, and expose the API associated
- with the service they compose. Providers are full service implementations
- in-and-of-themselves.
-* _Aggregators_ have dependencies as usual plus one additional dependency,
- which will be satisfied by the array of all providers registered of
- that type of service. Implementations are assumed to include an extra
- argument (after what they declare in `depends`) to receive this array.
- Aggregators make multiple providers appear as one.
-* _Decorators_ have dependencies as usual plus one additional dependency,
- which will be satisfied by either an aggregator (if one is present),
- the latest provider (if no aggregator is present), or another decorator
- (if multiple decorators are present.) As with aggregators, an additional
- argument should be accepted by the implementation to receive this.
- Decorators modify or augment the behavior of a service, but do not
- provide its core functionality.
-* All of the above must be declared with a `provides` property, which
- indicates which type of service they compose. Providers will only be
- paired with aggregators of matching types, and so on. The value of
- this property is also the name of the service that is ultimately
- registered with Angular to represent the composite service as a whole.
-
-The service compositor handles this in five steps:
-
-1. All providers are registered.
-2. Arrays of providers are registered.
-3. All aggregators are registered (with dependencies to the arrays
- registered in the previous step.)
-4. All decorators are registered (with dependencies on the most recent
- components of matching types.)
-5. Full composite services are registered (essentially aliasing back
- to the latest component registered of a given type.)
-
-Throughout these steps, components are registered with Angular using
-generated names like `typeService[decorator#11]`. It is technically possible
-to reference these dependencies elsewhere but that is not the intent.
-Rather, the resulting composed service should be referred to as
-`typeService` (or, more generally, the value matched from the `provides`
-field of the paired service components.)
-
-### General extensions
-
-Similar to composite services, each individual general extension gets
-registered using a generated name, like `types[extension#0]`. These are
-not intended to be referenced directly; instead, they are declared
-dependencies of the full list of general extensions of a given category.
-This list of extensions is registered with a square-brackets suffix,
-like `types[]`; this _is_ intended to be declared as a dependency by
-non-framework code.
diff --git a/platform/framework/bundle.js b/platform/framework/bundle.js
deleted file mode 100644
index aaead5e2f..000000000
--- a/platform/framework/bundle.js
+++ /dev/null
@@ -1,107 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([], function () {
-
- return {
- name: "platform/framework",
- definition: {
- "name": "Open MCT Framework Component",
- "description": "Framework layer for Open MCT; interprets bundle definitions and serves as an intermediary between Require and Angular",
- "libraries": "lib",
- "configuration": {
- "paths": {
- "angular": "angular.min"
- },
- "shim": {
- "angular": {
- "exports": "angular"
- }
- }
- },
- "extensions": {
- "licenses": [
- {
- "name": "Blanket.js",
- "version": "1.1.5",
- "description": "Code coverage measurement and reporting",
- "author": "Alex Seville",
- "website": "http://blanketjs.org/",
- "copyright": "Copyright (c) 2013 Alex Seville",
- "license": "license-mit",
- "link": "http://opensource.org/licenses/MIT"
- },
- {
- "name": "Jasmine",
- "version": "1.3.1",
- "description": "Unit testing",
- "author": "Pivotal Labs",
- "website": "http://jasmine.github.io/",
- "copyright": "Copyright (c) 2008-2011 Pivotal Labs",
- "license": "license-mit",
- "link": "http://opensource.org/licenses/MIT"
- },
- {
- "name": "RequireJS",
- "version": "2.1.22",
- "description": "Script loader",
- "author": "The Dojo Foundation",
- "website": "http://requirejs.org/",
- "copyright": "Copyright (c) 2010-2015, The Dojo Foundation",
- "license": "license-mit",
- "link": "https://github.com/jrburke/requirejs/blob/master/LICENSE"
- },
- {
- "name": "AngularJS",
- "version": "1.4.4",
- "description": "Client-side web application framework",
- "author": "Google",
- "website": "http://angularjs.org/",
- "copyright": "Copyright (c) 2010-2015 Google, Inc. http://angularjs.org",
- "license": "license-mit",
- "link": "https://github.com/angular/angular.js/blob/v1.4.4/LICENSE"
- },
- {
- "name": "Angular-Route",
- "version": "1.4.4",
- "description": "Client-side view routing",
- "author": "Google",
- "website": "http://angularjs.org/",
- "copyright": "Copyright (c) 2010-2015 Google, Inc. http://angularjs.org",
- "license": "license-mit",
- "link": "https://github.com/angular/angular.js/blob/v1.4.4/LICENSE"
- },
- {
- "name": "ES6-Promise",
- "version": "3.0.2",
- "description": "Promise polyfill for pre-ECMAScript 6 browsers",
- "author": "Yehuda Katz, Tom Dale, Stefan Penner and contributors",
- "website": "https://github.com/jakearchibald/es6-promise",
- "copyright": "Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors",
- "license": "license-mit",
- "link": "https://github.com/jakearchibald/es6-promise/blob/master/LICENSE"
- }
- ]
- }
- }
- };
-});
diff --git a/platform/framework/src/Constants.js b/platform/framework/src/Constants.js
deleted file mode 100644
index 1e06ad24e..000000000
--- a/platform/framework/src/Constants.js
+++ /dev/null
@@ -1,48 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Constants used by the framework layer.
- */
-define({
- MODULE_NAME: "OpenMCTWeb",
- BUNDLE_FILE: "bundle.json",
- SEPARATOR: "/",
- EXTENSION_SUFFIX: "[]",
- DEFAULT_BUNDLE: {
- "sources": "src",
- "resources": "res",
- "libraries": "lib",
- "tests": "test",
- "configuration": {},
- "extensions": {}
- },
- PRIORITY_LEVELS: {
- "fallback": Number.NEGATIVE_INFINITY,
- "default": -100,
- "none": 0,
- "optional": 100,
- "preferred": 1000,
- "mandatory": Number.POSITIVE_INFINITY
- },
- DEFAULT_PRIORITY: 0
-});
diff --git a/platform/framework/src/FrameworkInitializer.js b/platform/framework/src/FrameworkInitializer.js
deleted file mode 100644
index 43502bb78..000000000
--- a/platform/framework/src/FrameworkInitializer.js
+++ /dev/null
@@ -1,73 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Module defining FrameworkInitializer. Created by vwoeltje on 11/3/14.
- */
-define(
- [],
- function () {
-
- /**
- * Responsible for managing the four stages of framework
- * initialization:
- *
- * * Loading bundle metadata (JSON files)
- * * Resolving extension implementations with Require
- * * Registering extensions with Angular
- * * Bootstrapping the Angular application.
- *
- * @memberof platform/framework
- * @constructor
- * @param {platform/framework.BundleLoader} loader
- * @param {platform/framework.BundleResolver} resolver
- * @param {platform/framework.ExtensionRegistrar} registrar
- * @param {platform/framework.ApplicationBootstrapper} bootstrapper
- */
- function FrameworkInitializer(loader, resolver, registrar, bootstrapper) {
- this.loader = loader;
- this.resolver = resolver;
- this.registrar = registrar;
- this.bootstrapper = bootstrapper;
- }
-
- function bind(method, thisArg) {
- return function () {
- return method.apply(thisArg, arguments);
- };
- }
-
- /**
- * Run the application defined by this set of bundles.
- * @param bundleList
- * @returns {*}
- */
- FrameworkInitializer.prototype.runApplication = function () {
- return this.loader.loadBundles([])
- .then(bind(this.resolver.resolveBundles, this.resolver))
- .then(bind(this.registrar.registerExtensions, this.registrar))
- .then(bind(this.bootstrapper.bootstrap, this.bootstrapper));
- };
-
- return FrameworkInitializer;
- }
-);
diff --git a/platform/framework/src/FrameworkLayer.js b/platform/framework/src/FrameworkLayer.js
deleted file mode 100644
index 2c9e86be5..000000000
--- a/platform/framework/src/FrameworkLayer.js
+++ /dev/null
@@ -1,104 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
- './Constants',
- './FrameworkInitializer',
- './LogLevel',
- './load/BundleLoader',
- './resolve/ImplementationLoader',
- './resolve/ExtensionResolver',
- './resolve/BundleResolver',
- './register/CustomRegistrars',
- './register/ExtensionRegistrar',
- './register/ExtensionSorter',
- './bootstrap/ApplicationBootstrapper'
-], function (
- Constants,
- FrameworkInitializer,
- LogLevel,
- BundleLoader,
- ImplementationLoader,
- ExtensionResolver,
- BundleResolver,
- CustomRegistrars,
- ExtensionRegistrar,
- ExtensionSorter,
- ApplicationBootstrapper
-) {
-
- function FrameworkLayer($http, $log) {
- this.$http = $http;
- this.$log = $log;
- }
-
- FrameworkLayer.prototype.initializeApplication = function (
- angular,
- openmct,
- logLevel
- ) {
- var $http = this.$http,
- $log = this.$log,
- app = angular.module(Constants.MODULE_NAME, ["ngRoute"]),
- loader = new BundleLoader($http, $log, openmct.legacyRegistry),
- resolver = new BundleResolver(
- new ExtensionResolver(
- new ImplementationLoader({}),
- $log
- ),
- $log
- ),
- registrar = new ExtensionRegistrar(
- app,
- new CustomRegistrars(app, $log),
- new ExtensionSorter($log),
- $log
- ),
- bootstrapper = new ApplicationBootstrapper(
- angular,
- openmct.element,
- $log
- ),
- initializer = new FrameworkInitializer(
- loader,
- resolver,
- registrar,
- bootstrapper
- );
-
- // Override of angular1.6 ! hashPrefix
- app.config(['$locationProvider', function ($locationProvider) {
- $locationProvider.hashPrefix('');
- }]);
-
- // Apply logging levels; this must be done now, before the
- // first log statement.
- new LogLevel(logLevel).configure(app, $log);
-
- // Initialize the application
- $log.info("Initializing application.");
-
- return initializer.runApplication();
- };
-
- return FrameworkLayer;
-});
diff --git a/platform/framework/src/LogLevel.js b/platform/framework/src/LogLevel.js
deleted file mode 100644
index f90ea6c84..000000000
--- a/platform/framework/src/LogLevel.js
+++ /dev/null
@@ -1,100 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- // Log levels; note that these must be in order of
- // most-important-first for LogLevel to function correctly
- // as implemented.
- var LOG_LEVELS = [
- 'error',
- 'warn',
- 'info',
- 'log',
- 'debug'
- ];
-
- // No-op, to replace undesired log levels with
- function NOOP() {}
-
- /**
- * Handles enforcement of logging at different levels, specified
- * at load time. The provided level should be one of "error",
- * "warn", "info", "log", or "debug"; otherwise, "warn" is used
- * as a default. Only log messages of levels equal to or greater
- * than the specified level will be passed to console.
- *
- * @memberof platform/framework
- * @constructor
- * @param {string} level the logging level
- */
- function LogLevel(level) {
- // Find the numeric level associated with the string
- this.index = LOG_LEVELS.indexOf(level);
-
- // Default to 'warn' level if unspecified
- if (this.index < 0) {
- this.index = 1;
- }
- }
-
- /**
- * Configure logging to suppress log output if it is
- * not of an appropriate level. Both the Angular app
- * being initialized and a reference to `$log` should be
- * passed; the former is used to configure application
- * logging, while the latter is needed to apply the
- * same configuration during framework initialization
- * (since the framework also logs.)
- *
- * @param app the Angular app to configure
- * @param $log Angular's $log (also configured)
- * @memberof platform/framework.LogLevel#
- */
- LogLevel.prototype.configure = function (app, $log) {
- var index = this.index;
-
- // Replace logging methods with no-ops, if they are
- // not of an appropriate level.
- function decorate(log) {
- LOG_LEVELS.forEach(function (m, i) {
- // Determine applicability based on index
- // (since levels are in descending order)
- if (i > index) {
- log[m] = NOOP;
- }
- });
- }
-
- decorate($log);
- app.decorator('$log', ['$delegate', function ($delegate) {
- decorate($delegate);
-
- return $delegate;
- }]);
- };
-
- return LogLevel;
- }
-);
diff --git a/platform/framework/src/Main.js b/platform/framework/src/Main.js
deleted file mode 100644
index 750c33850..000000000
--- a/platform/framework/src/Main.js
+++ /dev/null
@@ -1,60 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Implements the framework layer, which handles the loading of bundles
- * and the wiring-together of the extensions they expose.
- * @namespace platform/framework
- */
-define(
- [
- './FrameworkLayer',
- 'angular',
- 'angular-route'
- ],
- function (
- FrameworkLayer,
- angular
- ) {
-
- function Main() {
- }
-
- Main.prototype.run = function (openmct) {
- // Get a reference to Angular's injector, so we can get $http and $log
- // services, which are useful to the framework layer.
- var injector = angular.injector(['ng']);
-
- // Look up log level from query string
- function logLevel() {
- var match = /[?&]log=([a-z]+)/.exec(window.location.search);
-
- return match ? match[1] : "";
- }
-
- return injector.instantiate(['$http', '$log', FrameworkLayer])
- .initializeApplication(angular, openmct, logLevel());
- };
-
- return Main;
- }
-);
diff --git a/platform/framework/src/bootstrap/ApplicationBootstrapper.js b/platform/framework/src/bootstrap/ApplicationBootstrapper.js
deleted file mode 100644
index 20e9fb581..000000000
--- a/platform/framework/src/bootstrap/ApplicationBootstrapper.js
+++ /dev/null
@@ -1,70 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Module defining Bootstrapper. Created by vwoeltje on 11/4/14.
- *
- * The bootstrapper is responsible
- */
-define(
- [],
- function () {
-
- /**
- * The application bootstrapper is responsible for issuing the
- * bootstrap call to Angular. This would normally not be needed
- * with an appropriately-placed ng-app directive, but the
- * framework needs to wait until all extensions have been loaded
- * and registered.
- *
- * @memberof platform/framework
- * @constructor
- */
- function ApplicationBootstrapper(angular, document, $log) {
- this.angular = angular;
- this.document = document;
- this.$log = $log;
- }
-
- /**
- * Bootstrap the application.
- *
- * @param {angular.Module} app the Angular application to
- * bootstrap
- */
- ApplicationBootstrapper.prototype.bootstrap = function (app) {
- var angular = this.angular,
- document = this.document,
- $log = this.$log;
-
- return new Promise(function (resolve, reject) {
- $log.info("Bootstrapping application " + (app || {}).name);
- angular.element(document).ready(function () {
- angular.bootstrap(document, [app.name], { strictDi: true });
- resolve(angular);
- });
- });
- };
-
- return ApplicationBootstrapper;
- }
-);
diff --git a/platform/framework/src/load/Bundle.js b/platform/framework/src/load/Bundle.js
deleted file mode 100644
index cfe5cf18f..000000000
--- a/platform/framework/src/load/Bundle.js
+++ /dev/null
@@ -1,207 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ['../Constants', './Extension'],
- function (Constants, Extension) {
-
- /**
- * A bundle's plain JSON definition.
- *
- * @name BundleDefinition
- * @property {string} name the human-readable name of this bundle
- * @property {string} sources the name of the directory which
- * contains source code used by this bundle
- * @property {string} resources the name of the directory which
- * contains resource files used by this bundle
- * @property {Object.<string,ExtensionDefinition[]>} [extensions={}]
- * all extensions exposed by this bundle
- * @constructor
- * @memberof platform/framework
- */
-
- /**
- * Instantiate a new reference to a bundle, based on its human-readable
- * definition.
- *
- * @param {string} path the path to the directory containing
- * this bundle
- * @param {BundleDefinition} bundleDefinition
- * @returns {{getDefinition: Function}}
- * @constructor
- */
- function Bundle(path, bundleDefinition) {
- // Start with defaults
- var definition = Object.create(Constants.DEFAULT_BUNDLE),
- logName = path;
-
- // Override defaults with specifics from bundle definition
- Object.keys(bundleDefinition).forEach(function (k) {
- definition[k] = bundleDefinition[k];
- });
-
- // Record path to bundle in definition
- definition.path = path;
-
- // Build up the log-friendly name for this bundle
- if (definition.key || definition.name) {
- logName += "(";
- logName += definition.key || "";
- logName += (definition.key && definition.name) ? " " : "";
- logName += definition.name || "";
- logName += ")";
- }
-
- this.path = path;
- this.definition = definition;
- this.logName = logName;
- }
-
- // Utility function for resolving paths in this bundle
- Bundle.prototype.resolvePath = function (elements) {
- var path = this.path;
-
- return [path].concat(elements || []).join(Constants.SEPARATOR);
- };
-
- /**
- * Get the path to this bundle.
- * @returns {string} path to this bundle;
- */
- Bundle.prototype.getPath = function () {
- return this.path;
- };
-
- /**
- * Get the path to this bundle's source folder. If an
- * argument is provided, the path will be to the source
- * file within the bundle's source file.
- *
- * @param {string} [sourceFile] optionally, give a path to
- * a specific source file in the bundle.
- * @returns {string} path to the source folder (or to the
- * source file within it)
- */
- Bundle.prototype.getSourcePath = function (sourceFile) {
- var subpath = sourceFile
- ? [this.definition.sources, sourceFile]
- : [this.definition.sources];
-
- return this.resolvePath(subpath);
- };
-
- /**
- * Get the path to this bundle's resource folder. If an
- * argument is provided, the path will be to the resource
- * file within the bundle's resource file.
- *
- * @param {string} [resourceFile] optionally, give a path to
- * a specific resource file in the bundle.
- * @returns {string} path to the resource folder (or to the
- * resource file within it)
- */
- Bundle.prototype.getResourcePath = function (resourceFile) {
- var subpath = resourceFile
- ? [this.definition.resources, resourceFile]
- : [this.definition.resources];
-
- return this.resolvePath(subpath);
- };
-
- /**
- * Get the path to this bundle's library folder. If an
- * argument is provided, the path will be to the library
- * file within the bundle's resource file.
- *
- * @param {string} [libraryFile] optionally, give a path to
- * a specific library file in the bundle.
- * @returns {string} path to the resource folder (or to the
- * resource file within it)
- */
- Bundle.prototype.getLibraryPath = function (libraryFile) {
- var subpath = libraryFile
- ? [this.definition.libraries, libraryFile]
- : [this.definition.libraries];
-
- return this.resolvePath(subpath);
- };
-
- /**
- * Get library configuration for this bundle. This is read
- * from the bundle's definition; if the bundle is well-formed,
- * it will resemble a require.config object.
- * @returns {object} library configuration
- */
- Bundle.prototype.getConfiguration = function () {
- return this.definition.configuration || {};
- };
-
- /**
- * Get a log-friendly name for this bundle; this will
- * include both the key (machine-readable name for this
- * bundle) and the name (human-readable name for this
- * bundle.)
- * @returns {string} log-friendly name for this bundle
- */
- Bundle.prototype.getLogName = function () {
- return this.logName;
- };
-
- /**
- * Get all extensions exposed by this bundle of a given
- * category.
- *
- * @param {string} category name of the extension category
- * @returns {Array} extension definitions of that category
- */
- Bundle.prototype.getExtensions = function (category) {
- var extensions = this.definition.extensions[category] || [],
- self = this;
-
- return extensions.map(function objectify(extDefinition) {
- return new Extension(self, category, extDefinition);
- });
- };
-
- /**
- * Get a list of all extension categories exposed by this bundle.
- *
- * @returns {string[]} the extension categories
- */
- Bundle.prototype.getExtensionCategories = function () {
- return Object.keys(this.definition.extensions);
- };
-
- /**
- * Get the plain definition of this bundle, as read from
- * its JSON declaration.
- *
- * @returns {platform/framework.BundleDefinition} the raw
- * definition of this bundle
- */
- Bundle.prototype.getDefinition = function () {
- return this.definition;
- };
-
- return Bundle;
- }
-);
diff --git a/platform/framework/src/load/BundleLoader.js b/platform/framework/src/load/BundleLoader.js
deleted file mode 100644
index 7d6865aa0..000000000
--- a/platform/framework/src/load/BundleLoader.js
+++ /dev/null
@@ -1,160 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Module defining BundleLoader.js. Created by vwoeltje on 10/31/14.
- */
-define(
- ['../Constants', './Bundle'],
- function (Constants, Bundle) {
-
- var INVALID_ARGUMENT_MESSAGE = "Malformed loadBundles argument; "
- + "expected string or array",
- BAD_CONTENTS_PREFIX = "Invalid bundle contents for ",
- LOAD_ERROR_PREFIX = "Failed to load bundle ";
-
- /**
- * Loads bundle definitions and wraps them in interfaces which are
- * useful to the framework. This provides the base information which
- * will be used by later phases of framework layer initialization.
- *
- * @memberof platform/framework
- * @constructor
- * @param $http Angular's HTTP requester
- * @param $log Angular's logging service
- */
- function BundleLoader($http, $log, legacyRegistry) {
- this.$http = $http;
- this.$log = $log;
- this.legacyRegistry = legacyRegistry;
- }
-
- /**
- * Load a group of bundles, to be used to constitute the
- * application by later framework initialization phases.
- *
- * @param {string|string[]} an array of bundle names to load, or
- * the name of a JSON file containing that array
- * @returns {Promise.<Bundle[]>} a promise for the loaded bundles
- */
- BundleLoader.prototype.loadBundles = function (bundles) {
- var $http = this.$http,
- $log = this.$log,
- legacyRegistry = this.legacyRegistry;
-
- // Utility function; load contents of JSON file using $http
- function getJSON(file) {
- return $http.get(file).then(function (response) {
- return response.data;
- });
- }
-
- // Remove bundles which failed to load properly.
- // These should have been logged when loaded by
- // loadBundleDefinition, so at this point they are safe
- // to discard.
- function filterBundles(array) {
- return array.filter(function (x) {
- return x !== undefined;
- });
- }
-
- // Load a definition for a bundle
- function loadBundleDefinition(bundlePath) {
- return getJSON(bundlePath + "/" + Constants.BUNDLE_FILE).then(
- function (x) {
- if (x === null || typeof x !== 'object') {
- $log.warn(BAD_CONTENTS_PREFIX + bundlePath);
-
- return undefined;
- }
-
- return x;
- },
- function () {
- $log.warn(LOAD_ERROR_PREFIX + bundlePath);
-
- return undefined;
- }
- );
- }
-
- // Load an individual bundle, as a Bundle object.
- // Returns undefined if the definition could not be loaded.
- function loadBundle(bundlePath) {
- if (legacyRegistry.contains(bundlePath)) {
- return Promise.resolve(new Bundle(
- bundlePath,
- legacyRegistry.get(bundlePath)
- ));
- }
-
- return loadBundleDefinition(bundlePath).then(function (definition) {
- return definition && (new Bundle(bundlePath, definition));
- });
- }
-
- // Used to filter out redundant bundles
- function unique(element, index, array) {
- return array.indexOf(element) === index;
- }
-
- // Load all named bundles from the array, returned as an array
- // of Bundle objects.
- function loadBundlesFromArray(bundleArray) {
- var bundlePromises = legacyRegistry.list()
- .concat(bundleArray)
- .filter(unique)
- .map(loadBundle);
-
- return Promise.all(bundlePromises)
- .then(filterBundles);
- }
-
- // Load all bundles named in the referenced file. The file is
- // presumed to be a JSON file
- function loadBundlesFromFile(listFile) {
- function handleError(err) {
- $log.info([
- "No external bundles loaded;",
- "could not load bundle listing in",
- listFile,
- "due to error",
- err.status,
- err.statusText
- ].join(' '));
-
- return loadBundlesFromArray([]);
- }
-
- return getJSON(listFile)
- .then(loadBundlesFromArray, handleError);
- }
-
- return Array.isArray(bundles) ? loadBundlesFromArray(bundles)
- : (typeof bundles === 'string') ? loadBundlesFromFile(bundles)
- : Promise.reject(new Error(INVALID_ARGUMENT_MESSAGE));
- };
-
- return BundleLoader;
- }
-);
diff --git a/platform/framework/src/load/Extension.js b/platform/framework/src/load/Extension.js
deleted file mode 100644
index 53310c15c..000000000
--- a/platform/framework/src/load/Extension.js
+++ /dev/null
@@ -1,190 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- /**
- * An extension's plain JSON definition.
- *
- * @name ExtensionDefinition
- * @property {string} [key] the machine-readable identifier for this
- * extension
- * @property {string} [implementation] the path to the AMD module
- * which implements this extension; this path is relative
- * to the containing bundle's source folder.
- * @property {string[]} [depends=[]] the dependencies needed by this
- * extension; these are strings as shall be passed to
- * Angular's dependency resolution mechanism.
- * @constructor
- * @memberof platform/framework
- */
-
- /**
- * Instantiate a new extension based on its definition. This serves
- * primarily as a wrapper around the extension's definition to expose
- * a useful interface.
- *
- * An extension
- *
- * @param {Bundle} bundle the bundle which exposed this extension
- * @param {string} category the type of extension being exposed
- * @param {ExtensionDefinition} definition the plain definition of
- * this extension
- * @constructor
- */
- function Extension(bundle, category, definition) {
- var logName = category,
- extensionDefinition = {};
-
- // Build up the log-friendly name for this bundle
- if (definition.key || definition.name) {
- logName += "(";
- logName += definition.key || "";
- logName += (definition.key && definition.name) ? " " : "";
- logName += definition.name || "";
- logName += ")";
- }
-
- logName += " from " + bundle.getLogName();
-
- // Copy over definition. This allows us to attach the bundle
- // definition without modifying the original definition object.
- Object.keys(definition).forEach(function (k) {
- extensionDefinition[k] = definition[k];
- });
-
- // Attach bundle metadata
- extensionDefinition.bundle = bundle.getDefinition();
-
- this.logName = logName;
- this.bundle = bundle;
- this.category = category;
- this.definition = definition;
- this.extensionDefinition = extensionDefinition;
- }
-
- /**
- * Get the machine-readable identifier for this extension.
- *
- * @returns {string} the identifier for this extension
- */
- Extension.prototype.getKey = function () {
- return this.definition.key || "undefined";
- };
-
- /**
- * Get the bundle which declared this extension.
- *
- * @returns {Bundle} the declaring bundle
- */
- Extension.prototype.getBundle = function () {
- return this.bundle;
- };
-
- /**
- * Get the category into which this extension falls.
- * (e.g. "directives")
- *
- * @returns {string} the extension category
- */
- Extension.prototype.getCategory = function () {
- return this.category;
- };
-
- /**
- * Check whether or not this extension should have an
- * associated implementation module which may need to
- * be loaded.
- *
- * @returns {boolean} true if an implementation separate
- * from this definition should also be loaded
- */
- Extension.prototype.hasImplementation = function () {
- return this.definition.implementation !== undefined;
- };
-
- /**
- * Get the path to the AMD module which implements this
- * extension. Will return undefined if there is no
- * implementation associated with this extension.
- *
- * @returns {string} path to implementation, or undefined
- */
- Extension.prototype.getImplementationPath = function () {
- return (this.hasImplementation() && !this.hasImplementationValue())
- ? this.bundle.getSourcePath(this.definition.implementation)
- : undefined;
- };
-
- /**
- * Check if an extension has an actual implementation value
- * (and not just a path to an implementation) defined.
- * @returns {function} the constructor for this extension instance
- */
- Extension.prototype.getImplementationValue = function () {
- return typeof this.definition.implementation === 'function'
- ? this.definition.implementation
- : undefined;
- };
-
- /**
- * Check if an extension has an actual implementation value
- * (and not just a path to an implementation) defined.
- * @returns {boolean} true if a value is available
- */
- Extension.prototype.hasImplementationValue = function () {
- return typeof this.definition.implementation === 'function';
- };
-
- /**
- * Get a log-friendly name for this extension; this will
- * include both the key (machine-readable name for this
- * extension) and the name (human-readable name for this
- * extension.)
- *
- * @returns {string} log-friendly name for this extension
- */
- Extension.prototype.getLogName = function () {
- return this.logName;
- };
-
- /**
- * Get the plain definition of the extension.
- *
- * Note that this definition will have an additional "bundle"
- * field which points back to the bundle which defined the
- * extension, as a convenience.
- *
- * @returns {ExtensionDefinition} the plain definition of
- * this extension, as read from the bundle
- * declaration.
- */
- Extension.prototype.getDefinition = function () {
- return this.extensionDefinition;
- };
-
- return Extension;
-
- }
-);
diff --git a/platform/framework/src/register/CustomRegistrars.js b/platform/framework/src/register/CustomRegistrars.js
deleted file mode 100644
index 4dcfc2af4..000000000
--- a/platform/framework/src/register/CustomRegistrars.js
+++ /dev/null
@@ -1,248 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-/* eslint-disable no-invalid-this */
-
-/**
- * Module defining CustomRegistrars. Created by vwoeltje on 11/3/14.
- */
-define(
- ['../Constants', './ServiceCompositor'],
- function (Constants, ServiceCompositor) {
- /*jshint validthis:true */
-
- /**
- * Handles registration of a few specific extension types that are
- * understood natively by Angular. This includes services and
- * directives.
- * @memberof platform/framework
- * @constructor
- */
- function CustomRegistrars(app, $log) {
- this.app = app;
- this.$log = $log;
- this.registered = {}; // Track registered keys by extension
- }
-
- // Utility; bind a function to a "this" pointer
- function bind(fn, thisArg) {
- return function () {
- return fn.apply(thisArg, arguments);
- };
- }
-
- // Used to create custom registration functions which map to
- // named methods on Angular modules, which follow the normal
- // app.method(key, [ deps..., function ]) pattern.
- function customRegistrar(angularFunction) {
- return function (extension, index) {
- var app = this.app,
- $log = this.$log,
- key = extension.key,
- dependencies = extension.depends || [],
- registered = this.registered[angularFunction] || {};
-
- this.registered[angularFunction] = registered;
-
- if (!key) {
- $log.warn([
- "Cannot register ",
- angularFunction,
- " ",
- index,
- ", no key specified. ",
- JSON.stringify(extension)
- ].join(""));
- } else if (registered[key]) {
- $log.debug([
- "Already registered ",
- angularFunction,
- " with key ",
- key,
- "; skipping."
- ].join(""));
- } else {
- $log.info([
- "Registering ",
- angularFunction,
- ": ",
- key
- ].join(""));
- registered[key] = true;
- app[angularFunction](
- key,
- dependencies.concat([extension])
- );
- }
- };
- }
-
- function registerConstant(extension) {
- var app = this.app,
- $log = this.$log,
- key = extension.key,
- value = extension.value;
-
- if (typeof key === "string" && value !== undefined) {
- $log.info([
- "Registering constant: ",
- key,
- " with value ",
- value
- ].join(""));
- app.constant(key, value);
- } else {
- $log.warn([
- "Cannot register constant ",
- key,
- " with value ",
- value
- ].join(""));
- }
-
- }
-
- // Custom registration function for extensions of category "runs"
- function registerRun(extension) {
- var app = this.app,
- $log = this.$log;
-
- if (typeof extension === 'function') {
- // Prepend dependencies, and schedule to run
- app.run((extension.depends || []).concat([extension]));
- } else {
- // If it's not a function, no implementation was given
- $log.warn([
- "Cannot register run extension from ",
- (extension.bundle || {}).path,
- "; no implementation."
- ].join(""));
- }
- }
-
- // Custom registration function for extensions of category "route"
- function registerRoute(extension) {
- var app = this.app,
- $log = this.$log,
- route = Object.create(extension);
-
- // Adjust path for bundle
- if (route.templateUrl) {
- route.templateUrl = [
- route.bundle.path,
- route.bundle.resources,
- route.templateUrl
- ].join(Constants.SEPARATOR);
- }
-
- // Log the registration
- $log.info("Registering route: " + (route.key || route.when));
-
- // Register the route with Angular
- app.config(['$routeProvider', function ($routeProvider) {
- if (route.when) {
- $routeProvider.when(route.when, route);
- } else {
- $routeProvider.otherwise(route);
- }
- }]);
- }
-
- // Handle service compositing
- function registerComponents(components) {
- var app = this.app,
- $log = this.$log;
-
- return new ServiceCompositor(app, $log)
- .registerCompositeServices(components);
- }
-
- // Utility; create a function which converts another function
- // (which acts on single objects) to one which acts upon arrays.
- function mapUpon(func) {
- return function (array) {
- return array.map(bind(func, this));
- };
- }
-
- // More like key-value pairs than methods; key is the
- // name of the extension category to be handled, and the value
- // is the function which handles it.
-
- /**
- * Register constant values.
- * @param {Array} extensions the resolved extensions
- */
- CustomRegistrars.prototype.constants =
- mapUpon(registerConstant);
-
- /**
- * Register Angular routes.
- * @param {Array} extensions the resolved extensions
- */
- CustomRegistrars.prototype.routes =
- mapUpon(registerRoute);
-
- /**
- * Register Angular directives.
- * @param {Array} extensions the resolved extensions
- */
- CustomRegistrars.prototype.directives =
- mapUpon(customRegistrar("directive"));
-
- /**
- * Register Angular controllers.
- * @param {Array} extensions the resolved extensions
- */
- CustomRegistrars.prototype.controllers =
- mapUpon(customRegistrar("controller"));
-
- /**
- * Register Angular services.
- * @param {Array} extensions the resolved extensions
- */
- CustomRegistrars.prototype.services =
- mapUpon(customRegistrar("service"));
-
- /**
- * Register Angular filters.
- * @param {Array} extensions the resolved extensions
- */
- CustomRegistrars.prototype.filters =
- mapUpon(customRegistrar("filter"));
-
- /**
- * Register functions which will run after bootstrapping.
- * @param {Array} extensions the resolved extensions
- */
- CustomRegistrars.prototype.runs =
- mapUpon(registerRun);
-
- /**
- * Register components of composite services.
- * @param {Array} extensions the resolved extensions
- */
- CustomRegistrars.prototype.components =
- registerComponents;
-
- return CustomRegistrars;
- }
-);
diff --git a/platform/framework/src/register/ExtensionRegistrar.js b/platform/framework/src/register/ExtensionRegistrar.js
deleted file mode 100644
index 04b14952f..000000000
--- a/platform/framework/src/register/ExtensionRegistrar.js
+++ /dev/null
@@ -1,232 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Module defining ExtensionRegistrar. Created by vwoeltje on 11/3/14.
- */
-define(
- ['../Constants', './PartialConstructor'],
- function (Constants, PartialConstructor) {
-
- /**
- * Responsible for registering extensions with Angular.
- *
- * @memberof platform/framework
- * @constructor
- * @param {angular.Module} the Angular application with which
- * extensions should be registered
- * @param {Object.<string,function>} customRegistrars an object
- * containing custom registration functions, primarily for
- * Angular built-ins.
- * @param {ExtensionSorter} sorter the sorter which will impose
- * priority ordering upon extensions
- * @param {*} $log Angular's logging service
- */
- function ExtensionRegistrar(app, customRegistrars, sorter, $log) {
- // Track which extension categories have already been registered.
- // Exceptions will be thrown if the same extension category is
- // registered twice.
- this.registeredCategories = {};
- this.customRegistrars = customRegistrars || {};
- this.app = app;
- this.sorter = sorter;
- this.$log = $log;
- }
-
- /**
- * Register a group of resolved extensions with the Angular
- * module managed by this registrar.
- *
- * For convenient chaining (particularly from the framework
- * initializer's perspective), this returns the Angular
- * module with which extensions were registered.
- *
- * @param {Object.<string, object[]>} extensionGroup an object
- * containing key-value pairs, where keys are extension
- * categories and values are arrays of resolved
- * extensions
- * @returns {angular.Module} the application module with
- * which extensions were registered
- */
- ExtensionRegistrar.prototype.registerExtensions = function (extensionGroup) {
- var registeredCategories = this.registeredCategories,
- customRegistrars = this.customRegistrars,
- app = this.app,
- sorter = this.sorter,
- $log = this.$log;
-
- // Used to build unique identifiers for individual extensions,
- // so that these can be registered separately with Angular
- function identify(category, extension, index) {
- var name = extension.key
- ? ("extension-" + extension.key + "#" + index)
- : ("extension#" + index);
-
- return category + "[" + name + "]";
- }
-
- // Echo arguments; used to represent groups of non-built-in
- // extensions as a single dependency.
- function echo() {
- return Array.prototype.slice.call(arguments);
- }
-
- // Always return a static value; used to represent plain
- // metadata as a single dependency in Angular.
- function staticFunction(value) {
- return function () {
- return value;
- };
- }
-
- // Utility function; create the second argument for Angular's
- // .service service registration method (an array containing
- // both dependencies and a factory method for the service.)
- function makeServiceArgument(category, extension) {
- var dependencies = extension.depends || [],
- factory = (typeof extension === 'function')
- ? new PartialConstructor(extension)
- : staticFunction(extension);
-
- return dependencies.concat([factory]);
- }
-
- // Register extension arrays with Angular under an appropriately
- // suffixed name, e.g. "types[]"
- function registerExtensionArraysForCategory(category, names) {
- var name = category + Constants.EXTENSION_SUFFIX;
- app.factory(name, names.concat([echo]));
- }
-
- function registerExtensionsForCategory(category, extensions) {
- var names = [];
-
- function registerExtension(extension, index) {
- var name = identify(category, extension, index);
-
- // Track individual extension names as-registered
- names.push(name);
-
- app.factory(
- name,
- makeServiceArgument(category, extension)
- );
- }
-
- if (registeredCategories[category]) {
- $log.warn([
- "Tried to register extensions for category ",
- category,
- " more than once. Ignoring all but first set."
- ].join(""));
- } else {
- // Register all extensions. Use custom registration
- // code for services, directives, etc; otherwise,
- // just register them under generic names.
- if (customRegistrars[category]) {
- customRegistrars[category](extensions);
- } else {
- extensions.forEach(registerExtension);
- registerExtensionArraysForCategory(category, names);
- }
-
- registeredCategories[category] = true;
-
- return true;
- }
- }
-
- // Check if a declared dependency looks like a dependency on
- // an extension category (e.g. is suffixed by [])
- function isExtensionDependency(dependency) {
- var index = dependency.indexOf(
- Constants.EXTENSION_SUFFIX,
- dependency.length - Constants.EXTENSION_SUFFIX.length
- );
-
- return index !== -1;
- }
-
- // Examine a group of resolved dependencies to determine
- // which extension categories still need to be satisfied.
- function findEmptyExtensionDependencies(extGroup) {
- var needed = {},
- categories = Object.keys(extGroup),
- allExtensions = [];
-
- // Build up an array of all extensions
- categories.forEach(function (category) {
- allExtensions =
- allExtensions.concat(extGroup[category]);
- });
-
- // Track all extension dependencies exposed therefrom
- allExtensions.forEach(function (extension) {
- (extension.depends || []).filter(
- isExtensionDependency
- ).forEach(function (dependency) {
- needed[dependency] = true;
- });
- });
-
- // Remove categories which have been provided
- categories.forEach(function (category) {
- var dependency = category + Constants.EXTENSION_SUFFIX;
- delete needed[dependency];
- });
-
- return Object.keys(needed);
- }
-
- // Register any extension categories that are depended-upon but
- // have not been declared anywhere; such dependencies are then
- // satisfied by an empty array, instead of not at all.
- function registerEmptyDependencies(extGroup) {
- findEmptyExtensionDependencies(extGroup)
- .forEach(function (name) {
- $log.info("Registering empty extension category " + name);
- app.factory(name, [staticFunction([])]);
- });
- }
-
- // Announce we're entering a new phase
- $log.info("Registering extensions...");
-
- // Register all declared extensions by category
- Object.keys(extensionGroup).forEach(function (category) {
- registerExtensionsForCategory(
- category,
- sorter.sort(extensionGroup[category])
- );
- });
-
- // Also handle categories which are needed but not declared
- registerEmptyDependencies(extensionGroup);
-
- // Return the application to which these extensions
- // have been registered
- return app;
- };
-
- return ExtensionRegistrar;
- }
-);
diff --git a/platform/framework/src/register/ExtensionSorter.js b/platform/framework/src/register/ExtensionSorter.js
deleted file mode 100644
index a6ddad6be..000000000
--- a/platform/framework/src/register/ExtensionSorter.js
+++ /dev/null
@@ -1,118 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../Constants"],
- function (Constants) {
-
- /**
- * Responsible for applying priority order to extensions in a
- * given category. This will sort in reverse order of the numeric
- * priority given for extensions in the `priority` priority (such
- * that large values are registered first.) Extensions may also
- * specify symbolic properties as strings (instead of numbers),
- * which will be looked up from the table `Constants.PRIORITY_LEVELS`.
- * @param $log Angular's logging service
- * @memberof platform/framework
- * @constructor
- */
- function ExtensionSorter($log) {
- this.$log = $log;
- }
-
- /**
- * Sort extensions according to priority.
- *
- * @param {object[]} extensions array of resolved extensions
- * @returns {object[]} the same extensions, in priority order
- */
- ExtensionSorter.prototype.sort = function (extensions) {
- var $log = this.$log;
-
- // Handle unknown or malformed priorities specified by extensions
- function unrecognizedPriority(extension) {
- // Issue a warning
- $log.warn([
- "Unrecognized priority '",
- (extension || {}).priority,
- "' specified for extension from ",
- ((extension || {}).bundle || {}).path,
- "; defaulting to ",
- Constants.DEFAULT_PRIORITY
- ].join(''));
-
- // Provide a return value (default priority) to make this
- // useful in an expression.
- return Constants.DEFAULT_PRIORITY;
- }
-
- function getPriority(extension) {
- var priority =
- (extension || {}).priority || Constants.DEFAULT_PRIORITY;
-
- // If it's a symbolic priority, look it up
- if (typeof priority === 'string') {
- priority = Constants.PRIORITY_LEVELS[priority];
- }
-
- // Should be a number; otherwise, issue a warning and
- // fall back to default priority level.
- return (typeof priority === 'number')
- ? priority : unrecognizedPriority(extension);
- }
-
- // Attach a numeric priority to an extension; this is done in
- // one pass outside of the comparator, mainly because getPriority
- // may log warnings, and we only want this to happen once
- // (instead of the many times that might occur during a sort.)
- function prioritize(extension, index) {
- return {
- // The extension itself, for later unwrapping
- extension: extension,
- // The index, to provide a stable sort (see compare)
- index: index,
- // The numeric priority of the extension
- priority: getPriority(extension)
- };
- }
-
- // Unwrap the original extension
- // (for use after ordering has been applied)
- function deprioritize(prioritized) {
- return prioritized.extension;
- }
-
- // Compare two prioritized extensions
- function compare(a, b) {
- // Reverse order by numeric priority; or, original order.
- return (b.priority - a.priority) || (a.index - b.index);
- }
-
- return (extensions || [])
- .map(prioritize)
- .sort(compare)
- .map(deprioritize);
- };
-
- return ExtensionSorter;
- }
-);
diff --git a/platform/framework/src/register/PartialConstructor.js b/platform/framework/src/register/PartialConstructor.js
deleted file mode 100644
index e6fca5af8..000000000
--- a/platform/framework/src/register/PartialConstructor.js
+++ /dev/null
@@ -1,78 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Module defining PartialConstructor. Created by vwoeltje on 11/3/14.
- */
-define(
- [],
- function () {
-
- /**
- * A partial constructor is used to instantiate objects in two
- * stages:
- *
- * * First, dependencies injected from Angular
- * * Second, arguments passed at run-time
- *
- * This allows extensions to accept both their Angular-injected
- * dependencies and their per-instance attributes all in one
- * constructor invocation. User code for these extensions then
- * does not see the Angular dependency arguments; they may
- * instantiate instances of these extensions by passing only
- * those per-instance arguments.
- *
- * @memberof platform/framework
- * @constructor
- */
- function PartialConstructor(Constructor) {
-
- function OuterConstructor() { // Bind services
- var dependencies = Array.prototype.slice.call(arguments);
-
- function InnerConstructor() { // Bind everything else
- var other = Array.prototype.slice.call(arguments),
- instance = Object.create(Constructor.prototype);
-
- // Mimic "new" behavior with apply.
- instance = Constructor.apply(
- instance,
- dependencies.concat(other)
- ) || instance;
-
- return instance;
- }
-
- // Copy properties from original constructor
- Object.keys(Constructor).forEach(function (k) {
- InnerConstructor[k] = Constructor[k];
- });
-
- return InnerConstructor;
- }
-
- return OuterConstructor;
- }
-
- return PartialConstructor;
- }
-);
diff --git a/platform/framework/src/register/ServiceCompositor.js b/platform/framework/src/register/ServiceCompositor.js
deleted file mode 100644
index c634a8567..000000000
--- a/platform/framework/src/register/ServiceCompositor.js
+++ /dev/null
@@ -1,257 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Module defining ServiceCompositor. Created by vwoeltje on 11/5/14.
- */
-define(
- [],
- function () {
-
- /**
- * Handles service compositing; that is, building up services
- * from provider, aggregator, and decorator components.
- *
- * @memberof platform/framework
- * @constructor
- */
- function ServiceCompositor(app, $log) {
- this.latest = {};
- this.providerLists = {}; // Track latest services registered
- this.app = app;
- this.$log = $log;
- }
-
- /**
- * Register composite services with Angular. This will build
- * up a dependency hierarchy between providers, aggregators,
- * and/or decorators, such that a dependency upon the service
- * type they expose shall be satisfied by their fully-wired
- * whole.
- *
- * Note that this method assumes that a complete set of
- * components shall be provided. Multiple calls to this
- * method may not behave as expected.
- *
- * @param {Array} components extensions of category component
- */
- ServiceCompositor.prototype.registerCompositeServices = function (components) {
- var latest = this.latest,
- providerLists = this.providerLists,
- app = this.app,
- $log = this.$log;
-
- // Log a warning; defaults to "no service provided by"
- function warn(extension, category, message) {
- var msg = message || "No service provided by";
- $log.warn([
- msg,
- " ",
- category,
- " ",
- extension.key,
- " from bundle ",
- (extension.bundle || { path: "unknown bundle" }).path,
- "; skipping."
- ].join(""));
- }
-
- //Log an info: defaults to "no service provide by"
- function info(extension, category, message) {
- var msg = message || "No service provided by";
- $log.info([
- msg,
- " ",
- category,
- " ",
- extension.key,
- " from bundle ",
- (extension.bundle || { path: "unknown bundle" }).path,
- "; skipping."
- ].join(""));
- }
-
- // Echo arguments; used to represent groups of non-built-in
- // extensions as a single dependency.
- function echoMany() {
- return Array.prototype.slice.call(arguments);
- }
-
- // Echo arguments; used to represent groups of non-built-in
- // extensions as a single dependency.
- function echoSingle(value) {
- return value;
- }
-
- // Generates utility functions to match types (one of
- // provider, aggregator, or decorator) of component. Used
- // to filter down to specific types, which are handled
- // in order.
- function hasType(type) {
- return function (extension) {
- return extension.type === type;
- };
- }
-
- // Make a unique name for a service component.
- function makeName(category, service, index) {
- return [
- service,
- "[",
- category,
- "#",
- index,
- "]"
- ].join("");
- }
-
- // Register a specific provider instance with Angular, and
- // record its name for subsequent stages.
- function registerProvider(provider, index) {
- var service = provider.provides,
- dependencies = provider.depends || [],
- name = makeName("provider", service, index);
-
- if (!service) {
- return warn(provider, "provider");
- }
-
- providerLists[service] = providerLists[service] || [];
- providerLists[service].push(name);
-
- // This provider is the latest candidate for resolving
- // the composite service.
- latest[service] = name;
-
- app.service(name, dependencies.concat([provider]));
-
- $log.info("Registering provider for " + service);
- }
-
- // Register an array of providers as a single dependency;
- // aggregators will then depend upon this to consume all
- // aggregated providers as a single dependency.
- function registerProviderSets() {
- Object.keys(providerLists).forEach(function (service) {
- var name = makeName("provider", service, "*"),
- list = providerLists[service];
-
- $log.info([
- "Compositing",
- list.length,
- "providers for",
- service
- ].join(" "));
-
- app.service(name, list.concat([echoMany]));
- });
- }
-
- // Registers an aggregator via Angular, including both
- // its declared dependencies and the additional, implicit
- // dependency upon the array of all providers.
- function registerAggregator(aggregator, index) {
- var service = aggregator.provides,
- dependencies = aggregator.depends || [],
- providerSetName = makeName("provider", service, "*"),
- name = makeName("aggregator", service, index);
-
- if (!service) {
- return info(aggregator, "aggregator");
- }
-
- // Aggregators need other services to aggregate, otherwise they
- // do nothing.
- if (!latest[service]) {
- return info(
- aggregator,
- "aggregator",
- "No services to aggregate for"
- );
- }
-
- dependencies = dependencies.concat([providerSetName]);
- latest[service] = name;
-
- app.service(name, dependencies.concat([aggregator]));
- }
-
- // Registers a decorator via Angular, including its implicit
- // dependency on the latest service component which has come
- // before it.
- function registerDecorator(decorator, index) {
- var service = decorator.provides,
- dependencies = decorator.depends || [],
- name = makeName("decorator", service, index);
-
- if (!service) {
- return warn(decorator, "decorator");
- }
-
- // Decorators need other services to decorate, otherwise they
- // do nothing.
- if (!latest[service]) {
- return warn(
- decorator,
- "decorator",
- "No services to decorate for"
- );
- }
-
- dependencies = dependencies.concat([latest[service]]);
- latest[service] = name;
-
- app.service(name, dependencies.concat([decorator]));
- }
-
- // Alias the latest services of various types back to the
- // more general service declaration.
- function registerLatest() {
- Object.keys(latest).forEach(function (service) {
- app.service(service, [latest[service], echoSingle]);
- });
- }
-
- // Register composite services in phases:
- // * Register providers
- // * Register aggregators (which use providers)
- // * Register decorators (which use anything)
- // Then, register the latest candidate as a plain service.
- function registerComposites(providers, aggregators, decorators) {
- providers.forEach(registerProvider);
- registerProviderSets();
- aggregators.forEach(registerAggregator);
- decorators.forEach(registerDecorator);
- registerLatest();
- }
-
- // Initial point of entry; split into three component types.
- registerComposites(
- components.filter(hasType("provider")),
- components.filter(hasType("aggregator")),
- components.filter(hasType("decorator"))
- );
- };
-
- return ServiceCompositor;
- }
-);
diff --git a/platform/framework/src/resolve/BundleResolver.js b/platform/framework/src/resolve/BundleResolver.js
deleted file mode 100644
index f04297baa..000000000
--- a/platform/framework/src/resolve/BundleResolver.js
+++ /dev/null
@@ -1,125 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Module defining BundleResolver. Created by vwoeltje on 11/4/14.
- */
-define(
- [],
- function () {
-
- /**
- * Responsible for the extension resolution phase of framework
- * initialization. During this phase, any scripts implementing
- * extensions provided by bundles are loaded.
- *
- * @memberof platform/framework
- * @constructor
- */
- function BundleResolver(extensionResolver, $log) {
- this.extensionResolver = extensionResolver;
- this.$log = $log;
- }
-
- /**
- * Resolve all extensions exposed by these bundles.
- *
- * @param {Bundle[]} bundles the bundles to resolve
- * @returns {Promise.<Object.<string, object[]>>} an promise
- * for an object containing
- * key-value pairs, where keys are extension
- * categories and values are arrays of resolved
- * extensions belonging to those categories
- */
- BundleResolver.prototype.resolveBundles = function (bundles) {
- var extensionResolver = this.extensionResolver,
- $log = this.$log;
-
- /*
- * Merge resolved bundles (where each is expressed as an
- * object containing key-value pairs, where keys are extension
- * categories and values are arrays of resolved extensions)
- * into one large object containing resolved extensions from
- * all bundles (in the same form.)
- *
- * @param {Object.<string, object[]>|Array} resolvedBundles
- * @returns {Object.<string, object[]>}
- * @memberof platform/framework.BundleResolver#
- */
- function mergeResolvedBundles(resolvedBundles) {
- var result = {};
-
- resolvedBundles.forEach(function (resolved) {
- Object.keys(resolved).forEach(function (k) {
- result[k] = (result[k] || []).concat(resolved[k]);
- });
- });
-
- return result;
- }
-
- // Resolve a bundle; resolve all extensions, and return
- // the resolved extensions in an object in the format described
- // for mergeResolvedBundles above
- function resolveBundle(bundle) {
- var categories = bundle.getExtensionCategories(),
- result = {};
-
- function resolveExtension(extension) {
- var category = extension.getCategory();
-
- function push(resolved) {
- result[category].push(resolved);
- }
-
- return extensionResolver.resolve(extension).then(push);
- }
-
- function resolveCategory(category) {
- result[category] = [];
-
- return Promise.all(
- bundle.getExtensions(category).map(resolveExtension)
- );
- }
-
- function giveResult() {
- return result;
- }
-
- // Log the large-scale task
- $log.info(
- "Resolving extensions for bundle " + bundle.getLogName()
- );
-
- return Promise.all(categories.map(resolveCategory))
- .then(giveResult);
- }
-
- // Then, resolve all extension implementations.
- return Promise.all(bundles.map(resolveBundle))
- .then(mergeResolvedBundles);
- };
-
- return BundleResolver;
- }
-);
diff --git a/platform/framework/src/resolve/ExtensionResolver.js b/platform/framework/src/resolve/ExtensionResolver.js
deleted file mode 100644
index 208f51868..000000000
--- a/platform/framework/src/resolve/ExtensionResolver.js
+++ /dev/null
@@ -1,147 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Module defining ExtensionResolver. Created by vwoeltje on 11/3/14.
- */
-define(
- [],
- function () {
-
- /**
- * An ExtensionResolver is responsible for loading any implementation
- * modules associated with specific extensions.
- *
- * @param {ImplementationLoader} loader used to load implementations
- * @param {*} $log Angular's logging service
- * @memberof platform/framework
- * @constructor
- */
- function ExtensionResolver(loader, $log) {
- this.loader = loader;
- this.$log = $log;
- }
-
- /**
- * Resolve the provided extension; this will give a promise
- * for the extension's implementation, if one has been
- * specified, or for the plain definition of the extension
- * otherwise. The plain definition will also be given
- * if the implementation fails to load for some reason.
- *
- * All key-value pairs from the extension definition
- * will additionally be attached to any loaded implementation.
- *
- * @param {Extension} extension the extension to resolve
- * @returns {Promise} a promise for the resolved extension
- */
- ExtensionResolver.prototype.resolve = function (extension) {
- var loader = this.loader,
- $log = this.$log;
-
- function loadImplementation(ext) {
- var implPromise = ext.hasImplementationValue()
- ? Promise.resolve(ext.getImplementationValue())
- : loader.load(ext.getImplementationPath()),
- definition = ext.getDefinition();
-
- // Wrap a constructor function (to avoid modifying the original)
- function constructorFor(impl) {
- function Constructor() {
- return impl.apply(this, arguments);
- }
-
- Constructor.prototype = impl.prototype;
-
- return Constructor;
- }
-
- // Attach values from the object definition to the
- // loaded implementation.
- function attachDefinition(impl) {
- var result = (typeof impl === 'function')
- ? constructorFor(impl)
- : Object.create(impl);
-
- // Copy over static properties
- Object.keys(impl).forEach(function (k) {
- result[k] = impl[k];
- });
-
- // Copy over definition
- Object.keys(definition).forEach(function (k) {
- if (result[k] === undefined) {
- result[k] = definition[k];
- }
- });
- result.definition = definition;
-
- // Log that this load was successful
- $log.info("Resolved " + ext.getLogName());
-
- return result;
- }
-
- // Log any errors in loading the implementation, and
- // return the plain extension definition instead.
- function handleError(err) {
- // Build up a log message from parts
- var message = [
- "Could not load implementation for extension ",
- ext.getLogName(),
- " due to ",
- err.message
- ].join("");
-
- // Log that the extension was not loaded
- $log.warn(message);
-
- return ext.getDefinition();
- }
-
- if (!ext.hasImplementationValue()) {
- // Log that loading has begun
- $log.info([
- "Loading implementation ",
- ext.getImplementationPath(),
- " for extension ",
- ext.getLogName()
- ].join(""));
- }
-
- return implPromise.then(attachDefinition, handleError);
- }
-
- // Log that loading has begun
- $log.info([
- "Resolving extension ",
- extension.getLogName()
- ].join(""));
-
- return extension.hasImplementation()
- ? loadImplementation(extension)
- : Promise.resolve(extension.getDefinition());
- };
-
- return ExtensionResolver;
- }
-);
diff --git a/platform/framework/src/resolve/ImplementationLoader.js b/platform/framework/src/resolve/ImplementationLoader.js
deleted file mode 100644
index a4362634c..000000000
--- a/platform/framework/src/resolve/ImplementationLoader.js
+++ /dev/null
@@ -1,64 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Module defining ImplementationLoader. Created by vwoeltje on 11/3/14.
- */
-define(
- [],
- function () {
-
- /**
- * Responsible for loading extension implementations
- * (AMD modules.) Acts as a wrapper around RequireJS to
- * provide a promise-like API.
- * @memberof platform/framework
- * @constructor
- * @param {*} require RequireJS, or an object with similar API
- * @param {*} $log Angular's logging service
- */
- function ImplementationLoader(require) {
- this.require = require;
- }
-
- /**
- * Load an extension's implementation; or, equivalently,
- * load an AMD module. This is fundamentally similar
- * to a call to RequireJS, except that the result is
- * wrapped in a promise. The promise will be fulfilled
- * with the loaded module, or rejected with the error
- * reported by Require.
- *
- * @param {string} path the path to the module to load
- * @returns {Promise} a promise for the specified module.
- */
- ImplementationLoader.prototype.load = function loadModule(path) {
- var require = this.require;
-
- return new Promise(function (fulfill, reject) {
- require([path], fulfill, reject);
- });
- };
-
- return ImplementationLoader;
- }
-);
diff --git a/platform/framework/test/FrameworkInitializerSpec.js b/platform/framework/test/FrameworkInitializerSpec.js
deleted file mode 100644
index 3f4b6a06c..000000000
--- a/platform/framework/test/FrameworkInitializerSpec.js
+++ /dev/null
@@ -1,60 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * FrameworkInitializerSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../src/FrameworkInitializer"],
- function (FrameworkInitializer) {
-
- describe("The framework initializer", function () {
- var initializer;
-
- function appender(name) {
- return function (value) {
- return Promise.resolve(value.concat([name]));
- };
- }
-
- beforeEach(function () {
- initializer = new FrameworkInitializer(
- { loadBundles: appender("loader") },
- { resolveBundles: appender("resolver") },
- { registerExtensions: appender("registrar") },
- { bootstrap: appender("bootstrapper")}
- );
- });
-
- // Really just delegates work, can only verify the
- // order of calls.
- it("calls injected stages in order", function () {
- return initializer.runApplication([]).then(function (result) {
- expect(result).toEqual(
- ["loader", "resolver", "registrar", "bootstrapper"]
- );
- });
- });
-
- });
- }
-);
diff --git a/platform/framework/test/LogLevelSpec.js b/platform/framework/test/LogLevelSpec.js
deleted file mode 100644
index ef0086233..000000000
--- a/platform/framework/test/LogLevelSpec.js
+++ /dev/null
@@ -1,98 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ['../src/LogLevel'],
- function (LogLevel) {
-
- var LOG_METHODS = [
- 'error',
- 'warn',
- 'info',
- 'log',
- 'debug'
- ];
-
- describe("The logging level handler", function () {
- var mockLog,
- mockApp,
- mockDelegate,
- mockMethods;
-
- function logAll(v) {
- LOG_METHODS.forEach(function (m) {
- mockLog[m](v);
- mockDelegate[m](v);
- });
- }
-
- function expectCalls(calls, v) {
- LOG_METHODS.forEach(function (m) {
- if (calls.indexOf(m) > -1) {
- expect(mockMethods[m]).toHaveBeenCalledWith(v);
- } else {
- expect(mockMethods[m]).not.toHaveBeenCalledWith(v);
- }
- });
- }
-
- beforeEach(function () {
- mockMethods = jasmine.createSpyObj("levels", LOG_METHODS);
- mockLog = jasmine.createSpyObj('$log', LOG_METHODS);
- mockApp = jasmine.createSpyObj('app', ['config', 'decorator']);
- mockDelegate = jasmine.createSpyObj('$delegate', LOG_METHODS);
-
- LOG_METHODS.forEach(function (m) {
- mockLog[m].and.callFake(mockMethods[m]);
- mockDelegate[m].and.callFake(mockMethods[m]);
- });
-
- mockApp.decorator.and.callFake(function (key, decoration) {
- // We only expect $log to be decorated
- if (key === '$log' && decoration[0] === '$delegate') {
- decoration[1](mockDelegate);
- }
- });
- });
-
- it("defaults to 'warn' level", function () {
- new LogLevel("garbage").configure(mockApp, mockLog);
- logAll("test");
- expectCalls(['error', 'warn'], 'test');
- });
-
- LOG_METHODS.forEach(function (m, i) {
- it("supports log level '" + m + "'", function () {
- // Note: This is sensitive to ordering of LOG_METHODS,
- // which needs to be highest-level-first above.
- var expected = LOG_METHODS.slice(0, i + 1),
- message = "test " + m;
-
- new LogLevel(m).configure(mockApp, mockLog);
- logAll(message);
- expectCalls(expected, message);
- });
- });
-
- });
- }
-);
diff --git a/platform/framework/test/bootstrap/ApplicationBootstrapperSpec.js b/platform/framework/test/bootstrap/ApplicationBootstrapperSpec.js
deleted file mode 100644
index 482d57fab..000000000
--- a/platform/framework/test/bootstrap/ApplicationBootstrapperSpec.js
+++ /dev/null
@@ -1,108 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * ApplicationBootstrapperSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/bootstrap/ApplicationBootstrapper"],
- function (ApplicationBootstrapper) {
-
- describe("The application bootstrapper", function () {
- // Test support variables
- var bootstrapper,
- captured,
- mockAngular,
- mockDocument,
- mockLog,
- mockApp;
-
- // Used to capture arguments to mocks
- function capture() {
- var names = Array.prototype.slice.apply(arguments, []);
-
- return function () {
- var values = arguments;
- names.forEach(function (name, index) {
- captured[name] = values[index];
- });
- };
- }
-
- // Set up the mocks before each run
- beforeEach(function () {
- captured = {};
-
- mockAngular = {
- element: function (selector) {
- captured.selector = selector;
-
- return { ready: capture("callback") };
- },
- bootstrap: capture("element", "appNames")
- };
-
- mockDocument = "I am just a value.";
-
- mockLog = { info: capture("info") };
-
- mockApp = { name: "MockApp" };
-
- bootstrapper = new ApplicationBootstrapper(
- mockAngular,
- mockDocument,
- mockLog
- );
-
- bootstrapper.bootstrap(mockApp);
- });
-
- // The tests.
-
- it("waits for the provided document element to be ready", function () {
- // Should have provided Angular a selector...
- expect(captured.selector).toBe(mockDocument);
- // ...and called ready on the response.
- expect(captured.callback).toBeDefined();
- });
-
- it("issues a bootstrap call once ready", function () {
- // Verify precondition; bootstrap not called
- expect(captured.element).toBeUndefined();
- expect(captured.appNames).toBeUndefined();
-
- // Call the "ready" function
- captured.callback();
-
- // Verify that bootstrap was called
- expect(captured.element).toBe(mockDocument);
- expect(captured.appNames).toEqual([mockApp.name]);
- });
-
- it("logs that the bootstrap phase has been reached", function () {
- expect(captured.info).toBeDefined();
- expect(typeof captured.info).toEqual('string');
- });
-
- });
- }
-);
diff --git a/platform/framework/test/load/BundleLoaderSpec.js b/platform/framework/test/load/BundleLoaderSpec.js
deleted file mode 100644
index a85974b35..000000000
--- a/platform/framework/test/load/BundleLoaderSpec.js
+++ /dev/null
@@ -1,122 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * BundleLoaderSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/load/BundleLoader"],
- function (BundleLoader) {
-
- describe("The bundle loader", function () {
- var loader,
- mockHttp,
- mockLog,
- mockRegistry,
- testBundles;
-
- beforeEach(function () {
- testBundles = {
- "bundles.json": ["bundle/a", "bundle/b"],
- "bundle/a/bundle.json": {"someValue": 6},
- "bundle/b/bundle.json": {"someValue": 7}
- };
-
- mockHttp = jasmine.createSpyObj("$http", ["get"]);
- mockLog = jasmine.createSpyObj("$log", ["error", "warn", "info", "debug"]);
- mockRegistry = jasmine.createSpyObj(
- 'legacyRegistry',
- ['list', 'contains', 'get']
- );
- mockRegistry.list.and.returnValue([]);
- mockRegistry.contains.and.returnValue(false);
- loader = new BundleLoader(mockHttp, mockLog, mockRegistry);
- });
-
- it("accepts a JSON file name and loads all bundles", function () {
- // Set up; return named bundles
- mockHttp.get.and.returnValue(Promise.resolve({ data: [] }));
-
- // Call load bundles
- return loader.loadBundles("test-filename.json").then(function (bundles) {
- // Should have loaded the file via $http
- expect(mockHttp.get).toHaveBeenCalledWith("test-filename.json");
-
- // Should have gotten an empty bundle list
- expect(bundles).toEqual([]);
- });
- });
-
- it("accepts a list of bundle paths", function () {
- // Set up; return named bundles
- mockHttp.get.and.callFake(function (name) {
- return Promise.resolve({data: testBundles[name]});
- });
-
- // Call load bundles
- return loader.loadBundles(["bundle/a", "bundle/b"]).then(function (bundles) {
- // Should have gotten back two bundles
- expect(bundles.length).toEqual(2);
-
- // They should have the values we expect; don't care about order,
- // some map/reduce the summation.
- expect(bundles.map(function (call) {
- return call.getDefinition().someValue;
- }).reduce(function (a, b) {
- return a + b;
- }, 0)).toEqual(13);
- });
- });
-
- it("warns about, then ignores, missing bundle declarations", function () {
- // File-not-found
- mockHttp.get.and.returnValue(Promise.reject(new Error("test error")));
-
- // Try and load
- return loader.loadBundles(["some/bundle"]).then(function (bundles) {
- // Should have gotten zero bundles
- expect(bundles.length).toEqual(0);
-
- // They should have the values we expect; don't care about order,
- // some map/reduce the summation.
- expect(mockLog.warn).toHaveBeenCalled();
- });
- });
-
- it("warns about, then ignores, malformed bundle declarations", function () {
- // File-not-found
- mockHttp.get.and.returnValue(Promise.resolve(["I am not a valid bundle."]));
-
- // Try and load
- return loader.loadBundles(["some/bundle"]).then(function (bundles) {
- // Should have gotten zero bundle
- expect(bundles.length).toEqual(0);
-
- // They should have the values we expect; don't care about order,
- // some map/reduce the summation.
- expect(mockLog.warn).toHaveBeenCalled();
- });
- });
-
- });
- }
-);
diff --git a/platform/framework/test/load/BundleSpec.js b/platform/framework/test/load/BundleSpec.js
deleted file mode 100644
index f87bfe54f..000000000
--- a/platform/framework/test/load/BundleSpec.js
+++ /dev/null
@@ -1,98 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * BundleSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/load/Bundle", "../../src/Constants"],
- function (Bundle, Constants) { // Verify against constants, too
-
- describe("A bundle", function () {
- var PATH = "some/path",
- KEY = "someKey";
-
- it("reports its path", function () {
- expect(new Bundle(PATH, {}).getPath()).toEqual(PATH);
- });
-
- it("reports its source path", function () {
- expect(new Bundle(PATH, { "sources": "test-src" }).getSourcePath()).toEqual(
- PATH + Constants.SEPARATOR + "test-src"
- );
- });
-
- it("reports the default source path if none is configured", function () {
- expect(new Bundle(PATH, {}).getSourcePath()).toEqual(
- PATH + Constants.SEPARATOR + Constants.DEFAULT_BUNDLE.sources
- );
- });
-
- it("reports its resource path", function () {
- expect(new Bundle(PATH, { "resources": "test-res" }).getResourcePath()).toEqual(
- PATH + Constants.SEPARATOR + "test-res"
- );
- });
-
- it("reports the default resource path if none is configured", function () {
- expect(new Bundle(PATH, {}).getResourcePath()).toEqual(
- PATH + Constants.SEPARATOR + Constants.DEFAULT_BUNDLE.resources
- );
- });
-
- it("has a log-friendly name for the bundle which includes its key and path", function () {
- // Use indexof to look for the bundle's key
- var logName = new Bundle(PATH, { key: KEY }).getLogName();
- expect(logName.indexOf(KEY)).not.toEqual(-1);
- expect(logName.indexOf(PATH)).not.toEqual(-1);
- });
-
- it("reports all declared extension categories", function () {
- var bundle = new Bundle(PATH, {
- extensions: {
- things: [],
- tests: [],
- foos: []
- }
- });
-
- expect(bundle.getExtensionCategories().sort()).toEqual(
- ["foos", "tests", "things"]
- );
- });
-
- it("reports all extensions that have been declared", function () {
- var bundle = new Bundle(PATH, {
- extensions: { things: [{}, {}, {}] }
- });
- expect(bundle.getExtensions("things").length).toEqual(3);
- });
-
- it("reports an empty list for extensions that have not been declared", function () {
- var bundle = new Bundle(PATH, {
- extensions: { things: [{}, {}, {}] }
- });
- expect(bundle.getExtensions("stuffs").length).toEqual(0);
- });
- });
- }
-);
diff --git a/platform/framework/test/load/ExtensionSpec.js b/platform/framework/test/load/ExtensionSpec.js
deleted file mode 100644
index 04e500d42..000000000
--- a/platform/framework/test/load/ExtensionSpec.js
+++ /dev/null
@@ -1,107 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * ExtensionSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/load/Extension", "../../src/load/Bundle"],
- function (Extension, Bundle) {
-
- describe("An extension", function () {
- var bundle;
-
- beforeEach(function () {
- bundle = new Bundle("test/bundle", {});
- });
-
- it("reports its key", function () {
- expect(new Extension(bundle, "tests", { key: "testKey"}).getKey())
- .toEqual("testKey");
- });
-
- it("reports some key, even if non is defined", function () {
- expect(new Extension(bundle, "tests", {}).getKey()).toBeDefined();
- });
-
- it("allows retrieval of its declaring bundle", function () {
- expect(new Extension(bundle, "tests", {}).getBundle()).toBe(bundle);
- });
-
- it("reports its category", function () {
- expect(new Extension(bundle, "tests", {}).getCategory())
- .toEqual("tests");
- expect(new Extension(bundle, "otherThings", {}).getCategory())
- .toEqual("otherThings");
- });
-
- it("provides a check to see if an implementation is associated with the extension", function () {
- expect(new Extension(bundle, "tests", {}).hasImplementation())
- .toBeFalsy();
- expect(new Extension(bundle, "tests", { implementation: "Something.js" }).hasImplementation())
- .toBeTruthy();
- });
-
- it("provides a full path to an implementation, if present", function () {
- expect(new Extension(bundle, "tests", { implementation: "Something.js" }).getImplementationPath())
- .toEqual("test/bundle/src/Something.js");
- });
-
- it("does not define an implementation path if there is no implementation", function () {
- expect(new Extension(bundle, "tests", {}).getImplementationPath())
- .toBeUndefined();
- });
-
- it("provides a log-friendly name which contains the extension key", function () {
- var logName =
- new Extension(bundle, "tests", { key: "testKey"}).getLogName();
- expect(logName.indexOf("testKey")).not.toEqual(-1);
- });
-
- it("allows its definition to be retrieved", function () {
- var definition = {
- key: "someKey",
- implementation: "SomeImplementation.js"
- },
- reported = new Extension(bundle, "tests", definition).getDefinition();
-
- // Bundle is added, so we can't do a normal toEqual; check that
- // all keys are still there, though.
- Object.keys(definition).forEach(function (k) {
- expect(reported[k]).toEqual(definition[k]);
- });
- });
-
- it("includes the bundle in its definition", function () {
- // Bundle is needed by some registrars in the the registration phase,
- // so make sure it gets attached to the definition.
- var definition = {
- key: "someKey",
- implementation: "SomeImplementation.js"
- },
- reported = new Extension(bundle, "tests", definition).getDefinition();
-
- expect(reported.bundle).toEqual(bundle.getDefinition());
- });
- });
- }
-);
diff --git a/platform/framework/test/register/CustomRegistrarsSpec.js b/platform/framework/test/register/CustomRegistrarsSpec.js
deleted file mode 100644
index ef3341a6d..000000000
--- a/platform/framework/test/register/CustomRegistrarsSpec.js
+++ /dev/null
@@ -1,198 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * CustomRegistrarsSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/register/CustomRegistrars"],
- function (CustomRegistrars) {
-
- describe("Custom registrars", function () {
- var mockLog,
- mockApp,
- customRegistrars;
-
- // Set up mock test dependencies
- beforeEach(function () {
- mockApp = jasmine.createSpyObj("app", [
- "controller",
- "directive",
- "service",
- "constant",
- "config",
- "run"
- ]);
-
- mockLog = jasmine.createSpyObj("$log", [
- "error",
- "warn",
- "info",
- "debug"
- ]);
-
- customRegistrars = new CustomRegistrars(mockApp, mockLog);
- });
-
- it("has custom registrars for Angular built-ins", function () {
- expect(customRegistrars.directives).toBeTruthy();
- expect(customRegistrars.controllers).toBeTruthy();
- expect(customRegistrars.services).toBeTruthy();
- expect(customRegistrars.routes).toBeTruthy();
- expect(customRegistrars.constants).toBeTruthy();
- expect(customRegistrars.runs).toBeTruthy();
- });
-
- it("invokes built-in functions on the app", function () {
- // Verify preconditions, invoke, expect to have been called
- expect(mockApp.directive.calls.count()).toEqual(0);
- customRegistrars.directives([{ key: "a" }, { key: "b" }, { key: "c" }]);
- expect(mockApp.directive.calls.count()).toEqual(3);
-
- expect(mockApp.controller.calls.count()).toEqual(0);
- customRegistrars.controllers([{ key: "a" }, { key: "b" }, { key: "c" }]);
- expect(mockApp.controller.calls.count()).toEqual(3);
-
- expect(mockApp.service.calls.count()).toEqual(0);
- customRegistrars.services([{ key: "a" }, { key: "b" }, { key: "c" }]);
- expect(mockApp.service.calls.count()).toEqual(3);
-
- expect(mockApp.constant.calls.count()).toEqual(0);
- customRegistrars.constants([{
- key: "a",
- value: "b"
- }, {
- key: "b",
- value: "c"
- }, {
- key: "c",
- value: "d"
- }]);
- expect(mockApp.constant.calls.count()).toEqual(3);
-
- expect(mockApp.run.calls.count()).toEqual(0);
- customRegistrars.runs([jasmine.createSpy("a"), jasmine.createSpy("a"), jasmine.createSpy("a")]);
- expect(mockApp.run.calls.count()).toEqual(3);
-
- });
-
- it("warns when keys are not defined, then skips", function () {
- // Verify preconditions, invoke, expect to have been called
- expect(mockApp.directive.calls.count()).toEqual(0);
- customRegistrars.directives([{ key: "a" }, { }, { key: "c" }]);
- expect(mockApp.directive.calls.count()).toEqual(2);
- expect(mockLog.warn.calls.count()).toEqual(1);
-
- expect(mockApp.controller.calls.count()).toEqual(0);
- customRegistrars.controllers([{ }, { }, { key: "c" }]);
- expect(mockApp.controller.calls.count()).toEqual(1);
- expect(mockLog.warn.calls.count()).toEqual(3);
-
- expect(mockApp.service.calls.count()).toEqual(0);
- customRegistrars.services([{ }, { }, { }]);
- expect(mockApp.service.calls.count()).toEqual(0);
- expect(mockLog.warn.calls.count()).toEqual(6);
-
- expect(mockApp.constant.calls.count()).toEqual(0);
- customRegistrars.constants([{ }, { }, { }]);
- expect(mockApp.constant.calls.count()).toEqual(0);
- expect(mockLog.warn.calls.count()).toEqual(9);
-
- // Notably, keys are not needed for run calls
- });
-
- it("does not re-register duplicate keys", function () {
- // Verify preconditions, invoke, expect to have been called
- expect(mockApp.directive.calls.count()).toEqual(0);
- customRegistrars.directives([{ key: "a" }, { key: "a" }]);
- expect(mockApp.directive.calls.count()).toEqual(1);
-
- expect(mockApp.controller.calls.count()).toEqual(0);
- customRegistrars.controllers([{ key: "c" }, { key: "c" }, { key: "c" }]);
- expect(mockApp.controller.calls.count()).toEqual(1);
-
- expect(mockApp.service.calls.count()).toEqual(0);
- customRegistrars.services([{ key: "b" }, { key: "b" }]);
- expect(mockApp.service.calls.count()).toEqual(1);
-
- // None of this should have warned, this is all
- // nominal behavior
- expect(mockLog.warn.calls.count()).toEqual(0);
- });
-
- it("allows routes to be registered", function () {
- var mockRouteProvider = jasmine.createSpyObj(
- "$routeProvider",
- ["when", "otherwise"]
- ),
- bundle = {
- path: "test/bundle",
- resources: "res"
- },
- routes = [
- {
- when: "foo",
- templateUrl: "templates/test.html",
- bundle: bundle
- },
- {
- templateUrl: "templates/default.html",
- bundle: bundle
- }
- ];
-
- customRegistrars.routes(routes);
-
- // Give it the route provider based on its config call
- mockApp.config.calls.all().forEach(function (call) {
- // Invoke the provided callback
- call.args[0][1](mockRouteProvider);
- });
-
- // The "when" clause should have been mapped to the when method...
- expect(mockRouteProvider.when).toHaveBeenCalled();
- expect(mockRouteProvider.when.calls.mostRecent().args[0]).toEqual("foo");
- expect(mockRouteProvider.when.calls.mostRecent().args[1].templateUrl)
- .toEqual("test/bundle/res/templates/test.html");
-
- // ...while the other should have been treated as a default route
- expect(mockRouteProvider.otherwise).toHaveBeenCalled();
- expect(mockRouteProvider.otherwise.calls.mostRecent().args[0].templateUrl)
- .toEqual("test/bundle/res/templates/default.html");
- });
-
- it("accepts components for service compositing", function () {
- // Most relevant code will be exercised in service compositor spec
- expect(customRegistrars.components).toBeTruthy();
- customRegistrars.components([]);
- });
-
- it("warns if no implementation is provided for runs", function () {
- // Verify precondition
- expect(mockLog.warn).not.toHaveBeenCalled();
- customRegistrars.runs([{ something: "that is not a function"}]);
- expect(mockLog.warn).toHaveBeenCalledWith(jasmine.any(String));
- expect(mockApp.run).not.toHaveBeenCalled();
- });
- });
- }
-);
diff --git a/platform/framework/test/register/ExtensionRegistrarSpec.js b/platform/framework/test/register/ExtensionRegistrarSpec.js
deleted file mode 100644
index 68d8b9ccd..000000000
--- a/platform/framework/test/register/ExtensionRegistrarSpec.js
+++ /dev/null
@@ -1,119 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * ExtensionRegistrarSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/register/ExtensionRegistrar"],
- function (ExtensionRegistrar) {
-
- describe("The extension registrar", function () {
- var mockApp,
- mockLog,
- mockSorter,
- customRegistrars,
- registrar;
-
- beforeEach(function () {
- mockApp = jasmine.createSpyObj("app", ["factory"]);
- mockLog = jasmine.createSpyObj("$log", ["error", "warn", "debug", "info"]);
- mockSorter = jasmine.createSpyObj("sorter", ["sort"]);
- customRegistrars = {};
-
- mockSorter.sort.and.callFake(function (v) {
- return v;
- });
-
- registrar = new ExtensionRegistrar(
- mockApp,
- customRegistrars,
- mockSorter,
- mockLog
- );
- });
-
- it("registers extensions using the factory", function () {
- registrar.registerExtensions({ things: [{}] });
- expect(mockApp.factory).toHaveBeenCalled();
- });
-
- it("registers extensions with square brackets, as arrays", function () {
- var callbacks = {};
- mockApp.factory.and.callFake(function (name, value) {
- callbacks[name] = value[value.length - 1];
- });
- registrar.registerExtensions({ things: [{}] });
- expect(callbacks["things[]"]).toBeDefined();
-
- // Verify dependency echo behavior
- expect(callbacks["things[]"]("a", "b", "c")).toEqual(["a", "b", "c"]);
- });
-
- it("warns if multiple registrations are made for the same category of extension", function () {
- registrar.registerExtensions({ things: [{}] });
- expect(mockLog.warn).not.toHaveBeenCalled();
- registrar.registerExtensions({ things: [{}] });
- expect(mockLog.warn).toHaveBeenCalled();
- });
-
- it("registers empty extension categories when they are needed", function () {
- var lengths = {};
- mockApp.factory.and.callFake(function (name, value) {
- lengths[name] = value.length;
- });
- // Nobody has registered tests[], but it looks like an extension dependency,
- // so register it as an empty array.
- registrar.registerExtensions({ things: [{ depends: ["tests[]", "other"] }] });
- expect(lengths["tests[]"]).toEqual(1);
- expect(lengths.other).toBeUndefined();
- });
-
- it("invokes custom registrars (not app.factory) when available", function () {
- customRegistrars.things = jasmine.createSpy("things");
- registrar.registerExtensions({ things: [{}] });
- expect(mockApp.factory).not.toHaveBeenCalled();
- expect(customRegistrars.things).toHaveBeenCalled();
- });
-
- it("sorts extensions before registering", function () {
- // Some extension definitions to sort
- var a = { a: 'a' }, b = { b: 'b' }, c = { c: 'c' };
-
- // Fake sorting; just reverse the array
- mockSorter.sort.and.callFake(function (v) {
- return v.reverse();
- });
-
- // Register the extensions
- registrar.registerExtensions({ things: [a, b, c] });
-
- // Verify registration interactions occurred in reverse-order
- [c, b, a].forEach(function (extension, index) {
- expect(mockApp.factory.calls.all()[index].args[1][0]())
- .toEqual(extension);
- });
- });
-
- });
- }
-);
diff --git a/platform/framework/test/register/ExtensionSorterSpec.js b/platform/framework/test/register/ExtensionSorterSpec.js
deleted file mode 100644
index a6de3ebec..000000000
--- a/platform/framework/test/register/ExtensionSorterSpec.js
+++ /dev/null
@@ -1,69 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../../src/register/ExtensionSorter"],
- function (ExtensionSorter) {
-
- describe("The extension sorter", function () {
- var mockLog,
- sorter;
-
- beforeEach(function () {
- mockLog = jasmine.createSpyObj(
- "$log",
- ["error", "warn", "debug", "info"]
- );
-
- sorter = new ExtensionSorter(mockLog);
- });
-
- it("sorts extensions in priority order", function () {
- var a = { priority: 10 },
- b = {},
- c = { priority: 'mandatory' }; // Should be +Inf
- expect(sorter.sort([a, b, c])).toEqual([c, a, b]);
- });
-
- it("warns about unrecognized priorities", function () {
- var a = { priority: 10 },
- b = {},
- c = { priority: 'mandatory' }, // Should be +Inf
- d = { priority: 'GARBAGE-TEXT' },
- e = { priority: { mal: "formed"} },
- f = { priority: 3 };
-
- // Sorting should use default order (note we assume
- // a stable sort here as well)
- expect(sorter.sort(
- [a, b, c, d, e, f]
- )).toEqual(
- [c, a, f, b, d, e]
- );
-
- // Should have been warned exactly twice (for d & e)
- expect(mockLog.warn.calls.count()).toEqual(2);
- });
-
- });
- }
-);
diff --git a/platform/framework/test/register/PartialConstructorSpec.js b/platform/framework/test/register/PartialConstructorSpec.js
deleted file mode 100644
index 4871aefcd..000000000
--- a/platform/framework/test/register/PartialConstructorSpec.js
+++ /dev/null
@@ -1,97 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * PartialConstructorSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/register/PartialConstructor"],
- function (PartialConstructor) {
-
- describe("A partial constructor", function () {
- var result,
- PartializedConstructor;
-
- function RegularConstructor(a, b, c, d) {
- result = {
- a: a,
- b: b,
- c: c,
- d: d
- };
-
- return result;
- }
-
- function ThisStyleConstructor(x, y, z) {
- this.message = [x, y, z].join(" ");
- }
-
- RegularConstructor.someProperty = "test property";
-
- beforeEach(function () {
- result = undefined;
- PartializedConstructor = new PartialConstructor(RegularConstructor);
- });
-
- it("splits a constructor call into two stages", function () {
- var RemainingConstructor = new PartializedConstructor("test"),
- instance;
- // first call should not have hit constructor
- expect(result).toBeUndefined();
-
- // Call again
- instance = new RemainingConstructor(1, 2, 3);
- expect(result).toEqual({
- a: "test",
- b: 1,
- c: 2,
- d: 3
- });
-
- // Should have returned the constructor's value
- expect(instance).toEqual(result);
- });
-
- it("handles this-style constructors", function () {
- var Partialized = new PartialConstructor(ThisStyleConstructor),
- Remaining = new Partialized("this"),
- instance = new Remaining("is", "correct");
-
- // We should have everything we put in "this", and we
- // should still pass an instanceof test.g
- expect(instance.message).toEqual("this is correct");
- expect(instance).toEqual(new ThisStyleConstructor("this", "is", "correct"));
- expect(instance instanceof ThisStyleConstructor).toBeTruthy();
- });
-
- it("retains static properties after partialization", function () {
- // This string should appear after invoking the partialized
- // constructor, such that the resulting inner constructor
- // exposes these as if we were looking at the original
- // RegularConstructor.
- expect(new PartializedConstructor().someProperty).toEqual("test property");
- });
-
- });
- }
-);
diff --git a/platform/framework/test/register/ServiceCompositorSpec.js b/platform/framework/test/register/ServiceCompositorSpec.js
deleted file mode 100644
index 0e78721e6..000000000
--- a/platform/framework/test/register/ServiceCompositorSpec.js
+++ /dev/null
@@ -1,258 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * ServiceCompositorSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/register/ServiceCompositor"],
- function (ServiceCompositor) {
-
- describe("The service compositor", function () {
- var registered,
- mockApp,
- mockLog,
- compositor;
-
- beforeEach(function () {
- registered = {};
-
- mockApp = jasmine.createSpyObj("app", ["service"]);
- mockLog = jasmine.createSpyObj("$log", ["error", "warn", "info", "debug"]);
-
- mockApp.service.and.callFake(function (name, value) {
- var factory = value[value.length - 1];
-
- registered[name] = {
- depends: value.slice(0, value.length - 1),
- callback: value[value.length - 1]
- };
-
- // Track what name it was registered under
- factory.registeredName = name;
- });
-
- compositor = new ServiceCompositor(mockApp, mockLog);
- });
-
- it("allows composite services to be registered", function () {
- compositor.registerCompositeServices([
- {
- type: "provider",
- provides: "testService"
- }
- ]);
-
- expect(mockApp.service).toHaveBeenCalled();
- });
-
- it("allows composite services to be registered", function () {
- // Prepare components that look like resolved extensions
- var components, name;
- function MyDecorator() {
- return {};
- }
-
- function MyOtherDecorator() {
- return {};
- }
-
- function MyProvider() {
- return {};
- }
-
- function MyOtherProvider() {
- return {};
- }
-
- function MyAggregator() {
- return {};
- }
-
- components = [
- MyDecorator,
- MyProvider,
- MyAggregator,
- MyOtherDecorator,
- MyOtherProvider
- ];
-
- MyDecorator.type = "decorator";
- MyOtherDecorator.type = "decorator";
- MyProvider.type = "provider";
- MyOtherProvider.type = "provider";
- MyAggregator.type = "aggregator";
- components.forEach(function (c) {
- c.provides = "testService";
- });
-
- // Add some test dependencies, to check prepending
- MyOtherDecorator.depends = ["someOtherService"];
- MyAggregator.depends = ["tests[]"];
-
- // Register!
- compositor.registerCompositeServices(components);
-
- expect(mockApp.service).toHaveBeenCalled();
-
- // Verify some interesting spots on dependency graph
- expect(registered.testService.depends).toEqual([
- MyOtherDecorator.registeredName
- ]);
- expect(registered[MyOtherDecorator.registeredName].depends).toEqual([
- "someOtherService",
- MyDecorator.registeredName
- ]);
- expect(registered[MyAggregator.registeredName].depends.length).toEqual(2);
- // Get the name of the group of providers
- name = registered[MyAggregator.registeredName].depends[1];
- // ...it should depend on both providers
- expect(registered[name].depends).toEqual([
- MyProvider.registeredName,
- MyOtherProvider.registeredName
- ]);
- });
-
- it("allows registered composite services to be instantiated", function () {
- // Prepare components that look like resolved extensions
- var components, name;
- function MyProvider() {
- return {};
- }
-
- function MyOtherProvider() {
- return {};
- }
-
- function MyAggregator() {
- return {};
- }
-
- components = [MyProvider, MyAggregator, MyOtherProvider];
-
- MyProvider.type = "provider";
- MyOtherProvider.type = "provider";
- MyAggregator.type = "aggregator";
- components.forEach(function (c) {
- c.provides = "testService";
- });
-
- // Register!
- compositor.registerCompositeServices(components);
-
- expect(mockApp.service).toHaveBeenCalled();
-
- // Test service should just be a reflecting dependency;
- // it will depend upon (then return) the aggregator.
- expect(registered.testService.callback("hello")).toEqual("hello");
-
- // The aggregated provider dependencies should be similar,
- // except they should reflect back the array of arguments.
- // Get the name of the group of providers
- name = registered[MyAggregator.registeredName].depends[0];
- // ...it should depend on both providers
- expect(registered[name].callback(1, 2, "hello")).toEqual([1, 2, "hello"]);
-
- });
-
- it("warns and skips components with no service type", function () {
- // Prepare components that look like resolved extensions
- var components;
- function MyProvider() {
- return {};
- }
-
- function MyDecorator() {
- return {};
- }
-
- function MyAggregator() {
- return {};
- }
-
- components = [MyProvider, MyAggregator, MyDecorator];
-
- MyProvider.type = "provider";
- MyDecorator.type = "decorator";
- MyAggregator.type = "aggregator";
-
- // Notably, we don't do
- // components.forEach(function (c) { c.provides = "testService"; });
-
- // Try to register...
- compositor.registerCompositeServices(components);
-
- // Nothing should have gotten registered
- expect(mockApp.service).not.toHaveBeenCalled();
-
- // Should have gotten one warning for each skipped component
- expect(mockLog.warn.calls.count()).toEqual(2);
- expect(mockLog.info.calls.count()).toEqual(1);
- });
-
- it("warns about and skips aggregators with zero providers", function () {
- // Prepare components that look like resolved extensions
- var components;
- function MyAggregator() {
- return {};
- }
-
- components = [MyAggregator];
-
- MyAggregator.type = "aggregator";
- MyAggregator.provides = "testService";
-
- // Try to register...
- compositor.registerCompositeServices(components);
-
- // Nothing should have gotten registered
- expect(mockApp.service).not.toHaveBeenCalled();
-
- // Should have gotten a warning
- expect(mockLog.info).toHaveBeenCalled();
- });
-
- it("warns about and skips decorators with nothing to decorate", function () {
- // Prepare components that look like resolved extensions
- var components;
- function MyDecorator() {
- return {};
- }
-
- components = [MyDecorator];
-
- MyDecorator.type = "decorator";
- MyDecorator.provides = "testService";
-
- // Try to register...
- compositor.registerCompositeServices(components);
-
- // Nothing should have gotten registered
- expect(mockApp.service).not.toHaveBeenCalled();
-
- // Should have gotten a warning
- expect(mockLog.warn).toHaveBeenCalled();
- });
-
- });
- }
-);
diff --git a/platform/framework/test/resolve/BundleResolverSpec.js b/platform/framework/test/resolve/BundleResolverSpec.js
deleted file mode 100644
index 61defd28e..000000000
--- a/platform/framework/test/resolve/BundleResolverSpec.js
+++ /dev/null
@@ -1,71 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * BundleResolverSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/resolve/BundleResolver", "../../src/load/Bundle"],
- function (BundleResolver, Bundle) {
-
- describe("The bundle resolver", function () {
- var mockExtensionResolver,
- mockLog,
- resolver;
-
- beforeEach(function () {
- mockExtensionResolver = jasmine.createSpyObj(
- "extensionResolver",
- ["resolve"]
- );
- mockLog = jasmine.createSpyObj(
- "$log",
- ["error", "warn", "info", "debug"]
- );
-
- mockExtensionResolver.resolve.and.returnValue(Promise.resolve("a"));
-
- resolver = new BundleResolver(
- mockExtensionResolver,
- mockLog
- );
- });
-
- it("invokes the extension resolver for all bundle extensions", function () {
- return resolver.resolveBundles([
- new Bundle("x", { extensions: { tests: [{}, {}, {}] } }),
- new Bundle("y", {
- extensions: {
- tests: [{}, {}],
- others: [{}, {}]
- }
- }),
- new Bundle("z", { extensions: { others: [{}] } })
- ]).then(function (result) {
- expect(result.tests).toEqual(["a", "a", "a", "a", "a"]);
- expect(result.others).toEqual(["a", "a", "a"]);
- });
- });
-
- });
- }
-);
diff --git a/platform/framework/test/resolve/ExtensionResolverSpec.js b/platform/framework/test/resolve/ExtensionResolverSpec.js
deleted file mode 100644
index 4e0b591c0..000000000
--- a/platform/framework/test/resolve/ExtensionResolverSpec.js
+++ /dev/null
@@ -1,114 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * ExtensionResolverSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/resolve/ExtensionResolver", "../../src/load/Bundle"],
- function (ExtensionResolver, Bundle) {
-
- describe("", function () {
- var mockLoader,
- mockLog,
- resolver;
-
- // Test implementation, to load from the mock loader
- function Constructor() {
- return { someKey: "some value" };
- }
-
- Constructor.someProperty = "some static value";
-
- beforeEach(function () {
- mockLoader = jasmine.createSpyObj("loader", ["load"]);
-
- mockLog = jasmine.createSpyObj(
- "$log",
- ["error", "warn", "info", "debug"]
- );
-
- mockLoader.load.and.returnValue(Promise.resolve(Constructor));
-
- resolver = new ExtensionResolver(mockLoader, mockLog);
- });
-
- it("requests implementations from the implementation loader", function () {
- var bundle = new Bundle("w", {
- sources: "x",
- extensions: { tests: [{ implementation: "y/z.js" }] }
- }),
- extension = bundle.getExtensions("tests")[0];
-
- return resolver.resolve(extension).then(function (result) {
- // Verify that the right file was requested
- expect(mockLoader.load).toHaveBeenCalledWith("w/x/y/z.js");
-
- // We should have resolved to the constructor from above
- expect(typeof result).toEqual('function');
- expect(result().someKey).toEqual("some value");
- });
- });
-
- it("issues a warning and defaults to plain definition if load fails", function () {
- var bundle = new Bundle("w", {
- sources: "x",
- extensions: {
- tests: [{
- someOtherKey: "some other value",
- implementation: "y/z.js"
- }]
- }
- }),
- extension = bundle.getExtensions("tests")[0];
-
- mockLoader.load.and.returnValue(Promise.reject(new Error("test error")));
-
- return resolver.resolve(extension).then(function (result) {
- // Should have gotten a warning
- expect(mockLog.warn).toHaveBeenCalled();
- // We should have resolved to the plain definition from above
- expect(typeof result).not.toEqual('function');
- expect(result.someOtherKey).toEqual("some other value");
- });
- });
-
- it("ensures implementation properties are exposed", function () {
- var bundle = new Bundle("w", {
- sources: "x",
- extensions: { tests: [{ implementation: "y/z.js" }] }
- }),
- extension = bundle.getExtensions("tests")[0];
-
- return resolver.resolve(extension).then(function (result) {
- // Verify that the right file was requested
- expect(mockLoader.load).toHaveBeenCalledWith("w/x/y/z.js");
- // We should have resolved to the constructor from above
- expect(typeof result).toEqual('function');
- expect(result().someKey).toEqual("some value");
- expect(result.someProperty).toEqual("some static value");
- });
- });
-
- });
- }
-);
diff --git a/platform/framework/test/resolve/ImplementationLoaderSpec.js b/platform/framework/test/resolve/ImplementationLoaderSpec.js
deleted file mode 100644
index 3ec3c8f2b..000000000
--- a/platform/framework/test/resolve/ImplementationLoaderSpec.js
+++ /dev/null
@@ -1,88 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * ImplementationLoaderSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/resolve/ImplementationLoader"],
- function (ImplementationLoader) {
-
- describe("The implementation loader", function () {
- var required,
- loader;
-
- function mockRequire(names, fulfill, reject) {
- required = {
- names: names,
- fulfill: fulfill,
- reject: reject
- };
- }
-
- beforeEach(function () {
- required = undefined;
- loader = new ImplementationLoader(mockRequire);
- });
-
- it("passes script names to require", function () {
- loader.load("xyz.js");
- expect(required.names).toEqual(["xyz.js"]);
- });
-
- it("wraps require results in a Promise that can resolve", function () {
- // Load and get the result
- var promise = loader.load("xyz.js").then(function (result) {
- expect(result).toEqual("test result");
- });
-
- required.fulfill("test result");
-
- return promise;
- });
-
- it("wraps require results in a Promise that can reject", function () {
- var result,
- rejection;
-
- // Load and get the result
- var promise = loader.load("xyz.js").then(
- function (v) {
- result = v;
- },
- function (v) {
- rejection = v;
- });
-
- expect(result).toBeUndefined();
-
- required.reject("test result");
-
- return promise.then(function () {
- expect(result).toBeUndefined();
- expect(rejection).toEqual("test result");
- });
- });
-
- });
- }
-);
diff --git a/platform/identity/README.md b/platform/identity/README.md
deleted file mode 100644
index 4df89f736..000000000
--- a/platform/identity/README.md
+++ /dev/null
@@ -1,2 +0,0 @@
-This bundle is responsible for introducing identity management features
-and API to Open MCT.
diff --git a/platform/identity/bundle.js b/platform/identity/bundle.js
deleted file mode 100644
index fc077b96f..000000000
--- a/platform/identity/bundle.js
+++ /dev/null
@@ -1,87 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
- "./src/IdentityAggregator",
- "./src/IdentityProvider",
- "./src/IdentityCreationDecorator",
- "./src/IdentityIndicator"
-], function (
- IdentityAggregator,
- IdentityProvider,
- IdentityCreationDecorator,
- IdentityIndicator
-) {
-
- return {
- name: "platform/identity",
- definition: {
- "extensions": {
- "components": [
- {
- "implementation": IdentityAggregator,
- "type": "aggregator",
- "provides": "identityService",
- "depends": [
- "$q"
- ]
- },
- {
- "implementation": IdentityProvider,
- "type": "provider",
- "provides": "identityService",
- "depends": [
- "$q"
- ],
- "priority": "fallback"
- },
- {
- "type": "decorator",
- "provides": "creationService",
- "implementation": IdentityCreationDecorator,
- "depends": [
- "identityService"
- ]
- }
- ],
- "indicators": [
- {
- "implementation": IdentityIndicator,
- "depends": [
- "identityService"
- ]
- }
- ],
- "types": [
- {
- "properties": [
- {
- "key": "creator",
- "name": "Creator"
- }
- ]
- }
- ]
- }
- }
- };
-});
diff --git a/platform/identity/src/IdentityAggregator.js b/platform/identity/src/IdentityAggregator.js
deleted file mode 100644
index 79c0ec7b7..000000000
--- a/platform/identity/src/IdentityAggregator.js
+++ /dev/null
@@ -1,91 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Defines interfaces and common infrastructure for establishing
- * a user's identity.
- * @namespace platform/identity
- */
-define(
- function () {
-
- /**
- * Provides information about the currently logged-in
- * user, if available.
- *
- * @interface IdentityService
- */
-
- /**
- * Get information about the current user. This returns a promise
- * which will resolve to metadata about the user, or undefined if
- * no information about the user is available.
- *
- * @method IdentityService#getUser
- * @returns {Promise.<IdentityMetadata>} a promise for metadata about
- * the current user
- */
-
- /**
- * Metadata about a user.
- *
- * @typedef IdentityMetadata
- * @property {string} name the user's human-readable name
- * @property {string} key the user's machine-readable name
- */
-
- /**
- * Aggregator for multiple identity services. Exposes the first
- * defined identity provided by any provider, according to
- * priority order.
- *
- * @constructor
- * @implements {IdentityService}
- * @memberof platform/identity
- */
- function IdentityAggregator($q, providers) {
- this.providers = providers;
- this.$q = $q;
- }
-
- function delegateGetUser(provider) {
- return provider.getUser();
- }
-
- function identity(value) {
- return value;
- }
-
- function giveFirst(results) {
- return results.filter(identity)[0];
- }
-
- IdentityAggregator.prototype.getUser = function () {
- var $q = this.$q,
- promises = this.providers.map(delegateGetUser);
-
- return $q.all(promises).then(giveFirst);
- };
-
- return IdentityAggregator;
- }
-);
diff --git a/platform/identity/src/IdentityProvider.js b/platform/identity/src/IdentityProvider.js
deleted file mode 100644
index bfa030f56..000000000
--- a/platform/identity/src/IdentityProvider.js
+++ /dev/null
@@ -1,51 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Defines interfaces and common infrastructure for establishing
- * a user's identity.
- * @namespace platform/identity
- */
-define(
- function () {
-
- /**
- * Default implementation of an identity service. Provides an
- * unknown user as an `undefined` value; this is present simply
- * to ensure that there is always an `identityService` available
- * for platform components to use.
- *
- * @constructor
- * @implements {IdentityService}
- * @memberof platform/identity
- */
- function IdentityProvider($q) {
- this.userPromise = $q.when(undefined);
- }
-
- IdentityProvider.prototype.getUser = function () {
- return this.userPromise;
- };
-
- return IdentityProvider;
- }
-);
diff --git a/platform/identity/test/IdentityAggregatorSpec.js b/platform/identity/test/IdentityAggregatorSpec.js
deleted file mode 100644
index ea9f3fc24..000000000
--- a/platform/identity/test/IdentityAggregatorSpec.js
+++ /dev/null
@@ -1,139 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../src/IdentityAggregator"],
- function (IdentityAggregator) {
-
- describe("The identity aggregator", function () {
- var mockProviders,
- mockQ,
- resolves,
- testUsers,
- aggregator;
-
- function resolveProviderPromises() {
- ['a', 'b', 'c'].forEach(function (id, i) {
- resolves[id](testUsers[i]);
- });
- }
-
- beforeEach(function () {
- testUsers = [
- {
- key: "user0",
- name: "User Zero"
- },
- {
- key: "user1",
- name: "User One"
- },
- {
- key: "user2",
- name: "User Two"
- }
- ];
-
- resolves = {};
-
- mockProviders = ['a', 'b', 'c'].map(function (id) {
- var mockProvider = jasmine.createSpyObj(
- 'provider-' + id,
- ['getUser']
- );
-
- mockProvider.getUser.and.returnValue(new Promise(function (r) {
- resolves[id] = r;
- }));
-
- return mockProvider;
- });
-
- mockQ = jasmine.createSpyObj('$q', ['all']);
- mockQ.all.and.callFake(function (promises) {
- return Promise.all(promises);
- });
-
- aggregator = new IdentityAggregator(
- mockQ,
- mockProviders
- );
- });
-
- it("delegates to the aggregated providers", function () {
- // Verify precondition
- mockProviders.forEach(function (p) {
- expect(p.getUser).not.toHaveBeenCalled();
- });
-
- aggregator.getUser();
-
- mockProviders.forEach(function (p) {
- expect(p.getUser).toHaveBeenCalled();
- });
- });
-
- it("returns the first result when it is defined", function () {
- var promise = aggregator.getUser();
-
- resolveProviderPromises();
-
- return promise.then(function (user) {
- expect(user).toEqual(testUsers[0]);
- });
- });
-
- it("returns a later result when earlier results are undefined", function () {
- testUsers[0] = undefined;
-
- var promise = aggregator.getUser();
-
- resolveProviderPromises();
-
- return promise.then(function (user) {
- expect(user).toEqual(testUsers[1]);
- });
- });
-
- it("returns undefined when no providers expose users", function () {
- testUsers = [undefined, undefined, undefined];
-
- var promise = aggregator.getUser();
-
- resolveProviderPromises();
-
- return promise.then(function (user) {
- expect(user).toBe(undefined);
- });
- });
-
- it("returns undefined when there are no providers", function () {
- var promise = new IdentityAggregator(mockQ, []).getUser();
-
- return promise.then(function (user) {
- expect(user).toBe(undefined);
- });
- });
-
- });
- }
-);
diff --git a/platform/identity/test/IdentityCreationDecoratorSpec.js b/platform/identity/test/IdentityCreationDecoratorSpec.js
deleted file mode 100644
index b35de02b3..000000000
--- a/platform/identity/test/IdentityCreationDecoratorSpec.js
+++ /dev/null
@@ -1,95 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [
- '../src/IdentityCreationDecorator'
- ],
- function (IdentityCreationDecorator) {
-
- describe("IdentityCreationDecorator", function () {
- var mockIdentityService,
- mockCreationService,
- mockParent,
- mockCreatedObject,
- decorator;
-
- beforeEach(function () {
- mockIdentityService = jasmine.createSpyObj(
- 'identityService',
- ['getUser']
- );
- mockCreationService = jasmine.createSpyObj(
- 'creationService',
- ['createObject']
- );
- mockParent = jasmine.createSpyObj(
- 'parentObject',
- ['getCapability', 'getId', 'getModel', 'hasCapability', 'useCapability']
- );
- mockCreatedObject = jasmine.createSpyObj(
- 'domainObject',
- ['getCapability', 'getId', 'getModel', 'hasCapability', 'useCapability']
- );
- mockCreationService.createObject
- .and.returnValue(Promise.resolve(mockCreatedObject));
- mockIdentityService.getUser
- .and.returnValue(Promise.resolve({ key: "test-user-id" }));
- mockParent.getId.and.returnValue('test-id');
- decorator = new IdentityCreationDecorator(
- mockIdentityService,
- mockCreationService
- );
- });
-
- it("delegates to its decorated service when identity is available", function () {
- var testModel = { someKey: "some value" };
-
- return decorator.createObject(testModel, mockParent)
- .then(function (object) {
- expect(object).toEqual(mockCreatedObject);
- });
- });
-
- it("adds a creator property", function () {
- var testModel = { someKey: "some value" };
-
- return decorator.createObject(testModel, mockParent)
- .then(function (object) {
- expect(object)
- .toEqual(mockCreatedObject);
-
- // Make sure arguments were delegated appropriately
- expect(mockCreationService.createObject)
- .toHaveBeenCalledWith(
- {
- someKey: "some value",
- creator: "test-user-id"
- },
- mockParent
- );
- });
- });
-
- });
- }
-);
diff --git a/platform/identity/test/IdentityIndicatorSpec.js b/platform/identity/test/IdentityIndicatorSpec.js
deleted file mode 100644
index 705089e06..000000000
--- a/platform/identity/test/IdentityIndicatorSpec.js
+++ /dev/null
@@ -1,70 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../src/IdentityIndicator"],
- function (IdentityIndicator) {
-
- describe("The identity indicator", function () {
- var mockPromise,
- mockIdentityService,
- indicator;
-
- beforeEach(function () {
- mockPromise = jasmine.createSpyObj('promise', ['then']);
- mockIdentityService = jasmine.createSpyObj(
- 'identityService',
- ['getUser']
- );
-
- mockIdentityService.getUser.and.returnValue(mockPromise);
-
- indicator = new IdentityIndicator(mockIdentityService);
- });
-
- it("shows information about the current user", function () {
- mockPromise.then.calls.mostRecent().args[0]({
- key: "testuserid",
- name: "A User"
- });
- expect(indicator.getCssClass()).toEqual("icon-person");
- expect(indicator.getText()).toEqual("A User");
- expect(indicator.getDescription().indexOf("testuserid"))
- .not.toEqual(-1);
- });
-
- it("shows nothing while no user information is available", function () {
- expect(indicator.getCssClass()).toBeUndefined();
- expect(indicator.getText()).toBeUndefined();
- expect(indicator.getDescription()).toBeUndefined();
- });
-
- it("shows nothing when there is no identity information", function () {
- mockPromise.then.calls.mostRecent().args[0](undefined);
- expect(indicator.getCssClass()).toBeUndefined();
- expect(indicator.getText()).toBeUndefined();
- expect(indicator.getDescription()).toBeUndefined();
- });
-
- });
- }
-);
diff --git a/platform/persistence/README.md b/platform/persistence/README.md
deleted file mode 100644
index e5ffbd156..000000000
--- a/platform/persistence/README.md
+++ /dev/null
@@ -1 +0,0 @@
-This directory contains bundles relating to the persistence of domain objects.
diff --git a/platform/persistence/aggregator/bundle.js b/platform/persistence/aggregator/bundle.js
deleted file mode 100644
index 142cfc2a5..000000000
--- a/platform/persistence/aggregator/bundle.js
+++ /dev/null
@@ -1,46 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
- "./src/PersistenceAggregator"
-], function (
- PersistenceAggregator
-) {
-
- return {
- name: "platform/persistence/aggregator",
- definition: {
- "extensions": {
- "components": [
- {
- "provides": "persistenceService",
- "type": "aggregator",
- "depends": [
- "$q"
- ],
- "implementation": PersistenceAggregator
- }
- ]
- }
- }
- };
-});
diff --git a/platform/persistence/aggregator/src/PersistenceAggregator.js b/platform/persistence/aggregator/src/PersistenceAggregator.js
deleted file mode 100644
index 712511df8..000000000
--- a/platform/persistence/aggregator/src/PersistenceAggregator.js
+++ /dev/null
@@ -1,90 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- // Return values to use when a persistence space is unknown,
- // and there is no appropriate provider to route to.
- var METHOD_DEFAULTS = {
- createObject: false,
- readObject: undefined,
- listObjects: [],
- updateObject: false,
- deleteObject: false
- };
-
- /**
- * Aggregates multiple persistence providers, such that they can be
- * utilized as if they were a single object. This is achieved by
- * routing persistence calls to an appropriate provider; the space
- * specified at call time is matched with the first provider (per
- * priority order) which reports that it provides persistence for
- * this space.
- *
- * @memberof platform/persistence/aggregator
- * @constructor
- * @implements {PersistenceService}
- * @param $q Angular's $q, for promises
- * @param {PersistenceService[]} providers the providers to aggregate
- */
- function PersistenceAggregator($q, providers) {
- var providerMap = {};
-
- function addToMap(provider) {
- return provider.listSpaces().then(function (spaces) {
- spaces.forEach(function (space) {
- providerMap[space] = providerMap[space] || provider;
- });
- });
- }
-
- this.providerMapPromise = $q.all(providers.map(addToMap))
- .then(function () {
- return providerMap;
- });
- }
-
- PersistenceAggregator.prototype.listSpaces = function () {
- return this.providerMapPromise.then(function (map) {
- return Object.keys(map);
- });
- };
-
- Object.keys(METHOD_DEFAULTS).forEach(function (method) {
- PersistenceAggregator.prototype[method] = function (space) {
- var delegateArgs = Array.prototype.slice.apply(arguments, []);
-
- return this.providerMapPromise.then(function (map) {
- var provider = map[space];
-
- return provider
- ? provider[method].apply(provider, delegateArgs)
- : METHOD_DEFAULTS[method];
- });
- };
- });
-
- return PersistenceAggregator;
- }
-);
diff --git a/platform/persistence/aggregator/test/PersistenceAggregatorSpec.js b/platform/persistence/aggregator/test/PersistenceAggregatorSpec.js
deleted file mode 100644
index 62b9e3540..000000000
--- a/platform/persistence/aggregator/test/PersistenceAggregatorSpec.js
+++ /dev/null
@@ -1,105 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ['../src/PersistenceAggregator'],
- function (PersistenceAggregator) {
-
- var PERSISTENCE_SERVICE_METHODS = [
- 'listSpaces',
- 'listObjects',
- 'createObject',
- 'readObject',
- 'updateObject',
- 'deleteObject'
- ],
- WRAPPED_METHODS = PERSISTENCE_SERVICE_METHODS.filter(function (m) {
- return m !== 'listSpaces';
- });
-
- function fakePromise(value) {
- return (value || {}).then ? value : {
- then: function (callback) {
- return fakePromise(callback(value));
- }
- };
- }
-
- describe("PersistenceAggregator", function () {
- var mockQ,
- mockProviders,
- mockCallback,
- testSpaces,
- aggregator;
-
- beforeEach(function () {
- testSpaces = ['a', 'b', 'c'];
- mockQ = jasmine.createSpyObj("$q", ['all']);
- mockProviders = testSpaces.map(function (space) {
- var mockProvider = jasmine.createSpyObj(
- 'provider-' + space,
- PERSISTENCE_SERVICE_METHODS
- );
- PERSISTENCE_SERVICE_METHODS.forEach(function (m) {
- mockProvider[m].and.returnValue(fakePromise(true));
- });
- mockProvider.listSpaces.and.returnValue(fakePromise([space]));
-
- return mockProvider;
- });
- mockCallback = jasmine.createSpy();
-
- mockQ.all.and.callFake(function (fakePromises) {
- var result = [];
- fakePromises.forEach(function (p) {
- p.then(function (v) {
- result.push(v);
- });
- });
-
- return fakePromise(result);
- });
-
- aggregator = new PersistenceAggregator(mockQ, mockProviders);
- });
-
- it("exposes spaces for all providers", function () {
- aggregator.listSpaces().then(mockCallback);
- expect(mockCallback).toHaveBeenCalledWith(testSpaces);
- });
-
- WRAPPED_METHODS.forEach(function (m) {
- it("redirects " + m + " calls to an appropriate provider", function () {
- testSpaces.forEach(function (space, index) {
- var key = 'key-' + space,
- value = 'val-' + space;
- expect(aggregator[m](space, key, value))
- .toEqual(mockProviders[index][m]());
- expect(mockProviders[index][m])
- .toHaveBeenCalledWith(space, key, value);
- });
- });
- });
-
- });
- }
-);
diff --git a/platform/persistence/elastic/README.md b/platform/persistence/elastic/README.md
deleted file mode 100644
index 413501d9c..000000000
--- a/platform/persistence/elastic/README.md
+++ /dev/null
@@ -1,8 +0,0 @@
-# Elasticsearch Persistence Provider
-An adapter for using Elastic for persistence of user-created objects. The installation function takes the URL for an
-Elasticsearch server as a parameter.
-
-## Installation
-```js
-openmct.install(openmct.plugins.Elasticsearch('http://localhost:9200'))
-``` \ No newline at end of file
diff --git a/platform/persistence/elastic/bundle.js b/platform/persistence/elastic/bundle.js
deleted file mode 100644
index 7fedf6e51..000000000
--- a/platform/persistence/elastic/bundle.js
+++ /dev/null
@@ -1,97 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
- "./src/ElasticPersistenceProvider",
- "./src/ElasticSearchProvider",
- "./src/ElasticIndicator"
-], function (
- ElasticPersistenceProvider,
- ElasticSearchProvider,
- ElasticIndicator
-) {
-
- return {
- name: "platform/persistence/elastic",
- definition: {
- "name": "ElasticSearch Persistence",
- "description": "Adapter to read and write objects using an ElasticSearch instance.",
- "extensions": {
- "components": [
- {
- "provides": "persistenceService",
- "type": "provider",
- "implementation": ElasticPersistenceProvider,
- "depends": [
- "$http",
- "$q",
- "PERSISTENCE_SPACE",
- "ELASTIC_ROOT",
- "ELASTIC_PATH"
- ]
- },
- {
- "provides": "searchService",
- "type": "provider",
- "implementation": ElasticSearchProvider,
- "depends": [
- "$http",
- "ELASTIC_ROOT"
- ]
- }
- ],
- "constants": [
- {
- "key": "PERSISTENCE_SPACE",
- "value": "mct"
- },
- {
- "key": "ELASTIC_ROOT",
- "value": "http://localhost:9200",
- "priority": "fallback"
- },
- {
- "key": "ELASTIC_PATH",
- "value": "mct/_doc",
- "priority": "fallback"
- },
- {
- "key": "ELASTIC_INDICATOR_INTERVAL",
- "value": 15000,
- "priority": "fallback"
- }
- ],
- "indicators": [
- {
- "implementation": ElasticIndicator,
- "depends": [
- "$http",
- "$interval",
- "ELASTIC_ROOT",
- "ELASTIC_INDICATOR_INTERVAL"
- ]
- }
- ]
- }
- }
- };
-});
diff --git a/platform/persistence/elastic/src/ElasticIndicator.js b/platform/persistence/elastic/src/ElasticIndicator.js
deleted file mode 100644
index dcb9e86eb..000000000
--- a/platform/persistence/elastic/src/ElasticIndicator.js
+++ /dev/null
@@ -1,104 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- 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.
- var CONNECTED = {
- text: "Connected",
- glyphClass: "ok",
- statusClass: "s-status-on",
- description: "Connected to the domain object database."
- },
- DISCONNECTED = {
- text: "Disconnected",
- glyphClass: "err",
- statusClass: "s-status-caution",
- description: "Unable to connect to the domain object database."
- },
- PENDING = {
- text: "Checking connection..."
- };
-
- /**
- * Indicator for the current ElasticSearch connection. Polls
- * ElasticSearch at a regular interval (defined by bundle constants)
- * to ensure that the database is available.
- * @constructor
- * @memberof platform/persistence/elastic
- * @implements {Indicator}
- * @param $http Angular's $http service
- * @param $interval Angular's $interval service
- * @param {string} path the URL to poll for elasticsearch availability
- * @param {number} interval the interval, in milliseconds, to poll at
- */
- function ElasticIndicator($http, $interval, path, interval) {
- // Track the current connection state
- var self = this;
-
- this.state = PENDING;
-
- // Callback if the HTTP request to ElasticSearch fails
- function handleError() {
- self.state = DISCONNECTED;
- }
-
- // Callback if the HTTP request succeeds.
- function handleResponse() {
- self.state = CONNECTED;
- }
-
- // Try to connect to ElasticSearch, and update the indicator.
- function updateIndicator() {
- $http.get(path).then(handleResponse, handleError);
- }
-
- // Update the indicator initially, and start polling.
- updateIndicator();
- $interval(updateIndicator, interval, 0, false);
- }
-
- ElasticIndicator.prototype.getCssClass = function () {
- return "c-indicator--clickable icon-suitcase";
- };
-
- ElasticIndicator.prototype.getGlyphClass = function () {
- return this.state.glyphClass;
- };
-
- ElasticIndicator.prototype.getText = function () {
- return this.state.text;
- };
-
- ElasticIndicator.prototype.getDescription = function () {
- return this.state.description;
- };
-
- return ElasticIndicator;
- }
-);
diff --git a/platform/persistence/elastic/src/ElasticPersistenceProvider.js b/platform/persistence/elastic/src/ElasticPersistenceProvider.js
deleted file mode 100644
index 2b5c801d4..000000000
--- a/platform/persistence/elastic/src/ElasticPersistenceProvider.js
+++ /dev/null
@@ -1,168 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * This bundle implements a persistence service which uses ElasticSearch to
- * store documents.
- * @namespace platform/persistence/elastic
- */
-define(
- [],
- function () {
-
- // JSLint doesn't like underscore-prefixed properties,
- // so hide them here.
- var SRC = "_source",
- CONFLICT = 409,
- SEQ_NO = "_seq_no",
- PRIMARY_TERM = "_primary_term";
-
- /**
- * The ElasticPersistenceProvider reads and writes JSON documents
- * (more specifically, domain object models) to/from an ElasticSearch
- * instance.
- * @memberof platform/persistence/elastic
- * @constructor
- * @implements {PersistenceService}
- * @param $http Angular's $http service
- * @param $interval Angular's $interval service
- * @param {string} space the name of the persistence space being served
- * @param {string} root the root of the path to ElasticSearch
- * @param {string} path the path to domain objects within ElasticSearch
- */
- function ElasticPersistenceProvider($http, $q, space, root, path) {
- this.spaces = [space];
- this.revs = {};
- this.$http = $http;
- this.$q = $q;
- this.root = root;
- this.path = path;
- }
-
- // Issue a request using $http; get back the plain JS object
- // from the expected JSON response
- ElasticPersistenceProvider.prototype.request = function (subpath, method, value, params) {
- return this.$http({
- method: method,
- url: this.root + '/' + this.path + '/' + subpath,
- params: params,
- data: value
- }).then(function (response) {
- return response.data;
- }, function (response) {
- return (response || {}).data;
- });
- };
-
- // Shorthand methods for GET/PUT methods
- ElasticPersistenceProvider.prototype.get = function (subpath) {
- return this.request(subpath, "GET");
- };
-
- ElasticPersistenceProvider.prototype.put = function (subpath, value, params) {
- return this.request(subpath, "PUT", value, params);
- };
-
- ElasticPersistenceProvider.prototype.del = function (subpath) {
- return this.request(subpath, "DELETE");
- };
-
- // Handle an update error
- ElasticPersistenceProvider.prototype.handleError = function (response, key) {
- var error = new Error("Persistence error."),
- $q = this.$q;
- if ((response || {}).status === CONFLICT) {
- error.key = "revision";
-
- // Load the updated model, then reject the promise
- return this.get(key).then(function (res) {
- error.model = res[SRC];
-
- return $q.reject(error);
- });
- }
-
- // Reject the promise
- return this.$q.reject(error);
- };
-
- // Get a domain object model out of ElasticSearch's response
- ElasticPersistenceProvider.prototype.getModel = function (response) {
- if (response && response[SRC]) {
- this.revs[response[SEQ_NO]] = response[SEQ_NO];
- this.revs[response[PRIMARY_TERM]] = response[PRIMARY_TERM];
-
- return response[SRC];
- } else {
- return undefined;
- }
- };
-
- // Check the response to a create/update/delete request;
- // track the rev if it's valid, otherwise return false to
- // indicate that the request failed.
- ElasticPersistenceProvider.prototype.checkResponse = function (response, key) {
- if (response && !response.error) {
- this.revs[SEQ_NO] = response[SEQ_NO];
- this.revs[PRIMARY_TERM] = response[PRIMARY_TERM];
-
- return response;
- } else {
- return this.handleError(response, key);
- }
- };
-
- // Public API
- ElasticPersistenceProvider.prototype.listSpaces = function () {
- return this.$q.when(this.spaces);
- };
-
- ElasticPersistenceProvider.prototype.listObjects = function () {
- // Not yet implemented
- return this.$q.when([]);
- };
-
- ElasticPersistenceProvider.prototype.createObject = function (space, key, value) {
- return this.put(key, value).then(this.checkResponse.bind(this));
- };
-
- ElasticPersistenceProvider.prototype.readObject = function (space, key) {
- return this.get(key).then(this.getModel.bind(this));
- };
-
- ElasticPersistenceProvider.prototype.updateObject = function (space, key, value) {
- var self = this;
- function checkUpdate(response) {
- return self.checkResponse(response, key);
- }
-
- return this.put(key, value)
- .then(checkUpdate);
- };
-
- ElasticPersistenceProvider.prototype.deleteObject = function (space, key) {
- return this.del(key).then(this.checkResponse.bind(this));
- };
-
- return ElasticPersistenceProvider;
- }
-);
diff --git a/platform/persistence/elastic/src/ElasticSearchProvider.js b/platform/persistence/elastic/src/ElasticSearchProvider.js
deleted file mode 100644
index 28ce84362..000000000
--- a/platform/persistence/elastic/src/ElasticSearchProvider.js
+++ /dev/null
@@ -1,142 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Module defining ElasticSearchProvider. Created by shale on 07/16/2015.
- */
-define([
-
-], function (
-
-) {
-
- var ID_PROPERTY = '_id',
- SOURCE_PROPERTY = '_source',
- SCORE_PROPERTY = '_score';
-
- /**
- * A search service which searches through domain objects in
- * the filetree using ElasticSearch.
- *
- * @constructor
- * @param $http Angular's $http service, for working with urls.
- * @param ROOT the constant `ELASTIC_ROOT` which allows us to
- * interact with ElasticSearch.
- */
- function ElasticSearchProvider($http, ROOT) {
- this.$http = $http;
- this.root = ROOT;
- }
-
- /**
- * Search for domain objects using elasticsearch as a search provider.
- *
- * @param {String} searchTerm the term to search by.
- * @param {Number} [maxResults] the max number of results to return.
- * @returns {Promise} promise for a modelResults object.
- */
- ElasticSearchProvider.prototype.query = function (searchTerm, maxResults) {
- var searchUrl = this.root + '/_search/',
- params = {},
- provider = this;
-
- searchTerm = this.cleanTerm(searchTerm);
- searchTerm = this.fuzzyMatchUnquotedTerms(searchTerm);
-
- params.q = searchTerm;
- params.size = maxResults;
-
- return this
- .$http({
- method: "GET",
- url: searchUrl,
- params: params
- })
- .then(function success(succesResponse) {
- return provider.parseResponse(succesResponse);
- }, function error() {
- // Gracefully fail.
- return {
- hits: [],
- total: 0
- };
- });
- };
-
- /**
- * Clean excess whitespace from a search term and return the cleaned
- * version.
- *
- * @private
- * @param {string} the search term to clean.
- * @returns {string} search terms cleaned of excess whitespace.
- */
- ElasticSearchProvider.prototype.cleanTerm = function (term) {
- return term.trim().replace(/ +/g, ' ');
- };
-
- /**
- * Add fuzzy matching markup to search terms that are not quoted.
- *
- * The following:
- * hello welcome "to quoted village" have fun
- * will become
- * hello~ welcome~ "to quoted village" have~ fun~
- *
- * @private
- */
- ElasticSearchProvider.prototype.fuzzyMatchUnquotedTerms = function (query) {
- var matchUnquotedSpaces = '\\s+(?=([^"]*"[^"]*")*[^"]*$)',
- matcher = new RegExp(matchUnquotedSpaces, 'g');
-
- return query
- .replace(matcher, '~ ')
- .replace(/$/, '~')
- .replace(/"~+/, '"');
- };
-
- /**
- * Parse the response from ElasticSearch and convert it to a
- * modelResults object.
- *
- * @private
- * @param response a ES response object from $http
- * @returns modelResults
- */
- ElasticSearchProvider.prototype.parseResponse = function (response) {
- var results = response.data.hits.hits,
- searchResults = results.map(function (result) {
- return {
- id: result[ID_PROPERTY],
- model: result[SOURCE_PROPERTY],
- score: result[SCORE_PROPERTY]
- };
- });
-
- return {
- hits: searchResults,
- total: response.data.hits.total
- };
- };
-
- return ElasticSearchProvider;
-});
diff --git a/platform/persistence/elastic/test/ElasticIndicatorSpec.js b/platform/persistence/elastic/test/ElasticIndicatorSpec.js
deleted file mode 100644
index ea4bbd175..000000000
--- a/platform/persistence/elastic/test/ElasticIndicatorSpec.js
+++ /dev/null
@@ -1,109 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../src/ElasticIndicator"],
- function (ElasticIndicator) {
-
- xdescribe("The ElasticSearch status indicator", function () {
- var mockHttp,
- mockInterval,
- testPath,
- testInterval,
- mockPromise,
- indicator;
-
- beforeEach(function () {
- mockHttp = jasmine.createSpyObj("$http", ["get"]);
- mockInterval = jasmine.createSpy("$interval");
- mockPromise = jasmine.createSpyObj("promise", ["then"]);
- testPath = "/test/path";
- testInterval = 12321; // Some number
-
- mockHttp.get.and.returnValue(mockPromise);
-
- indicator = new ElasticIndicator(
- mockHttp,
- mockInterval,
- testPath,
- testInterval
- );
- });
-
- it("polls for changes", function () {
- expect(mockInterval).toHaveBeenCalledWith(
- jasmine.any(Function),
- testInterval,
- 0,
- false
- );
- });
-
- it("has a database icon", function () {
- expect(indicator.getCssClass()).toEqual("icon-suitcase");
- });
-
- it("consults the database at the configured path", function () {
- expect(mockHttp.get).toHaveBeenCalledWith(testPath);
- });
-
- it("changes when the database connection is nominal", function () {
- var initialText = indicator.getText(),
- initialDescrption = indicator.getDescription(),
- initialGlyphClass = indicator.getGlyphClass();
-
- // Nominal just means getting back an object, without
- // an error field.
- mockPromise.then.calls.mostRecent().args[0]({ data: {} });
-
- // Verify that these values changed;
- // don't test for specific text.
- expect(indicator.getText()).not.toEqual(initialText);
- expect(indicator.getGlyphClass()).not.toEqual(initialGlyphClass);
- expect(indicator.getDescription()).not.toEqual(initialDescrption);
-
- // Do check for specific class
- expect(indicator.getGlyphClass()).toEqual("ok");
- });
-
- it("changes when the server cannot be reached", function () {
- var initialText = indicator.getText(),
- initialDescrption = indicator.getDescription(),
- initialGlyphClass = indicator.getGlyphClass();
-
- // Nominal just means getting back an object, without
- // an error field.
- mockPromise.then.calls.mostRecent().args[1]({ data: {} });
-
- // Verify that these values changed;
- // don't test for specific text.
- expect(indicator.getText()).not.toEqual(initialText);
- expect(indicator.getGlyphClass()).not.toEqual(initialGlyphClass);
- expect(indicator.getDescription()).not.toEqual(initialDescrption);
-
- // Do check for specific class
- expect(indicator.getGlyphClass()).toEqual("err");
- });
-
- });
- }
-);
diff --git a/platform/persistence/elastic/test/ElasticPersistenceProviderSpec.js b/platform/persistence/elastic/test/ElasticPersistenceProviderSpec.js
deleted file mode 100644
index 18a3836bd..000000000
--- a/platform/persistence/elastic/test/ElasticPersistenceProviderSpec.js
+++ /dev/null
@@ -1,253 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../src/ElasticPersistenceProvider"],
- function (ElasticPersistenceProvider) {
-
- describe("The ElasticSearch persistence provider", function () {
- var mockHttp,
- mockQ,
- testSpace = "testSpace",
- testRoot = "/test",
- testPath = "db",
- capture,
- provider;
-
- function mockPromise(value) {
- return (value || {}).then ? value : {
- then: function (callback) {
- return mockPromise(callback(value));
- }
- };
- }
-
- beforeEach(function () {
- mockHttp = jasmine.createSpy("$http");
- mockQ = jasmine.createSpyObj("$q", ["when", "reject"]);
-
- mockQ.when.and.callFake(mockPromise);
- mockQ.reject.and.callFake(function (value) {
- return {
- then: function (ignored, callback) {
- return mockPromise(callback(value));
- }
- };
- });
-
- // Capture promise results
- capture = jasmine.createSpy("capture");
-
- provider = new ElasticPersistenceProvider(
- mockHttp,
- mockQ,
- testSpace,
- testRoot,
- testPath
- );
- });
-
- it("reports available spaces", function () {
- provider.listSpaces().then(capture);
- expect(capture).toHaveBeenCalledWith([testSpace]);
- });
-
- // General pattern of tests below is to simulate ElasticSearch's
- // response, verify that request looks like what ElasticSearch
- // would expect, and finally verify that ElasticPersistenceProvider's
- // return values match what is expected.
- it("lists all available documents", function () {
- // Not implemented yet
- provider.listObjects().then(capture);
- expect(capture).toHaveBeenCalledWith([]);
- });
-
- it("allows object creation", function () {
- var model = { someKey: "some value" };
- mockHttp.and.returnValue(mockPromise({
- data: {
- "_id": "abc",
- "_seq_no": 1,
- "_primary_term": 1
- }
- }));
- provider.createObject("testSpace", "abc", model).then(capture);
- expect(mockHttp).toHaveBeenCalledWith({
- url: "/test/db/abc",
- method: "PUT",
- data: model,
- params: undefined
- });
- expect(capture.calls.mostRecent().args[0]).toBeTruthy();
- });
-
- it("allows object models to be read back", function () {
- var model = { someKey: "some value" };
- mockHttp.and.returnValue(mockPromise({
- data: {
- "_id": "abc",
- "_seq_no": 1,
- "_primary_term": 1,
- "_source": model
- }
- }));
- provider.readObject("testSpace", "abc").then(capture);
- expect(mockHttp).toHaveBeenCalledWith({
- url: "/test/db/abc",
- method: "GET",
- params: undefined,
- data: undefined
- });
- expect(capture).toHaveBeenCalledWith(model);
- });
-
- it("allows object update", function () {
- var model = { someKey: "some value" };
-
- // First do a read to populate rev tags...
- mockHttp.and.returnValue(mockPromise({
- data: {
- "_id": "abc",
- "_source": {}
- }
- }));
- provider.readObject("testSpace", "abc");
-
- // Now perform an update
- mockHttp.and.returnValue(mockPromise({
- data: {
- "_id": "abc",
- "_seq_no": 1,
- "_source": {}
- }
- }));
- provider.updateObject("testSpace", "abc", model).then(capture);
- expect(mockHttp).toHaveBeenCalledWith({
- url: "/test/db/abc",
- method: "PUT",
- params: undefined,
- data: model
- });
- expect(capture.calls.mostRecent().args[0]).toBeTruthy();
- });
-
- it("allows object deletion", function () {
- // First do a read to populate rev tags...
- mockHttp.and.returnValue(mockPromise({
- data: {
- "_id": "abc",
- "_source": {}
- }
- }));
- provider.readObject("testSpace", "abc");
-
- // Now perform an update
- mockHttp.and.returnValue(mockPromise({
- data: {
- "_id": "abc",
- "_source": {}
- }
- }));
- provider.deleteObject("testSpace", "abc", {}).then(capture);
- expect(mockHttp).toHaveBeenCalledWith({
- url: "/test/db/abc",
- method: "DELETE",
- params: undefined,
- data: undefined
- });
- expect(capture.calls.mostRecent().args[0]).toBeTruthy();
- });
-
- it("returns undefined when objects are not found", function () {
- // Act like a 404
- mockHttp.and.returnValue({
- then: function (success, fail) {
- return mockPromise(fail());
- }
- });
- provider.readObject("testSpace", "abc").then(capture);
- expect(capture).toHaveBeenCalledWith(undefined);
- });
-
- it("handles rejection due to _seq_no", function () {
- var model = { someKey: "some value" },
- mockErrorCallback = jasmine.createSpy('error');
-
- // First do a read to populate rev tags...
- mockHttp.and.returnValue(mockPromise({
- data: {
- "_id": "abc",
- "_seq_no": 1,
- "_source": {}
- }
- }));
- provider.readObject("testSpace", "abc");
-
- // Now perform an update
- mockHttp.and.returnValue(mockPromise({
- data: {
- "status": 409,
- "error": "Revision error..."
- }
- }));
- provider.updateObject("testSpace", "abc", model).then(
- capture,
- mockErrorCallback
- );
-
- expect(capture).not.toHaveBeenCalled();
- expect(mockErrorCallback).toHaveBeenCalled();
- });
-
- it("handles rejection due to unknown reasons", function () {
- var model = { someKey: "some value" },
- mockErrorCallback = jasmine.createSpy('error');
-
- // First do a read to populate rev tags...
- mockHttp.and.returnValue(mockPromise({
- data: {
- "_id": "abc",
- "_seq_no": 1,
- "_source": {}
- }
- }));
- provider.readObject("testSpace", "abc");
-
- // Now perform an update
- mockHttp.and.returnValue(mockPromise({
- data: {
- "status": 410,
- "error": "Revision error..."
- }
- }));
- provider.updateObject("testSpace", "abc", model).then(
- capture,
- mockErrorCallback
- );
-
- expect(capture).not.toHaveBeenCalled();
- expect(mockErrorCallback).toHaveBeenCalled();
- });
-
- });
- }
-);
diff --git a/platform/persistence/elastic/test/ElasticSearchProviderSpec.js b/platform/persistence/elastic/test/ElasticSearchProviderSpec.js
deleted file mode 100644
index 0c751ed3d..000000000
--- a/platform/persistence/elastic/test/ElasticSearchProviderSpec.js
+++ /dev/null
@@ -1,164 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * SearchSpec. Created by shale on 07/31/2015.
- */
-define([
- '../src/ElasticSearchProvider'
-], function (
- ElasticSearchProvider
-) {
-
- describe('ElasticSearchProvider', function () {
- var $http,
- ROOT,
- provider;
-
- beforeEach(function () {
- $http = jasmine.createSpy('$http');
- ROOT = 'http://localhost:9200';
-
- provider = new ElasticSearchProvider($http, ROOT);
- });
-
- describe('query', function () {
- beforeEach(function () {
- spyOn(provider, 'cleanTerm').and.returnValue('cleanedTerm');
- spyOn(provider, 'fuzzyMatchUnquotedTerms').and.returnValue('fuzzy');
- spyOn(provider, 'parseResponse').and.returnValue('parsedResponse');
- $http.and.returnValue(Promise.resolve({
- data: {
- hits: {
- hits: []
- }
- }
- }));
- });
-
- it('cleans terms and adds fuzzyness', function () {
- return provider.query('hello', 10)
- .then(() => {
- expect(provider.cleanTerm).toHaveBeenCalledWith('hello');
- expect(provider.fuzzyMatchUnquotedTerms)
- .toHaveBeenCalledWith('cleanedTerm');
- });
- });
-
- it('calls through to $http', function () {
- return provider.query('hello', 10).then(() => {
- expect($http).toHaveBeenCalledWith({
- method: 'GET',
- params: {
- q: 'fuzzy',
- size: 10
- },
- url: 'http://localhost:9200/_search/'
- });
- });
- });
-
- it('gracefully fails when http fails', function () {
- $http.and.returnValue(Promise.reject());
-
- return provider
- .query('hello', 10)
- .then(function (results) {
- expect(results).toEqual({
- hits: [],
- total: 0
- });
- });
- });
-
- it('parses and returns when http succeeds', function () {
- $http.and.returnValue(Promise.resolve('successResponse'));
-
- return provider
- .query('hello', 10)
- .then(function (results) {
- expect(provider.parseResponse)
- .toHaveBeenCalledWith('successResponse');
- expect(results).toBe('parsedResponse');
- });
- });
- });
-
- it('can clean terms', function () {
- expect(provider.cleanTerm(' asdhs ')).toBe('asdhs');
- expect(provider.cleanTerm(' and some words'))
- .toBe('and some words');
- expect(provider.cleanTerm('Nice input')).toBe('Nice input');
- });
-
- it('can create fuzzy term matchers', function () {
- expect(provider.fuzzyMatchUnquotedTerms('pwr dvc 43'))
- .toBe('pwr~ dvc~ 43~');
-
- expect(provider.fuzzyMatchUnquotedTerms(
- 'hello welcome "to quoted village" have fun'
- )).toBe(
- 'hello~ welcome~ "to quoted village" have~ fun~'
- );
- });
-
- it('can parse responses', function () {
- var elasticSearchResponse = {
- data: {
- hits: {
- total: 2,
- hits: [
- {
- '_id': 'hit1Id',
- '_source': 'hit1Model',
- '_score': 0.56
- },
- {
- '_id': 'hit2Id',
- '_source': 'hit2Model',
- '_score': 0.34
- }
- ]
- }
- }
- };
-
- expect(provider.parseResponse(elasticSearchResponse))
- .toEqual({
- hits: [
- {
- id: 'hit1Id',
- model: 'hit1Model',
- score: 0.56
- },
- {
- id: 'hit2Id',
- model: 'hit2Model',
- score: 0.34
- }
- ],
- total: 2
- });
- });
- });
-
-});
diff --git a/platform/persistence/queue/README.md b/platform/persistence/queue/README.md
deleted file mode 100644
index b64bba3c3..000000000
--- a/platform/persistence/queue/README.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This bundle provides an Overwrite/Cancel dialog when persisting
-domain objects, if persistence fails. It is meant to be paired
-with a persistence adapter which performs revision-checking
-on update calls, in order to provide the user interface for
-choosing between Overwrite and Cancel in that situation.
diff --git a/platform/persistence/queue/bundle.js b/platform/persistence/queue/bundle.js
deleted file mode 100644
index 0aa0c79e3..000000000
--- a/platform/persistence/queue/bundle.js
+++ /dev/null
@@ -1,82 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
- "./src/QueuingPersistenceCapabilityDecorator",
- "./src/PersistenceQueue",
- "./src/PersistenceFailureController",
- "./res/templates/persistence-failure-dialog.html"
-], function (
- QueuingPersistenceCapabilityDecorator,
- PersistenceQueue,
- PersistenceFailureController,
- persistenceFailureDialogTemplate
-) {
-
- return {
- name: "platform/persistence/queue",
- definition: {
- "extensions": {
- "components": [
- {
- "type": "decorator",
- "provides": "capabilityService",
- "implementation": QueuingPersistenceCapabilityDecorator,
- "depends": [
- "persistenceQueue"
- ]
- }
- ],
- "services": [
- {
- "key": "persistenceQueue",
- "implementation": PersistenceQueue,
- "depends": [
- "$q",
- "$timeout",
- "dialogService",
- "PERSISTENCE_QUEUE_DELAY"
- ]
- }
- ],
- "constants": [
- {
- "key": "PERSISTENCE_QUEUE_DELAY",
- "value": 5
- }
- ],
- "templates": [
- {
- "key": "persistence-failure-dialog",
- "template": persistenceFailureDialogTemplate
- }
- ],
- "controllers": [
- {
- "key": "PersistenceFailureController",
- "implementation": PersistenceFailureController
- }
- ]
- }
- }
- };
-});
diff --git a/platform/persistence/queue/res/templates/persistence-failure-dialog.html b/platform/persistence/queue/res/templates/persistence-failure-dialog.html
deleted file mode 100644
index d477b31f9..000000000
--- a/platform/persistence/queue/res/templates/persistence-failure-dialog.html
+++ /dev/null
@@ -1,52 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
- as represented by the Administrator of the National Aeronautics and Space
- Administration. All rights reserved.
-
- Open MCT is licensed under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0.
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Open MCT includes source code licensed under additional open source
- licenses. See the Open Source Licenses file (LICENSES.md) included with
- this source code distribution or the Licensing information page available
- at runtime from the About dialog for additional information.
--->
-<span ng-controller="PersistenceFailureController as controller">
-
-<div ng-if="ngModel.revised.length > 0">
- External changes have been made to the following objects:
- <ul>
- <li ng-repeat="failure in ngModel.revised">
- <mct-representation key="'label'"
- mct-object="failure.domainObject">
- </mct-representation>
- was modified at
- <b>{{controller.formatTimestamp(failure.error.model.modified)}}</b>
- by
- <i>{{controller.formatUsername(failure.error.model.modifier)}}</i>
- </li>
- </ul>
- You may overwrite these objects, or discard your changes to keep
- the updates that were made externally.
-</div>
-
-<div ng-if="ngModel.unrecoverable.length > 0">
- Changes to these objects could not be saved for unknown reasons:
- <ul>
- <li ng-repeat="failure in ngModel.unrecoverable">
- <mct-representation key="'label'"
- mct-object="failure.domainObject">
- </mct-representation>
- </li>
- </ul>
-</div>
-
-</span>
diff --git a/platform/persistence/queue/src/PersistenceFailureController.js b/platform/persistence/queue/src/PersistenceFailureController.js
deleted file mode 100644
index b4b33d1cc..000000000
--- a/platform/persistence/queue/src/PersistenceFailureController.js
+++ /dev/null
@@ -1,55 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ['moment', './PersistenceFailureConstants'],
- function (moment, Constants) {
-
- /**
- * Controller to support the template to be shown in the
- * dialog shown for persistence failures.
- * @constructor
- * @memberof platform/persistence/queue
- */
- function PersistenceFailureController() {
- }
-
- /**
- * Format a timestamp for display in the dialog.
- * @memberof platform/persistence/queue.PersistenceFailureController#
- */
- PersistenceFailureController.prototype.formatTimestamp = function (timestamp) {
- return moment.utc(timestamp)
- .format(Constants.TIMESTAMP_FORMAT);
- };
-
- /**
- * Format a user name for display in the dialog.
- * @memberof platform/persistence/queue.PersistenceFailureController#
- */
- PersistenceFailureController.prototype.formatUsername = function (username) {
- return username || Constants.UNKNOWN_USER;
- };
-
- return PersistenceFailureController;
- }
-);
diff --git a/platform/persistence/queue/src/PersistenceFailureDialog.js b/platform/persistence/queue/src/PersistenceFailureDialog.js
deleted file mode 100644
index 9e2d187c3..000000000
--- a/platform/persistence/queue/src/PersistenceFailureDialog.js
+++ /dev/null
@@ -1,78 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ['./PersistenceFailureConstants'],
- function (PersistenceFailureConstants) {
-
- var OVERWRITE_CANCEL_OPTIONS = [
- {
- name: "Overwrite",
- key: PersistenceFailureConstants.OVERWRITE_KEY
- },
- {
- name: "Discard",
- key: "cancel"
- }
- ],
- OK_OPTIONS = [{
- name: "OK",
- key: "ok"
- }];
-
- /**
- * Populates a `dialogModel` to pass to `dialogService.getUserChoise`
- * in order to choose between Overwrite and Cancel.
- * @constructor
- * @memberof platform/persistence/queue
- */
- function PersistenceFailureDialog(failures) {
- var revisionErrors = [],
- otherErrors = [];
-
- // Place this failure into an appropriate group
- function categorizeFailure(failure) {
- // Check if the error is due to object revision
- var isRevisionError = ((failure || {}).error || {}).key
- === PersistenceFailureConstants.REVISION_ERROR_KEY;
- // Push the failure into the appropriate group
- (isRevisionError ? revisionErrors : otherErrors).push(failure);
- }
-
- // Separate into revision errors, and other errors
- failures.forEach(categorizeFailure);
-
- return {
- title: "Save Error",
- template: "persistence-failure-dialog",
- model: {
- revised: revisionErrors,
- unrecoverable: otherErrors
- },
- options: revisionErrors.length > 0
- ? OVERWRITE_CANCEL_OPTIONS : OK_OPTIONS
- };
- }
-
- return PersistenceFailureDialog;
- }
-);
diff --git a/platform/persistence/queue/src/PersistenceFailureHandler.js b/platform/persistence/queue/src/PersistenceFailureHandler.js
deleted file mode 100644
index b7a9543d4..000000000
--- a/platform/persistence/queue/src/PersistenceFailureHandler.js
+++ /dev/null
@@ -1,69 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ['./PersistenceFailureDialog', './PersistenceFailureConstants'],
- function (PersistenceFailureDialog, PersistenceFailureConstants) {
-
- /**
- * Handle failures to persist domain object models.
- * @param $q Angular's `$q`
- * @param {DialogService} dialogService the dialog service
- * @constructor
- * @memberof platform/persistence/queue
- */
- function PersistenceFailureHandler($q, dialogService) {
- this.$q = $q;
- this.dialogService = dialogService;
- }
-
- /**
- * Discard failures
- * @param {Array} failures persistence failures, as prepared
- * by PersistenceQueueHandler
- * @memberof platform/persistence/queue.PersistenceFailureHandler#
- */
- PersistenceFailureHandler.prototype.handle = function handleFailures(failures) {
-
- var dialogModel = new PersistenceFailureDialog(failures),
- revisionErrors = dialogModel.model.revised,
- $q = this.$q;
-
- // Discard changes for a failed refresh
- function discard(failure) {
- var persistence =
- failure.domainObject.getCapability('persistence');
-
- return persistence.refresh();
- }
-
- // Discard changes associated with a failed save
- function discardAll(failuresToDiscard) {
- return $q.all(failuresToDiscard.map(discard));
- }
-
- return discardAll(revisionErrors);
- };
-
- return PersistenceFailureHandler;
- }
-);
diff --git a/platform/persistence/queue/src/PersistenceQueue.js b/platform/persistence/queue/src/PersistenceQueue.js
deleted file mode 100644
index d3b5fea85..000000000
--- a/platform/persistence/queue/src/PersistenceQueue.js
+++ /dev/null
@@ -1,76 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [
- './PersistenceQueueImpl',
- './PersistenceQueueHandler',
- './PersistenceFailureHandler'
- ],
- function (
- PersistenceQueueImpl,
- PersistenceQueueHandler,
- PersistenceFailureHandler
- ) {
-
- /**
- * The PersistenceQueue is used by the QueuingPersistenceCapability
- * to aggregate calls for object persistence. These are then issued
- * in a group, such that if some or all are rejected, this result can
- * be shown to the user (again, in a group.)
- *
- * This constructor is exposed as a service, but handles only the
- * wiring of injected dependencies; behavior is implemented in the
- * various component parts.
- *
- * @param $timeout Angular's $timeout
- * @param {PersistenceQueueHandler} handler handles actual
- * persistence when the queue is flushed
- * @param {number} [DELAY] optional; delay in milliseconds between
- * attempts to flush the queue
- * @constructor
- * @memberof platform/persistence/queue
- */
- function PersistenceQueue(
- $q,
- $timeout,
- dialogService,
- PERSISTENCE_QUEUE_DELAY
- ) {
- // Wire up injected dependencies
- return new PersistenceQueueImpl(
- $q,
- $timeout,
- new PersistenceQueueHandler(
- $q,
- new PersistenceFailureHandler(
- $q,
- dialogService
- )
- ),
- PERSISTENCE_QUEUE_DELAY
- );
- }
-
- return PersistenceQueue;
- }
-);
diff --git a/platform/persistence/queue/src/PersistenceQueueHandler.js b/platform/persistence/queue/src/PersistenceQueueHandler.js
deleted file mode 100644
index dd7caa6e2..000000000
--- a/platform/persistence/queue/src/PersistenceQueueHandler.js
+++ /dev/null
@@ -1,115 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- /**
- * Handles actual persistence invocations for queued persistence
- * attempts, in a group. Handling in a group in this manner
- * also allows failure to be handled in a group (e.g. in a single
- * summary dialog.)
- * @param $q Angular's $q, for promises
- * @param {PersistenceFailureHandler} handler to invoke in the event
- * that a persistence attempt fails.
- * @constructor
- * @memberof platform/persistence/queue
- */
- function PersistenceQueueHandler($q, failureHandler) {
- this.$q = $q;
- this.failureHandler = failureHandler;
- }
-
- /**
- * Invoke the persist method on the provided persistence
- * capabilities.
- * @param {Object.<string,PersistenceCapability>} persistences
- * capabilities to invoke, in id->capability pairs.
- * @param {Object.<string,DomainObject>} domainObjects
- * associated domain objects, in id->object pairs.
- * @param {PersistenceQueue} queue the persistence queue,
- * to requeue as necessary
- * @memberof platform/persistence/queue.PersistenceQueueHandler#
- */
- PersistenceQueueHandler.prototype.persist = function (persistences, domainObjects, queue) {
- var ids = Object.keys(persistences),
- $q = this.$q,
- failureHandler = this.failureHandler;
-
- // Handle a group of persistence invocations
- function persistGroup(groupIds, persistenceCaps, domainObjs, pQueue) {
- var failures = [];
-
- // Try to persist a specific domain object
- function tryPersist(id) {
- // Look up its persistence capability from the provided
- // id->persistence object.
- var persistence = persistenceCaps[id],
- domainObject = domainObjs[id];
-
- // Put a domain object back in the queue
- // (e.g. after Overwrite)
- function requeue() {
- return pQueue.put(domainObject, persistence);
- }
-
- // Handle success
- function succeed(value) {
- return value;
- }
-
- // Handle failure (build up a list of failures)
- function fail(error) {
- failures.push({
- id: id,
- persistence: persistence,
- domainObject: domainObject,
- requeue: requeue,
- error: error
- });
-
- return false;
- }
-
- // Invoke the actual persistence capability, then
- // note success or failures
- return persistence.persist().then(succeed, fail);
- }
-
- // Handle any failures from the full operation
- function handleFailure(value) {
- return failures.length > 0
- ? failureHandler.handle(failures)
- : value;
- }
-
- // Try to persist everything, then handle any failures
- return $q.all(groupIds.map(tryPersist)).then(handleFailure);
- }
-
- return persistGroup(ids, persistences, domainObjects, queue);
- };
-
- return PersistenceQueueHandler;
- }
-);
diff --git a/platform/persistence/queue/src/PersistenceQueueImpl.js b/platform/persistence/queue/src/PersistenceQueueImpl.js
deleted file mode 100644
index 3c2a04051..000000000
--- a/platform/persistence/queue/src/PersistenceQueueImpl.js
+++ /dev/null
@@ -1,149 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- /**
- * The PersistenceQueue is used by the QueuingPersistenceCapability
- * to aggregrate calls for object persistence. These are then issued
- * in a group, such that if some or all are rejected, this result can
- * be shown to the user (again, in a group.)
- *
- * This implementation is separate out from PersistenceQueue, which
- * handles the wiring of injected dependencies into an instance of
- * this class.
- *
- * @param $timeout Angular's $timeout
- * @param {PersistenceQueueHandler} handler handles actual
- * persistence when the queue is flushed
- * @param {number} [DELAY] optional; delay in milliseconds between
- * attempts to flush the queue
- * @constructor
- * @memberof platform/persistence/queue
- */
- function PersistenceQueueImpl($q, $timeout, handler, delay) {
-
- this.persistences = {};
- this.objects = {};
- this.lastObservedSize = 0;
- this.activeDefer = $q.defer();
-
- // If no delay is provided, use a default
- this.delay = delay || 0;
- this.handler = handler;
- this.$timeout = $timeout;
- this.$q = $q;
- }
-
- // Schedule a flushing of the queue (that is, plan to flush
- // all objects in the queue)
- PersistenceQueueImpl.prototype.scheduleFlush = function () {
- var self = this,
- $timeout = this.$timeout,
- $q = this.$q,
- handler = this.handler;
-
- // Check if the queue's size has stopped increasing)
- function quiescent() {
- return Object.keys(self.persistences).length
- === self.lastObservedSize;
- }
-
- // Persist all queued objects
- function flush() {
- // Get a local reference to the active promise;
- // this will be replaced with a promise for the next round
- // of persistence calls, so we want to make sure we clear
- // the correct one when this flush completes.
- var flushingDefer = self.activeDefer;
-
- // Clear the active promise for a queue flush
- function clearFlushPromise(value) {
- self.flushPromise = undefined;
- flushingDefer.resolve(value);
-
- return value;
- }
-
- // Persist all queued objects
- self.flushPromise = handler.persist(
- self.persistences,
- self.objects,
- self
- ).then(clearFlushPromise, clearFlushPromise);
-
- // Reset queue, etc.
- self.persistences = {};
- self.objects = {};
- self.lastObservedSize = 0;
- self.pendingTimeout = undefined;
- self.activeDefer = $q.defer();
- }
-
- function maybeFlush() {
- // Timeout fired, so clear it
- self.pendingTimeout = undefined;
- // Only flush when we've stopped receiving updates
- if (quiescent()) {
- flush();
- } else {
- self.scheduleFlush();
- }
-
- // Update lastObservedSize to detect quiescence
- self.lastObservedSize = Object.keys(self.persistences).length;
- }
-
- // If we are already flushing the queue...
- if (self.flushPromise) {
- // Wait until that's over before considering a flush
- self.flushPromise.then(maybeFlush);
- } else {
- // Otherwise, schedule a flush on a timeout (to give
- // a window for other updates to get aggregated)
- self.pendingTimeout = self.pendingTimeout
- || $timeout(maybeFlush, self.delay, false);
- }
-
- return self.activeDefer.promise;
- };
-
- /**
- * Queue persistence of a domain object.
- * @param {DomainObject} domainObject the domain object
- * @param {PersistenceCapability} persistence the object's
- * undecorated persistence capability
- * @returns {Promise} a promise which will resolve upon persistence
- */
- PersistenceQueueImpl.prototype.put = function (domainObject, persistence) {
- var id = domainObject.getId();
- this.persistences[id] = persistence;
- this.objects[id] = domainObject;
-
- return this.scheduleFlush();
- };
-
- return PersistenceQueueImpl;
- }
-);
diff --git a/platform/persistence/queue/src/QueuingPersistenceCapability.js b/platform/persistence/queue/src/QueuingPersistenceCapability.js
deleted file mode 100644
index 30beb0ce0..000000000
--- a/platform/persistence/queue/src/QueuingPersistenceCapability.js
+++ /dev/null
@@ -1,51 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- /**
- * The QueuingPersistenceCapability places `persist` calls in a queue
- * to be handled in batches.
- * @param {PersistenceQueue} queue of persistence calls
- * @param {PersistenceCapability} persistence the wrapped persistence
- * capability
- * @param {DomainObject} domainObject the domain object which exposes
- * the capability
- * @constructor
- * @memberof platform/persistence/queue
- */
- function QueuingPersistenceCapability(queue, persistence, domainObject) {
- var queuingPersistence = Object.create(persistence);
-
- // Override persist calls to queue them instead
- queuingPersistence.persist = function () {
- return queue.put(domainObject, persistence);
- };
-
- return queuingPersistence;
- }
-
- return QueuingPersistenceCapability;
- }
-);
diff --git a/platform/persistence/queue/src/QueuingPersistenceCapabilityDecorator.js b/platform/persistence/queue/src/QueuingPersistenceCapabilityDecorator.js
deleted file mode 100644
index 59a32311e..000000000
--- a/platform/persistence/queue/src/QueuingPersistenceCapabilityDecorator.js
+++ /dev/null
@@ -1,86 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * This bundle decorates the persistence service to handle persistence
- * in batches, and to provide notification of persistence errors in batches
- * as well.
- * @namespace platform/persistence/queue
- */
-define(
- ['./QueuingPersistenceCapability'],
- function (QueuingPersistenceCapability) {
-
- /**
- * Capability decorator. Adds queueing support to persistence
- * capabilities for domain objects, such that persistence attempts
- * will be handled in batches (allowing failure notification to
- * also be presented in batches.)
- *
- * @memberof platform/persistence/queue
- * @constructor
- * @implements {CapabilityService}
- * @param {platform/persistence/queue.PersistenceQueue} persistenceQueue
- * @param {CapabilityService} the decorated capability service
- */
- function QueuingPersistenceCapabilityDecorator(
- persistenceQueue,
- capabilityService
- ) {
- this.persistenceQueue = persistenceQueue;
- this.capabilityService = capabilityService;
- }
-
- QueuingPersistenceCapabilityDecorator.prototype.getCapabilities = function (model, id) {
- var capabilityService = this.capabilityService,
- persistenceQueue = this.persistenceQueue;
-
- function decoratePersistence(capabilities) {
- var originalPersistence = capabilities.persistence;
- if (originalPersistence) {
- capabilities.persistence = function (domainObject) {
- // Get/instantiate the original
- var original =
- (typeof originalPersistence === 'function')
- ? originalPersistence(domainObject)
- : originalPersistence;
-
- // Provide a decorated version
- return new QueuingPersistenceCapability(
- persistenceQueue,
- original,
- domainObject
- );
- };
- }
-
- return capabilities;
- }
-
- return decoratePersistence(
- capabilityService.getCapabilities(model, id)
- );
- };
-
- return QueuingPersistenceCapabilityDecorator;
- }
-);
diff --git a/platform/persistence/queue/test/PersistenceFailureControllerSpec.js b/platform/persistence/queue/test/PersistenceFailureControllerSpec.js
deleted file mode 100644
index 9e1e84df3..000000000
--- a/platform/persistence/queue/test/PersistenceFailureControllerSpec.js
+++ /dev/null
@@ -1,45 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../src/PersistenceFailureController"],
- function (PersistenceFailureController) {
-
- describe("The persistence failure controller", function () {
- var controller;
-
- beforeEach(function () {
- controller = new PersistenceFailureController();
- });
-
- it("converts timestamps to human-readable dates", function () {
- expect(controller.formatTimestamp(402514331000))
- .toEqual("1982-10-03 17:32:11Z");
- });
-
- it("provides default user names", function () {
- expect(controller.formatUsername(undefined))
- .toEqual(jasmine.any(String));
- });
- });
- }
-);
diff --git a/platform/persistence/queue/test/PersistenceFailureDialogSpec.js b/platform/persistence/queue/test/PersistenceFailureDialogSpec.js
deleted file mode 100644
index 8cd5f48c9..000000000
--- a/platform/persistence/queue/test/PersistenceFailureDialogSpec.js
+++ /dev/null
@@ -1,71 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../src/PersistenceFailureDialog", "../src/PersistenceFailureConstants"],
- function (PersistenceFailureDialog, Constants) {
-
- describe("The persistence failure dialog", function () {
- var testFailures,
- dialog;
-
- beforeEach(function () {
- testFailures = [
- {
- error: { key: Constants.REVISION_ERROR_KEY },
- someKey: "abc"
- },
- {
- error: { key: "..." },
- someKey: "def"
- },
- {
- error: { key: Constants.REVISION_ERROR_KEY },
- someKey: "ghi"
- },
- {
- error: { key: Constants.REVISION_ERROR_KEY },
- someKey: "jkl"
- },
- {
- error: { key: "..." },
- someKey: "mno"
- }
- ];
- dialog = new PersistenceFailureDialog(testFailures);
- });
-
- it("categorizes failures", function () {
- expect(dialog.model.revised).toEqual([
- testFailures[0], testFailures[2], testFailures[3]
- ]);
- expect(dialog.model.unrecoverable).toEqual([
- testFailures[1], testFailures[4]
- ]);
- });
-
- it("provides an overwrite option", function () {
- expect(dialog.options[0].key).toEqual(Constants.OVERWRITE_KEY);
- });
- });
- }
-);
diff --git a/platform/persistence/queue/test/PersistenceFailureHandlerSpec.js b/platform/persistence/queue/test/PersistenceFailureHandlerSpec.js
deleted file mode 100644
index 2bcb9bf64..000000000
--- a/platform/persistence/queue/test/PersistenceFailureHandlerSpec.js
+++ /dev/null
@@ -1,82 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../src/PersistenceFailureHandler", "../src/PersistenceFailureConstants"],
- function (PersistenceFailureHandler, Constants) {
-
- describe("The persistence failure handler", function () {
- var mockQ,
- mockDialogService,
- mockFailures,
- mockPromise,
- handler;
-
- function makeMockFailure(id, index) {
- var mockFailure = jasmine.createSpyObj(
- 'failure-' + id,
- ['requeue']
- ),
- mockPersistence = jasmine.createSpyObj(
- 'persistence-' + id,
- ['refresh', 'persist']
- );
- mockFailure.domainObject = jasmine.createSpyObj(
- 'domainObject',
- ['getCapability', 'useCapability', 'getModel']
- );
- mockFailure.domainObject.getCapability.and.callFake(function (c) {
- return (c === 'persistence') && mockPersistence;
- });
- mockFailure.domainObject.getModel.and.returnValue({
- id: id,
- modified: index
- });
- mockFailure.persistence = mockPersistence;
- mockFailure.id = id;
- mockFailure.error = { key: Constants.REVISION_ERROR_KEY };
-
- return mockFailure;
- }
-
- beforeEach(function () {
- mockQ = jasmine.createSpyObj('$q', ['all', 'when']);
- mockDialogService = jasmine.createSpyObj('dialogService', ['getUserChoice']);
- mockFailures = ['a', 'b', 'c'].map(makeMockFailure);
- mockPromise = jasmine.createSpyObj('promise', ['then']);
- mockDialogService.getUserChoice.and.returnValue(mockPromise);
- mockQ.all.and.returnValue(mockPromise);
- mockPromise.then.and.returnValue(mockPromise);
- handler = new PersistenceFailureHandler(mockQ, mockDialogService);
- });
-
- it("discards on handle", function () {
- handler.handle(mockFailures);
- mockFailures.forEach(function (mockFailure) {
- expect(mockFailure.persistence.refresh).toHaveBeenCalled();
- expect(mockFailure.requeue).not.toHaveBeenCalled();
- expect(mockFailure.domainObject.useCapability).not.toHaveBeenCalled();
- });
- });
- });
- }
-);
diff --git a/platform/persistence/queue/test/PersistenceQueueHandlerSpec.js b/platform/persistence/queue/test/PersistenceQueueHandlerSpec.js
deleted file mode 100644
index b78d1ef6a..000000000
--- a/platform/persistence/queue/test/PersistenceQueueHandlerSpec.js
+++ /dev/null
@@ -1,136 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../src/PersistenceQueueHandler"],
- function (PersistenceQueueHandler) {
-
- var TEST_ERROR = { someKey: "some value" };
-
- describe("The persistence queue handler", function () {
- var mockQ,
- mockFailureHandler,
- mockPersistences,
- mockDomainObjects,
- mockQueue,
- mockRejection,
- handler;
-
- function asPromise(value) {
- return (value || {}).then ? value : {
- then: function (callback) {
- return asPromise(callback(value));
- }
- };
- }
-
- function makeMockPersistence(id) {
- var mockPersistence = jasmine.createSpyObj(
- 'persistence-' + id,
- ['persist', 'refresh']
- );
- mockPersistence.persist.and.returnValue(asPromise(true));
-
- return mockPersistence;
- }
-
- function makeMockDomainObject(id) {
- var mockDomainObject = jasmine.createSpyObj(
- 'domainObject-' + id,
- ['getId']
- );
- mockDomainObject.getId.and.returnValue(id);
-
- return mockDomainObject;
- }
-
- beforeEach(function () {
- mockQ = jasmine.createSpyObj('$q', ['all']);
- mockFailureHandler = jasmine.createSpyObj('handler', ['handle']);
- mockQueue = jasmine.createSpyObj('queue', ['put']);
- mockPersistences = {};
- mockDomainObjects = {};
- ['a', 'b', 'c'].forEach(function (id) {
- mockPersistences[id] = makeMockPersistence(id);
- mockDomainObjects[id] = makeMockDomainObject(id);
- });
- mockRejection = jasmine.createSpyObj('rejection', ['then']);
- mockQ.all.and.returnValue(asPromise([]));
- mockRejection.then.and.callFake(function (callback, fallback) {
- return asPromise(fallback({ someKey: "some value" }));
- });
- handler = new PersistenceQueueHandler(mockQ, mockFailureHandler);
- });
-
- it("invokes persistence on all members in the group", function () {
- handler.persist(mockPersistences, mockDomainObjects, mockQueue);
- expect(mockPersistences.a.persist).toHaveBeenCalled();
- expect(mockPersistences.b.persist).toHaveBeenCalled();
- expect(mockPersistences.c.persist).toHaveBeenCalled();
- // No failures in this group
- expect(mockFailureHandler.handle).not.toHaveBeenCalled();
- });
-
- it("handles failures that occur", function () {
- mockPersistences.b.persist.and.returnValue(mockRejection);
- mockPersistences.c.persist.and.returnValue(mockRejection);
- handler.persist(mockPersistences, mockDomainObjects, mockQueue);
- expect(mockFailureHandler.handle).toHaveBeenCalledWith([
- {
- id: 'b',
- persistence: mockPersistences.b,
- domainObject: mockDomainObjects.b,
- requeue: jasmine.any(Function),
- error: TEST_ERROR
- },
- {
- id: 'c',
- persistence: mockPersistences.c,
- domainObject: mockDomainObjects.c,
- requeue: jasmine.any(Function),
- error: TEST_ERROR
- }
- ]);
- });
-
- it("provides a requeue method for failures", function () {
- // This method is needed by PersistenceFailureHandler
- // to allow requeuing of objects for persistence when
- // Overwrite is chosen.
- mockPersistences.b.persist.and.returnValue(mockRejection);
- handler.persist(mockPersistences, mockDomainObjects, mockQueue);
-
- // Verify precondition
- expect(mockQueue.put).not.toHaveBeenCalled();
-
- // Invoke requeue
- mockFailureHandler.handle.calls.mostRecent().args[0][0].requeue();
-
- // Should have returned the object to the queue
- expect(mockQueue.put).toHaveBeenCalledWith(
- mockDomainObjects.b,
- mockPersistences.b
- );
- });
- });
- }
-);
diff --git a/platform/persistence/queue/test/PersistenceQueueImplSpec.js b/platform/persistence/queue/test/PersistenceQueueImplSpec.js
deleted file mode 100644
index 42f55beab..000000000
--- a/platform/persistence/queue/test/PersistenceQueueImplSpec.js
+++ /dev/null
@@ -1,153 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../src/PersistenceQueueImpl"],
- function (PersistenceQueueImpl) {
-
- var TEST_DELAY = 42;
-
- describe("The implemented persistence queue", function () {
- var mockQ,
- mockTimeout,
- mockHandler,
- mockDeferred,
- mockPromise,
- queue;
-
- function makeMockDomainObject(id) {
- var mockDomainObject = jasmine.createSpyObj(
- 'domainObject-' + id,
- ['getId']
- );
- mockDomainObject.getId.and.returnValue(id);
-
- return mockDomainObject;
- }
-
- function makeMockPersistence(id) {
- var mockPersistence = jasmine.createSpyObj(
- 'persistence-' + id,
- ['persist']
- );
-
- return mockPersistence;
- }
-
- beforeEach(function () {
- mockQ = jasmine.createSpyObj('$q', ['when', 'defer']);
- mockTimeout = jasmine.createSpy('$timeout');
- mockHandler = jasmine.createSpyObj('handler', ['persist']);
- mockDeferred = jasmine.createSpyObj('deferred', ['resolve']);
- mockDeferred.promise = jasmine.createSpyObj('promise', ['then']);
- mockPromise = jasmine.createSpyObj('promise', ['then']);
- mockQ.defer.and.returnValue(mockDeferred);
- mockTimeout.and.returnValue({});
- mockHandler.persist.and.returnValue(mockPromise);
- mockPromise.then.and.returnValue(mockPromise);
- queue = new PersistenceQueueImpl(
- mockQ,
- mockTimeout,
- mockHandler,
- TEST_DELAY
- );
- });
-
- it("schedules a timeout to persist objects", function () {
- expect(mockTimeout).not.toHaveBeenCalled();
- queue.put(makeMockDomainObject('a'), makeMockPersistence('a'));
- expect(mockTimeout).toHaveBeenCalledWith(
- jasmine.any(Function),
- TEST_DELAY,
- false
- );
- });
-
- it("does not schedule multiple timeouts for multiple objects", function () {
- // Put three objects in without triggering the timeout;
- // shouldn't schedule multiple timeouts
- queue.put(makeMockDomainObject('a'), makeMockPersistence('a'));
- queue.put(makeMockDomainObject('b'), makeMockPersistence('b'));
- queue.put(makeMockDomainObject('c'), makeMockPersistence('c'));
- expect(mockTimeout.calls.count()).toEqual(1);
- });
-
- it("returns a promise", function () {
- expect(queue.put(makeMockDomainObject('a'), makeMockPersistence('a')))
- .toEqual(mockDeferred.promise);
- });
-
- it("waits for quiescence to proceed", function () {
- // Keep adding objects to the queue between timeouts.
- // Should keep scheduling timeouts instead of resolving.
- queue.put(makeMockDomainObject('a'), makeMockPersistence('a'));
- expect(mockTimeout.calls.count()).toEqual(1);
- mockTimeout.calls.mostRecent().args[0]();
- queue.put(makeMockDomainObject('b'), makeMockPersistence('b'));
- expect(mockTimeout.calls.count()).toEqual(2);
- mockTimeout.calls.mostRecent().args[0]();
- queue.put(makeMockDomainObject('c'), makeMockPersistence('c'));
- expect(mockTimeout.calls.count()).toEqual(3);
- mockTimeout.calls.mostRecent().args[0]();
- expect(mockHandler.persist).not.toHaveBeenCalled();
- });
-
- it("persists upon quiescence", function () {
- // Add objects to the queue, but fire two timeouts afterward
- queue.put(makeMockDomainObject('a'), makeMockPersistence('a'));
- queue.put(makeMockDomainObject('b'), makeMockPersistence('b'));
- queue.put(makeMockDomainObject('c'), makeMockPersistence('c'));
- mockTimeout.calls.mostRecent().args[0]();
- mockTimeout.calls.mostRecent().args[0]();
- expect(mockHandler.persist).toHaveBeenCalled();
- });
-
- it("waits on an active flush, while flushing", function () {
- // Persist some objects
- queue.put(makeMockDomainObject('a'), makeMockPersistence('a'));
- queue.put(makeMockDomainObject('b'), makeMockPersistence('b'));
- mockTimeout.calls.mostRecent().args[0]();
- mockTimeout.calls.mostRecent().args[0]();
- expect(mockTimeout.calls.count()).toEqual(2);
- // Adding a new object should not trigger a new timeout,
- // because we haven't completed the previous flush
- queue.put(makeMockDomainObject('c'), makeMockPersistence('c'));
- expect(mockTimeout.calls.count()).toEqual(2);
- });
-
- it("clears the active flush after it has completed", function () {
- // Persist some objects
- queue.put(makeMockDomainObject('a'), makeMockPersistence('a'));
- queue.put(makeMockDomainObject('b'), makeMockPersistence('b'));
- mockTimeout.calls.mostRecent().args[0]();
- mockTimeout.calls.mostRecent().args[0]();
- expect(mockTimeout.calls.count()).toEqual(2);
- // Resolve the promise from handler.persist
- mockPromise.then.calls.all()[0].args[0](true);
- // Adding a new object should now trigger a new timeout,
- // because we have completed the previous flush
- queue.put(makeMockDomainObject('c'), makeMockPersistence('c'));
- expect(mockTimeout.calls.count()).toEqual(3);
- });
- });
- }
-);
diff --git a/platform/persistence/queue/test/PersistenceQueueSpec.js b/platform/persistence/queue/test/PersistenceQueueSpec.js
deleted file mode 100644
index d8171c7b0..000000000
--- a/platform/persistence/queue/test/PersistenceQueueSpec.js
+++ /dev/null
@@ -1,53 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../src/PersistenceQueue"],
- function (PersistenceQueue) {
-
- describe("The persistence queue", function () {
- var mockQ,
- mockTimeout,
- mockDialogService,
- queue;
-
- beforeEach(function () {
- mockQ = jasmine.createSpyObj("$q", ['defer']);
- mockTimeout = jasmine.createSpy("$timeout");
- mockDialogService = jasmine.createSpyObj(
- 'dialogService',
- ['getUserChoice']
- );
- queue = new PersistenceQueue(mockQ, mockTimeout, mockDialogService);
- });
-
- // PersistenceQueue is just responsible for handling injected
- // dependencies and wiring the PersistenceQueueImpl and its
- // handlers. Functionality is tested there, so our test here is
- // minimal (get back expected interface, no exceptions)
- it("provides a queue with a put method", function () {
- expect(queue.put).toEqual(jasmine.any(Function));
- });
-
- });
- }
-);
diff --git a/platform/persistence/queue/test/QueuingPersistenceCapabilityDecoratorSpec.js b/platform/persistence/queue/test/QueuingPersistenceCapabilityDecoratorSpec.js
deleted file mode 100644
index 24dd75025..000000000
--- a/platform/persistence/queue/test/QueuingPersistenceCapabilityDecoratorSpec.js
+++ /dev/null
@@ -1,83 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../src/QueuingPersistenceCapabilityDecorator"],
- function (QueuingPersistenceCapabilityDecorator) {
-
- describe("A queuing persistence capability decorator", function () {
- var mockQueue,
- mockCapabilityService,
- mockPersistenceConstructor,
- mockPersistence,
- mockDomainObject,
- testModel,
- testId,
- decorator;
-
- beforeEach(function () {
- mockQueue = jasmine.createSpyObj('queue', ['put']);
- mockCapabilityService = jasmine.createSpyObj(
- 'capabilityService',
- ['getCapabilities']
- );
- testModel = { someKey: "some value" };
- testId = 'someId';
- mockPersistence = jasmine.createSpyObj(
- 'persistence',
- ['persist', 'refresh']
- );
- mockPersistenceConstructor = jasmine.createSpy();
- mockDomainObject = jasmine.createSpyObj(
- 'domainObject',
- ['getId']
- );
-
- mockCapabilityService.getCapabilities.and.returnValue({
- persistence: mockPersistenceConstructor
- });
- mockPersistenceConstructor.and.returnValue(mockPersistence);
-
- decorator = new QueuingPersistenceCapabilityDecorator(
- mockQueue,
- mockCapabilityService
- );
- });
-
- // Here, we verify that the decorator wraps the calls it is expected
- // to wrap; remaining responsibility belongs to
- // QueuingPersistenceCapability itself, which has its own tests.
-
- it("delegates to its wrapped service", function () {
- decorator.getCapabilities(testModel, testId);
- expect(mockCapabilityService.getCapabilities)
- .toHaveBeenCalledWith(testModel, testId);
- });
-
- it("wraps its persistence capability's constructor", function () {
- decorator.getCapabilities(testModel).persistence(mockDomainObject);
- expect(mockPersistenceConstructor).toHaveBeenCalledWith(mockDomainObject);
- });
-
- });
- }
-);
diff --git a/platform/persistence/queue/test/QueuingPersistenceCapabilitySpec.js b/platform/persistence/queue/test/QueuingPersistenceCapabilitySpec.js
deleted file mode 100644
index ddbba8b66..000000000
--- a/platform/persistence/queue/test/QueuingPersistenceCapabilitySpec.js
+++ /dev/null
@@ -1,64 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../src/QueuingPersistenceCapability"],
- function (QueuingPersistenceCapability) {
-
- describe("A queuing persistence capability", function () {
- var mockQueue,
- mockPersistence,
- mockDomainObject,
- persistence;
-
- beforeEach(function () {
- mockQueue = jasmine.createSpyObj('queue', ['put']);
- mockPersistence = jasmine.createSpyObj(
- 'persistence',
- ['persist', 'refresh']
- );
- mockDomainObject = {};
- persistence = new QueuingPersistenceCapability(
- mockQueue,
- mockPersistence,
- mockDomainObject
- );
- });
-
- it("puts a request for persistence into the queue on persist", function () {
- // Verify precondition
- expect(mockQueue.put).not.toHaveBeenCalled();
- // Invoke persistence
- persistence.persist();
- // Should have queued
- expect(mockQueue.put).toHaveBeenCalledWith(
- mockDomainObject,
- mockPersistence
- );
- });
-
- it("exposes other methods from the wrapped persistence capability", function () {
- expect(persistence.refresh).toBe(mockPersistence.refresh);
- });
- });
- }
-);
diff --git a/platform/policy/README.md b/platform/policy/README.md
deleted file mode 100644
index 571782c17..000000000
--- a/platform/policy/README.md
+++ /dev/null
@@ -1,93 +0,0 @@
-# Overview
-
-This bundle provides support for policy in Open MCT. Policy can be
-used to limit the applicability of certain actions, or more broadly,
-to provide an extension point for arbitrary decisions.
-
-# Services
-
-This bundle introduces the `policyService`, which may be consulted for
-various decisions which are intended to be open for extension.
-
-The `policyService` has a single method, `allow`, which takes three
-arguments and returns a boolean value (true if policy says this decision
-should be allowed, false if not):
-
-* `category`: A string identifying which kind of decision is being made.
- Typically, this will be a non-plural form of an extension type that is
- being filtered down; for instance, to check whether or not a given
- action should be returned by an `actionService`, one would use the
- `action` category of extension.
-* `candidate`: An object representing the thing which shall or shall not
- be allowed. Usually, this will be an instance of an extension of the
- category defined above.
- * This does need to be the case; additional
- policies which are not specific to any extension may also be defined
- and consulted using unique `category` identifiers. In this case, the
- type of the object delivered for the candidate may be unique to the
- policy type.
-* `context`: An object representing the context in which the decision is
- occurring. Its contents are specific to each policy category.
-* `callback`: Optional; a function to call if the policy decision is
- rejected. This function will be called with the `message` string
- (which may be undefined) of whichever individual policy caused the
- operation to fail.
-
-_Design rationale_: Returning a boolean here limits the amount of
-information that can be conveyed by a policy decision, but has the
-benefit of simplicity. In MCT on the desktop, the policy service
-returned a more complex object with both a boolean status and a string
-message; the string message was used rarely (by only around 15% of
-policy user code) and as such is made optional in the call itself here.
-
-_Design rationale_: Returning a boolean instead of a promise here implies
-that policy decisions must occur synchronously. This limits the logic
-which can be involved in a policy decision, but broadens its applicability;
-policy is meant to be used by a variety of other services to separate out
-a certain category of business logic, and a synchronous response means
-that this capability may be utilized by both synchronous and asynchronous
-services. Additionally, policies will often be used in loops (e.g. to filter
-down a set of applicable actions) where latency will have the result of
-harming the user experience (e.g. the user right-clicks and gets stuck
-waiting for a bunch of policy decisions to complete before a menu showing
-available actions can appear.)
-
-The `policyService` is a composite service; it may be modified by adding
-decorators, aggregators, etc.
-
-## Service Components
-
-The policy service is most often used by decorators for other composite
-services. For instance, this bundle contains a decorator for `actionService`
-which filters down the applicable actions exposed by that service based
-on policy.
-
-# Policy Categories
-
-This bundle introduces `action` as a policy category. Policies of this
-category shall take action instances as their candidate argument, and
-action contexts as their context argument.
-
-# Extensions
-
-This bundle introduces the `policies` category of extension. An extension
-of this category should have both an implementation, as well as the following
-metadata:
-
-* `category`: A string identifying which kind of policy decision this
- effects.
-* `message`: Optional; a human-readable string describing the policy
- decision when it fails.
-
-An extension of this category must also have an implementation which
-takes no arguments to its constructor and provides a single method,
-`allow`, which takes two arguments, `candidate` and `context` (see
-descriptions above under documentation for `actionService`) and returns
-a boolean indicating whether or not it allows the policy decision.
-
-Policy decisions require consensus among all policies; that is, if a
-single policy returns false, then the policy decision as a whole returns
-false. As a consequence, policies should be written in a permissive
-manner; that is, they should be designed to prohibit behavior under a
-specific set of conditions (by returning false), and allow any behavior
-which does not match those conditions (by returning true.)
diff --git a/platform/policy/bundle.js b/platform/policy/bundle.js
deleted file mode 100644
index 0f9ae86d0..000000000
--- a/platform/policy/bundle.js
+++ /dev/null
@@ -1,69 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
- "./src/PolicyActionDecorator",
- "./src/PolicyViewDecorator",
- "./src/PolicyProvider"
-], function (
- PolicyActionDecorator,
- PolicyViewDecorator,
- PolicyProvider
-) {
-
- return {
- name: "platform/policy",
- definition: {
- "name": "Policy Service",
- "description": "Provides support for extension-driven decisions.",
- "sources": "src",
- "extensions": {
- "components": [
- {
- "type": "decorator",
- "provides": "actionService",
- "implementation": PolicyActionDecorator,
- "depends": [
- "policyService"
- ]
- },
- {
- "type": "decorator",
- "provides": "viewService",
- "implementation": PolicyViewDecorator,
- "depends": [
- "policyService"
- ]
- },
- {
- "type": "provider",
- "provides": "policyService",
- "implementation": PolicyProvider,
- "depends": [
- "policies[]"
- ]
- }
- ]
- }
- }
- };
-});
diff --git a/platform/policy/src/PolicyActionDecorator.js b/platform/policy/src/PolicyActionDecorator.js
deleted file mode 100644
index 6416d56e7..000000000
--- a/platform/policy/src/PolicyActionDecorator.js
+++ /dev/null
@@ -1,55 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- /**
- * Filters out actions based on policy.
- * @param {PolicyService} policyService the service which provides
- * policy decisions
- * @param {ActionService} actionService the service to decorate
- * @constructor
- * @memberof platform/policy
- * @implements {ActionService}
- */
- function PolicyActionDecorator(policyService, actionService) {
- this.policyService = policyService;
- this.actionService = actionService;
- }
-
- PolicyActionDecorator.prototype.getActions = function (context) {
- var policyService = this.policyService;
-
- // Check if an action is allowed by policy.
- function allow(action) {
- return policyService.allow('action', action, context);
- }
-
- // Look up actions, filter out the disallowed ones.
- return this.actionService.getActions(context).filter(allow);
- };
-
- return PolicyActionDecorator;
- }
-);
diff --git a/platform/policy/src/PolicyProvider.js b/platform/policy/src/PolicyProvider.js
deleted file mode 100644
index 179b6b42a..000000000
--- a/platform/policy/src/PolicyProvider.js
+++ /dev/null
@@ -1,143 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * This bundle implements the policy service.
- * @namespace platform/policy
- */
-define(
- [],
- function () {
-
- /**
- * A policy is a participant in decision-making policies. Policies
- * are divided into categories (identified symbolically by strings);
- * within a given category, every given policy-driven decision will
- * occur by consulting all available policies and requiring their
- * collective consent (that is, every individual policy has the
- * power to reject the decision entirely.)
- *
- * @interface Policy
- * @template C, X
- */
-
- /**
- * Check if this policy allows the described decision. The types
- * of the arguments expected here vary depending on policy category.
- *
- * @method Policy#allow
- * @template C, X
- * @param {C} candidate the thing to allow or disallow
- * @param {X} context the context in which the decision occurs
- * @returns {boolean} false if disallowed; otherwise, true
- */
-
- /**
- * The `policyService` handles decisions about what things
- * are and are not allowed in certain contexts.
- * @interface PolicyService
- */
-
- /**
- * Check whether or not a certain decision is allowed by
- * policy.
- * @param {string} category a machine-readable identifier
- * for the kind of decision being made
- * @param candidate the object about which the decision is
- * being made
- * @param context the context in which the decision occurs
- * @param {Function} [callback] callback to invoke with a
- * string message describing the reason a decision
- * was disallowed (if its disallowed)
- * @returns {boolean} true if the decision is allowed,
- * otherwise false.
- * @method PolicyService#allow
- */
-
- /**
- * Provides an implementation of `policyService` which consults
- * various policy extensions to determine whether or not a specific
- * decision should be allowed.
- * @memberof platform/policy
- * @constructor
- * @implements {PolicyService}
- * @param {Policy[]} policies the policies to enforce
- */
- function PolicyProvider(policies) {
- var policyMap = {};
-
- // Instantiate a policy. Mostly just a constructor call, but
- // we also track the message (which was provided as metadata
- // along with the constructor) so that we can expose this later.
- function instantiate(Policy) {
- var policy = Object.create(new Policy());
- policy.message = Policy.message;
-
- return policy;
- }
-
- // Add a specific policy to the map for later lookup,
- // according to its category. Note that policy extensions are
- // provided as constructors, so they are instantiated here.
- function addToMap(Policy) {
- var category = (Policy || {}).category;
- if (category) {
- // Create a new list for that category if needed...
- policyMap[category] = policyMap[category] || [];
- // ...and put an instance of this policy in that list.
- policyMap[category].push(instantiate(Policy));
- }
- }
-
- // Populate the map for subsequent lookup
- policies.forEach(addToMap);
- this.policyMap = policyMap;
- }
-
- PolicyProvider.prototype.allow = function (category, candidate, context, callback) {
- var policyList = this.policyMap[category] || [],
- i;
-
- // Iterate through policies. We do this instead of map or
- // forEach so that we can return immediately if a policy
- // chooses to disallow this decision.
- for (i = 0; i < policyList.length; i += 1) {
- // Consult the policy...
- if (!policyList[i].allow(candidate, context)) {
- // ...it disallowed, so pass its message to
- // the callback (if any)
- if (callback) {
- callback(policyList[i].message);
- }
-
- // And return the failed result.
- return false;
- }
- }
-
- // No policy disallowed this decision.
- return true;
- };
-
- return PolicyProvider;
- }
-);
diff --git a/platform/policy/src/PolicyViewDecorator.js b/platform/policy/src/PolicyViewDecorator.js
deleted file mode 100644
index 7a9b114ff..000000000
--- a/platform/policy/src/PolicyViewDecorator.js
+++ /dev/null
@@ -1,55 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- /**
- * Filters out views based on policy.
- * @param {PolicyService} policyService the service which provides
- * policy decisions
- * @param {ViewService} viewService the service to decorate
- * @constructor
- * @memberof platform/policy
- * @implements {ViewService}
- */
- function PolicyViewDecorator(policyService, viewService) {
- this.policyService = policyService;
- this.viewService = viewService;
- }
-
- PolicyViewDecorator.prototype.getViews = function (domainObject) {
- var policyService = this.policyService;
-
- // Check if an action is allowed by policy.
- function allow(view) {
- return policyService.allow('view', view, domainObject);
- }
-
- // Look up actions, filter out the disallowed ones.
- return this.viewService.getViews(domainObject).filter(allow);
- };
-
- return PolicyViewDecorator;
- }
-);
diff --git a/platform/policy/test/PolicyActionDecoratorSpec.js b/platform/policy/test/PolicyActionDecoratorSpec.js
deleted file mode 100644
index 8ddc8b699..000000000
--- a/platform/policy/test/PolicyActionDecoratorSpec.js
+++ /dev/null
@@ -1,98 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../src/PolicyActionDecorator"],
- function (PolicyActionDecorator) {
-
- describe("The policy action decorator", function () {
- var mockPolicyService,
- mockActionService,
- testContext,
- testActions,
- decorator;
-
- beforeEach(function () {
- mockPolicyService = jasmine.createSpyObj(
- 'policyService',
- ['allow']
- );
- mockActionService = jasmine.createSpyObj(
- 'actionService',
- ['getActions']
- );
-
- // Content of actions should be irrelevant to this
- // decorator, so just give it some objects to pass
- // around.
- testActions = [
- { someKey: "a" },
- { someKey: "b" },
- { someKey: "c" }
- ];
- testContext = { someKey: "some value" };
-
- mockActionService.getActions.and.returnValue(testActions);
- mockPolicyService.allow.and.returnValue(true);
-
- decorator = new PolicyActionDecorator(
- mockPolicyService,
- mockActionService
- );
- });
-
- it("delegates to its decorated action service", function () {
- decorator.getActions(testContext);
- expect(mockActionService.getActions)
- .toHaveBeenCalledWith(testContext);
- });
-
- it("provides actions from its decorated action service", function () {
- // Mock policy service allows everything by default,
- // so everything should be returned
- expect(decorator.getActions(testContext))
- .toEqual(testActions);
- });
-
- it("consults the policy service for each candidate action", function () {
- decorator.getActions(testContext);
- testActions.forEach(function (testAction) {
- expect(mockPolicyService.allow).toHaveBeenCalledWith(
- 'action',
- testAction,
- testContext
- );
- });
- });
-
- it("filters out policy-disallowed actions", function () {
- // Disallow the second action
- mockPolicyService.allow.and.callFake(function (cat, candidate) {
- return candidate.someKey !== 'b';
- });
- expect(decorator.getActions(testContext))
- .toEqual([testActions[0], testActions[2]]);
- });
-
- });
- }
-);
diff --git a/platform/policy/test/PolicyProviderSpec.js b/platform/policy/test/PolicyProviderSpec.js
deleted file mode 100644
index 7304030c8..000000000
--- a/platform/policy/test/PolicyProviderSpec.js
+++ /dev/null
@@ -1,128 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../src/PolicyProvider"],
- function (PolicyProvider) {
-
- describe("The policy provider", function () {
- var testPolicies,
- mockPolicies,
- mockPolicyConstructors,
- testCandidate,
- testContext,
- provider;
-
- beforeEach(function () {
- testPolicies = [
- {
- category: "a",
- message: "some message",
- result: true
- },
- {
- category: "a",
- result: true
- },
- {
- category: "a",
- result: true
- },
- {
- category: "b",
- message: "some message",
- result: true
- },
- {
- category: "b",
- result: true
- },
- {
- category: "b",
- result: true
- }
- ];
- mockPolicies = testPolicies.map(function (p) {
- var mockPolicy = jasmine.createSpyObj("policy", ['allow']);
- mockPolicy.allow.and.callFake(function () {
- return p.result;
- });
-
- return mockPolicy;
- });
- mockPolicyConstructors = testPolicies.map(function (p, i) {
- var mockPolicyConstructor = jasmine.createSpy();
- mockPolicyConstructor.and.returnValue(mockPolicies[i]);
- mockPolicyConstructor.message = p.message;
- mockPolicyConstructor.category = p.category;
-
- return mockPolicyConstructor;
- });
-
- testCandidate = { someKey: "some value" };
- testContext = { someOtherKey: "some other value" };
-
- provider = new PolicyProvider(mockPolicyConstructors);
- });
-
- it("has an allow method", function () {
- expect(provider.allow).toEqual(jasmine.any(Function));
- });
-
- it("consults all relevant policies", function () {
- provider.allow("a", testCandidate, testContext);
- expect(mockPolicies[0].allow)
- .toHaveBeenCalledWith(testCandidate, testContext);
- expect(mockPolicies[1].allow)
- .toHaveBeenCalledWith(testCandidate, testContext);
- expect(mockPolicies[2].allow)
- .toHaveBeenCalledWith(testCandidate, testContext);
- expect(mockPolicies[3].allow)
- .not.toHaveBeenCalled();
- expect(mockPolicies[4].allow)
- .not.toHaveBeenCalled();
- expect(mockPolicies[5].allow)
- .not.toHaveBeenCalled();
- });
-
- it("allows what all policies allow", function () {
- expect(provider.allow("a", testCandidate, testContext))
- .toBeTruthy();
- });
-
- it("disallows what any one policy disallows", function () {
- testPolicies[1].result = false;
- expect(provider.allow("a", testCandidate, testContext))
- .toBeFalsy();
- });
-
- it("provides a message for policy failure, when available", function () {
- var mockCallback = jasmine.createSpy();
- testPolicies[0].result = false;
- expect(provider.allow("a", testCandidate, testContext, mockCallback))
- .toBeFalsy();
- expect(mockCallback).toHaveBeenCalledWith(testPolicies[0].message);
- });
-
- });
- }
-);
diff --git a/platform/policy/test/PolicyViewDecoratorSpec.js b/platform/policy/test/PolicyViewDecoratorSpec.js
deleted file mode 100644
index 2ed795144..000000000
--- a/platform/policy/test/PolicyViewDecoratorSpec.js
+++ /dev/null
@@ -1,102 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../src/PolicyViewDecorator"],
- function (PolicyViewDecorator) {
-
- describe("The policy view decorator", function () {
- var mockPolicyService,
- mockViewService,
- mockDomainObject,
- testViews,
- decorator;
-
- beforeEach(function () {
- mockPolicyService = jasmine.createSpyObj(
- 'policyService',
- ['allow']
- );
- mockViewService = jasmine.createSpyObj(
- 'viewService',
- ['getViews']
- );
- mockDomainObject = jasmine.createSpyObj(
- 'domainObject',
- ['getId']
- );
-
- // Content of actions should be irrelevant to this
- // decorator, so just give it some objects to pass
- // around.
- testViews = [
- { someKey: "a" },
- { someKey: "b" },
- { someKey: "c" }
- ];
-
- mockDomainObject.getId.and.returnValue('xyz');
- mockViewService.getViews.and.returnValue(testViews);
- mockPolicyService.allow.and.returnValue(true);
-
- decorator = new PolicyViewDecorator(
- mockPolicyService,
- mockViewService
- );
- });
-
- it("delegates to its decorated view service", function () {
- decorator.getViews(mockDomainObject);
- expect(mockViewService.getViews)
- .toHaveBeenCalledWith(mockDomainObject);
- });
-
- it("provides views from its decorated view service", function () {
- // Mock policy service allows everything by default,
- // so everything should be returned
- expect(decorator.getViews(mockDomainObject))
- .toEqual(testViews);
- });
-
- it("consults the policy service for each candidate view", function () {
- decorator.getViews(mockDomainObject);
- testViews.forEach(function (testView) {
- expect(mockPolicyService.allow).toHaveBeenCalledWith(
- 'view',
- testView,
- mockDomainObject
- );
- });
- });
-
- it("filters out policy-disallowed views", function () {
- // Disallow the second action
- mockPolicyService.allow.and.callFake(function (cat, candidate) {
- return candidate.someKey !== 'b';
- });
- expect(decorator.getViews(mockDomainObject))
- .toEqual([testViews[0], testViews[2]]);
- });
-
- });
- }
-);
diff --git a/platform/representation/README.md b/platform/representation/README.md
deleted file mode 100644
index 493a04bd0..000000000
--- a/platform/representation/README.md
+++ /dev/null
@@ -1,120 +0,0 @@
-This bundle introduces the notion of "representations" to Open MCT,
-primarily via an Angular directive, `mct-representation`.
-
-A representation is used to display domain objects as Angular templates.
-
-# Extension Categories
-
-This bundle introduces four new categories of extension:
-
-* `templates`: Reusable Angular templates. This category of extension is
- present to support the `mct-include` directive, which in turn is present
- to allow templates to be loaded from across bundles, without knowing
- their path ahead of time. A template has the following fields:
- * `key`: The machine-readable name which identifies this template,
- matched against the value given to the `key` attribute of the
- `mct-include` directive.
- * `templateUrl`: The path to the relevant Angular template. This
- path is relative to the bundle's resources directory.
-* `representations`: Ways of representing a domain object. A representation
- is defined with the following fields:
- * `key`: The machine-readable name which identifies the representation.
- * `templateUrl`: The path to the representation's Angular template. This
- path is relative to the bundle's resources directory.
- * `uses`: An array of capability names. Indicates that this representation
- intends to use those capabilities of a domain object (via a
- `useCapability` call), and expects to find the latest results of
- that `useCapability` call in the scope of the presented template (under
- the same name as the capability itself.)
- * `gestures`: An array of keys identifying gestures which should be
- available upon this representation. Examples of gestures include
- "drag" (for representations that should act as draggable sources
- for drag-drop operations) and "menu" (for representations which
- should show a domain-object-specific context menu on right-click.)
-* `views`: A view is a representation with a visible identity to the user
- (e.g. something they can switch among in the view menu.) A view
- supports the same fields as a representation, and additionally:
- * `name`: The human-readable name of the view.
- * `glyph`: A character to display as an icon for this view.
- * `description`: The human-readable description of the view.
-* `gestures`: A gesture is a user action which can be taken upon a
- representation of a domain object. Gestures are described by:
- * `key`: The machine-readable name used to look up the gesture.
- * `implementation`: The class (relative to the bundle's sources
- directory) which implements the gesture. This is instantiated once
- per representation that uses the gesture. This class will
- receive the jqLite-wrapped `mct-representation` element and the
- domain object being represented as arguments, and should do any
- necessary "wiring" (e.g. listening for events) during its
- constructor call. This class may also expose an optional `destroy()`
- method which should be called when the gesture should be removed,
- to avoid memory leaks by way of unremoved listeners.
-
-
-# Extensions
-
-## Directives
-
-* `mct-include`: Includes a template by symbolic key; used to augment the
- capability of Angular's `ng-include`, which loads templates by path.
- Takes three attributes as Angular expressions:
- * `key`: The symbolic identifier of the template to load, matched
- against keys defined in extensions of category `templates`.
- Note that this is an Angular expression, so internal quotes
- may be necessary (see documentation of `ng-include`, which has the same
- "gotcha" for URLs.)
- * `ng-model`: Optional (and not often used); a model which should appear
- in the included template's scope, for it to modify. The specific
- interpretation of this attribute will vary depending on the included
- template.
- * `parameters`: Optional (and not often used); as `ng-model`, except the
- intent is to provide information about how to display the included
- template (e.g. "title", "color"). The specific interpretation of
- this attribute will vary depending on the included template.
-* `mct-representation`: Similar to `mct-include`, except the template to
- include is specifically a representation of a domain object.
- * `key`: As used in `mct-include`, except it will refer to an extension
- or category `representations` or of `views`.
- * `mct-object`: An Angular expression; the domain object to be
- represented.
- * `parameters`: As defined for `mct-include`.
-
-### Examples
-
- <mct-include key="'status-bar'"></mct-include>
- <mct-representation key="'grid-item'"></mct-representation>
-
- <mct-include key="'title-bar'"
- parameters="{title: 'Hello', tooltip: 'Hello, world.'}">
- </mct-include>
-
-
-## Components
-
-* `gestureService`: A provider of type `gestureService` is included to
- remove the need to depend on `gestures[]` directly; instead, the
- gesture service can be used to add/remove gestures in groups. This is
- present primarily for bundle-internal use (it is used by the
- `mct-representation` directive) but it is exposed as a service component
- for convenience.
-
-## Gestures
-
-In addition to introducing `gestures` as a category of extension, this bundle
-introduces three specific gestures as "built in" options, listed by key:
-
-* `drag`: Representations with this gesture can serve as drag sources for
- drag-drop domain object composition.
-* `drop`: Representations with this gesture can serve as drop targets for
- drag-drop domain object composition.
- * When a drop occurs, an `mctDrop` event will be broadcast with two
- arguments (in addition to Angular's event object): The domain object
- identifier for the dropped object, and the position (with `x` and `y`
- properties in pixels) of the drop, relative to the top-left of the
- representation which features the drop gesture.
-* `menu`: Representations with this gesture will provide a custom context
- menu (instead of the browser default).
- * It should be noted that this gesture does _not_ define the appearance
- or functionality of this menu; rather, it simply adds a
- representation of key `context-menu` to the document at an appropriate
- location. This representation will be supplied by the commonUI bundle.
diff --git a/platform/representation/bundle.js b/platform/representation/bundle.js
deleted file mode 100644
index 915eb306b..000000000
--- a/platform/representation/bundle.js
+++ /dev/null
@@ -1,144 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
- "./src/MCTInclude",
- "./src/MCTRepresentation",
- "./src/gestures/DragGesture",
- "./src/gestures/DropGesture",
- "./src/gestures/GestureProvider",
- "./src/gestures/GestureRepresenter",
- "./src/services/DndService",
- "./src/TemplateLinker",
- "./src/TemplatePrefetcher"
-], function (
- MCTInclude,
- MCTRepresentation,
- DragGesture,
- DropGesture,
- GestureProvider,
- GestureRepresenter,
- DndService,
- TemplateLinker,
- TemplatePrefetcher
-) {
-
- return {
- name: "platform/representation",
- definition: {
- "extensions": {
- "directives": [
- {
- "key": "mctInclude",
- "implementation": MCTInclude,
- "depends": [
- "templates[]",
- "templateLinker"
- ]
- },
- {
- "key": "mctRepresentation",
- "implementation": MCTRepresentation,
- "depends": [
- "representations[]",
- "views[]",
- "representers[]",
- "$q",
- "templateLinker",
- "$log"
- ]
- }
- ],
- "gestures": [
- {
- "key": "drag",
- "implementation": DragGesture,
- "depends": [
- "$log",
- "dndService"
- ]
- },
- {
- "key": "drop",
- "implementation": DropGesture,
- "depends": [
- "dndService",
- "$q"
- ]
- }
- ],
- "components": [
- {
- "provides": "gestureService",
- "type": "provider",
- "implementation": GestureProvider,
- "depends": [
- "gestures[]"
- ]
- }
- ],
- "representers": [
- {
- "implementation": GestureRepresenter,
- "depends": [
- "gestureService"
- ]
- }
- ],
- "services": [
- {
- "key": "dndService",
- "implementation": DndService,
- "depends": [
- "$log"
- ]
- },
- {
- "key": "templateLinker",
- "implementation": TemplateLinker,
- "depends": [
- "$templateRequest",
- "$sce",
- "$compile",
- "$log"
- ],
- "comment": "For internal use by mct-include and mct-representation."
- }
- ],
- "runs": [
- {
- "priority": "mandatory",
- "implementation": TemplatePrefetcher,
- "depends": [
- "templateLinker",
- "templates[]",
- "views[]",
- "representations[]",
- "controls[]",
- "containers[]"
- ]
- }
- ]
- }
- }
- };
-});
diff --git a/platform/representation/src/MCTInclude.js b/platform/representation/src/MCTInclude.js
deleted file mode 100644
index c2c308975..000000000
--- a/platform/representation/src/MCTInclude.js
+++ /dev/null
@@ -1,102 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Module defining MCTInclude. Created by vwoeltje on 11/7/14.
- */
-define(
- [],
- function () {
-
- /**
- * Defines the mct-include directive. This acts like the
- * ng-include directive, except it accepts a symbolic
- * key which can be exposed by bundles, instead of requiring
- * an explicit path.
- *
- * This directive uses two-way binding for three attributes:
- *
- * * `key`, matched against the key of a defined template extension
- * in order to determine which actual template to include.
- * * `ng-model`, populated as `ngModel` in the loaded template's
- * scope; used for normal ng-model purposes (e.g. if the
- * included template is meant to two-way bind to a data model.)
- * * `parameters`, used to communicate display parameters to
- * the included template (e.g. title.) The difference between
- * `parameters` and `ngModel` is intent: Both are two-way
- * bound, but `ngModel` is useful for data models (more like
- * an output) and `parameters` is meant to be useful for
- * display parameterization (more like an input.)
- *
- * @memberof platform/representation
- * @constructor
- * @param {TemplateDefinition[]} templates an array of
- * template extensions
- */
- function MCTInclude(templates, templateLinker) {
- var templateMap = {};
-
- function link(scope, element) {
- var changeTemplate = templateLinker.link(
- scope,
- element,
- scope.key && templateMap[scope.key]
- );
-
- scope.$watch('key', function (newKey, oldKey) {
- if (newKey !== oldKey) {
- changeTemplate(newKey && templateMap[newKey]);
- }
- });
- }
-
- // Prepopulate templateMap for easy look up by key
- templates.forEach(function (template) {
- var key = template.key;
- // First found should win (priority ordering)
- templateMap[key] =
- templateMap[key] || template;
- });
-
- return {
- // Only show at the element level
- restrict: "E",
-
- // Use the included controller to populate scope
- link: link,
-
- // May hide the element, so let other directives act first
- priority: -1000,
-
- // Two-way bind key, ngModel, and parameters
- scope: {
- key: "=",
- ngModel: "=",
- parameters: "="
- }
- };
- }
-
- return MCTInclude;
- }
-);
-
diff --git a/platform/representation/src/MCTRepresentation.js b/platform/representation/src/MCTRepresentation.js
deleted file mode 100644
index 0bed90266..000000000
--- a/platform/representation/src/MCTRepresentation.js
+++ /dev/null
@@ -1,308 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * This bundle implements the directives for representing domain objects
- * as Angular-managed HTML.
- * @namespace platform/representation
- */
-define(
- [],
- function () {
-
- /**
- * Defines the mct-representation directive. This may be used to
- * present domain objects as HTML (with event wiring), with the
- * specific representation being mapped to a defined extension
- * (as defined in either the `representation` category-of-extension,
- * or the `views` category-of-extension.)
- *
- * This directive uses two-way binding for three attributes:
- *
- * * `key`, matched against the key of a defined template extension
- * in order to determine which actual template to include.
- * * `mct-object`, populated as `domainObject` in the loaded
- * template's scope. This is the domain object being
- * represented as HTML by this directive.
- * * `parameters`, used to communicate display parameters to
- * the included template (e.g. title.)
- *
- * @memberof platform/representation
- * @constructor
- * @param {RepresentationDefinition[]} representations an array of
- * representation extensions
- * @param {ViewDefinition[]} views an array of view extensions
- */
- function MCTRepresentation(representations, views, representers, $q, templateLinker, $log) {
- var representationMap = {};
-
- // Assemble all representations and views
- // The distinction between views and representations is
- // not important here (view is-a representation)
- representations.concat(views).forEach(function (representation) {
- var key = representation.key;
-
- // Store the representation
- representationMap[key] = representationMap[key] || [];
- representationMap[representation.key].push(representation);
- });
-
- // Look up a matching representation for this domain object
- function lookup(key, domainObject) {
- var candidates = representationMap[key] || [],
- type,
- i;
- // Filter candidates by object type
- for (i = 0; i < candidates.length; i += 1) {
- type = candidates[i].type;
- if (!type || !domainObject
- || domainObject.getCapability('type').instanceOf(type)) {
- return candidates[i];
- }
- }
- }
-
- function link($scope, element, attrs) {
- var activeRepresenters = representers.map(function (Representer) {
- return new Representer($scope, element, attrs);
- }),
- toClear = [], // Properties to clear out of scope on change
- counter = 0,
- couldRepresent = false,
- lastIdPath = [],
- lastKey,
- mutationListener,
- changeTemplate = templateLinker.link($scope, element);
-
- // Populate scope with any capabilities indicated by the
- // representation's extension definition
- function refreshCapabilities() {
- var domainObject = $scope.domainObject,
- representation = lookup($scope.key, domainObject),
- uses = ((representation || {}).uses || []),
- myCounter = counter;
-
- if (mutationListener) {
- mutationListener();
- mutationListener = undefined;
- }
-
- if (domainObject) {
- mutationListener = domainObject
- .getCapability('mutation')
- .listen(refreshCapabilities);
-
- // Update model
- $scope.model = domainObject.getModel();
-
- // Provide any of the capabilities requested
- uses.forEach(function (used) {
- $log.debug([
- "Requesting capability ",
- used,
- " for representation ",
- $scope.key
- ].join(""));
-
- $q.when(
- domainObject.useCapability(used)
- ).then(function (c) {
- // Avoid clobbering capabilities from
- // subsequent representations;
- // Angular reuses scopes.
- if (counter === myCounter) {
- $scope[used] = c;
- }
- });
- });
- }
- }
-
- // Destroy (deallocate any resources associated with) any
- // active representers.
- function destroyRepresenters() {
- activeRepresenters.forEach(function (activeRepresenter) {
- activeRepresenter.destroy();
- });
- }
-
- function unchanged(canRepresent, idPath, key) {
- return (canRepresent === couldRepresent)
- && (key === lastKey)
- && (idPath.length === lastIdPath.length)
- && idPath.every(function (id, i) {
- return id === lastIdPath[i];
- });
- }
-
- function getIdPath(domainObject) {
- if (!domainObject) {
- return [];
- }
-
- if (!domainObject.hasCapability('context')) {
- return [domainObject.getId()];
- }
-
- return domainObject.getCapability('context')
- .getPath().map(function (pathObject) {
- return pathObject.getId();
- });
- }
-
- // General-purpose refresh mechanism; should set up the scope
- // as appropriate for current representation key and
- // domain object.
- function refresh() {
- var domainObject = $scope.domainObject,
- representation = lookup($scope.key, domainObject),
- uses = ((representation || {}).uses || []),
- canRepresent = Boolean(representation && domainObject),
- idPath = getIdPath(domainObject),
- key = $scope.key;
-
- if (unchanged(canRepresent, idPath, key)) {
- return;
- }
-
- // Create an empty object named "representation", for this
- // representation to store local variables into.
- $scope.representation = {};
-
- // Change templates (passing in undefined to clear
- // if we don't have enough info to show a template.)
- changeTemplate(canRepresent ? representation : undefined);
-
- // Any existing representers are no longer valid; release them.
- destroyRepresenters();
-
- // Log if a key was given, but no matching representation
- // was found.
- if (!representation && $scope.key) {
- $log.warn("No representation found for " + $scope.key);
- }
-
- // Clear out the scope from the last representation
- toClear.forEach(function (property) {
- delete $scope[property];
- });
-
- // To allow simplified change detection next time around
- couldRepresent = canRepresent;
- lastIdPath = idPath;
- lastKey = key;
-
- // Populate scope with fields associated with the current
- // domain object (if one has been passed in)
- if (canRepresent) {
- // Track how many representations we've made in this scope,
- // to ensure that the correct representations are matched to
- // the correct object/key pairs.
- counter += 1;
-
- // Initialize any capabilities
- refreshCapabilities();
-
- // Also provide the view configuration,
- // for the specific view
- $scope.configuration =
- ($scope.model.configuration || {})[$scope.key] || {};
-
- // Finally, wire up any additional behavior (such as
- // gestures) associated with this representation.
- activeRepresenters.forEach(function (representer) {
- representer.represent(representation, domainObject);
- });
-
- // Track which properties we want to clear from scope
- // next change object/key pair changes
- toClear = uses.concat(['model']);
- }
- }
-
- // Update the representation when the key changes (e.g. if a
- // different representation has been selected)
- $scope.$watch("key", refresh);
-
- // Also update when the represented domain object changes
- // (to a different object)
- $scope.$watch("domainObject", refresh);
-
- // Make sure any resources allocated by representers also get
- // released.
- $scope.$on("$destroy", destroyRepresenters);
- $scope.$on("$destroy", function () {
- if (mutationListener) {
- mutationListener();
- }
- });
-
- // Do one initial refresh, so that we don't need another
- // digest iteration just to populate the scope. Failure to
- // do this can result in unstable digest cycles, which
- // Angular will detect, and throw an Error about.
- refresh();
- }
-
- return {
- // Only applicable at the element level
- restrict: "E",
-
- // Handle Angular's linking step
- link: link,
-
- // May hide the element, so let other directives act first
- priority: -1000,
-
- // Two-way bind key and parameters, get the represented domain
- // object as "mct-object"
- scope: {
- key: "=",
- domainObject: "=mctObject",
- ngModel: "=",
- parameters: "="
- }
- };
- }
-
- /**
- * A representer participates in the process of instantiating a
- * representation of a domain object.
- *
- * @interface Representer
- * @augments {Destroyable}
- */
- /**
- * Set the current representation in use, and the domain
- * object being represented.
- *
- * @method Representer#represent
- * @param {RepresentationDefinition} representation the
- * definition of the representation in use
- * @param {DomainObject} domainObject the domain object
- * being represented
- */
-
- return MCTRepresentation;
- }
-);
-
diff --git a/platform/representation/src/TemplateLinker.js b/platform/representation/src/TemplateLinker.js
deleted file mode 100644
index d42a9e8ee..000000000
--- a/platform/representation/src/TemplateLinker.js
+++ /dev/null
@@ -1,177 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- /**
- * The `templateLinker` service is intended for internal use by
- * the `mct-include` and `mct-representation` directives. It is
- * used to support common behavior of directives; specifically,
- * loading templates and inserting them into a specified element,
- * and/or removing that element from the DOM when there is no
- * template to populate it with.
- *
- * @param {Function} $templateRequest Angular's `$templateRequest`
- * service
- * @param $sce Angular's `$sce` service
- * @param {Function} $compile Angular's `$compile` service
- * @param $log Angular's `$log` service
- * @private
- */
- function TemplateLinker($templateRequest, $sce, $compile, $log) {
- this.$templateRequest = $templateRequest;
- this.$sce = $sce;
- this.$compile = $compile;
- this.$log = $log;
- }
-
- /**
- * Load a template from the given URL. This request will be handled
- * via `$templateRequest` to ensure caching et cetera.
- * @param {string} the URL for the template
- * @returns {Promise.<string>} a promise for the HTML content of
- * the template
- */
- TemplateLinker.prototype.load = function (templateUrl) {
- return this.$templateRequest(
- this.$sce.trustAsResourceUrl(templateUrl),
- false
- );
- };
-
- /**
- * Get a path to a template from an extension definition fo
- * a template, representation, or view.
- * @param {TemplateDefinition} extensionDefinition the definition
- * of the template/representation/view to resolve
- */
- TemplateLinker.prototype.getPath = function (extensionDefinition) {
- return [
- extensionDefinition.bundle.path,
- extensionDefinition.bundle.resources,
- extensionDefinition.templateUrl
- ].join('/');
- };
-
- /**
- * Populate the given element with templates, within the given scope;
- * intended to support the `link` function of the supported directives.
- *
- * @param {Scope} scope the Angular scope to use when rendering
- * templates
- * @param element the jqLite-wrapped element into which templates
- * should be inserted
- * @param {TemplateDefinition} extensionDefinition the definition
- * of the template/representation/view to display initially
- * @returns {Function} a function which can be called with a template's
- * extension definition to switch templates, or `undefined`
- * to remove.
- */
- TemplateLinker.prototype.link = function (scope, element, ext) {
- var activeElement = element,
- activeTemplateUrl,
- comment = this.$compile('<!-- hidden mct element -->')(scope),
- activeScope,
- self = this;
-
- function destroyScope() {
- if (activeScope) {
- activeScope.$destroy();
- activeScope = undefined;
- }
- }
-
- function removeElement() {
- if (activeElement !== comment) {
- destroyScope();
- activeElement.replaceWith(comment);
- activeElement = comment;
- }
- }
-
- function addElement() {
- if (activeElement !== element) {
- activeElement.replaceWith(element);
- activeElement = element;
- activeElement.empty();
- }
- }
-
- function populateElement(template) {
- destroyScope();
- activeScope = scope.$new(false);
- element.html(template);
- self.$compile(element.contents())(activeScope);
- }
-
- function showTemplate(template) {
- addElement();
- populateElement(template);
- activeTemplateUrl = undefined;
- }
-
- function badTemplateUrl(templateUrl) {
- self.$log.warn("Couldn't load template at " + templateUrl);
- removeElement();
- }
-
- function changeTemplateUrl(templateUrl) {
- if (templateUrl) {
- destroyScope();
- addElement();
- self.load(templateUrl).then(function (template) {
- // Avoid race conditions
- if (templateUrl === activeTemplateUrl) {
- populateElement(template);
- }
- }, function () {
- badTemplateUrl(templateUrl);
- });
- } else {
- removeElement();
- }
-
- activeTemplateUrl = templateUrl;
- }
-
- function changeTemplate(templateExt) {
- templateExt = templateExt || {};
- if (templateExt.templateUrl) {
- changeTemplateUrl(self.getPath(templateExt));
- } else if (templateExt.template) {
- showTemplate(templateExt.template);
- } else {
- removeElement();
- }
- }
-
- changeTemplate(ext);
-
- return changeTemplate;
- };
-
- return TemplateLinker;
- }
-);
-
diff --git a/platform/representation/src/TemplatePrefetcher.js b/platform/representation/src/TemplatePrefetcher.js
deleted file mode 100644
index 1509d2d0c..000000000
--- a/platform/representation/src/TemplatePrefetcher.js
+++ /dev/null
@@ -1,48 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- function () {
-
- /**
- * Loads all templates when the application is started.
- * @param {platform/representation.TemplateLinker} templateLinker
- * the `templateLinker` service, used to load and cache
- * template extensions
- * @param {...Array.<{templateUrl: string}>} extensions arrays
- * of template or template-like extensions
- */
- function TemplatePrefetcher(templateLinker) {
- Array.prototype.slice.apply(arguments, [1])
- .reduce(function (a, b) {
- return a.concat(b);
- }, [])
- .forEach(function (ext) {
- if (ext.templateUrl) {
- templateLinker.load(templateLinker.getPath(ext));
- }
- });
- }
-
- return TemplatePrefetcher;
- }
-);
diff --git a/platform/representation/src/gestures/DragGesture.js b/platform/representation/src/gestures/DragGesture.js
deleted file mode 100644
index 937b63f6a..000000000
--- a/platform/representation/src/gestures/DragGesture.js
+++ /dev/null
@@ -1,119 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Module defining DragGesture. Created by vwoeltje on 11/17/14.
- */
-define(
- ['./GestureConstants'],
- function (GestureConstants) {
-
- /**
- * Add event handlers to a representation such that it may be
- * dragged as the source for drag-drop composition.
- *
- * @memberof platform/representation
- * @constructor
- * @implements {Gesture}
- * @param $log Angular's logging service
- * @param element the jqLite-wrapped element which should become
- * draggable
- * @param {DomainObject} domainObject the domain object which
- * is represented; this will be passed on drop.
- */
- function DragGesture($log, dndService, element, domainObject) {
- function startDrag(e) {
- var event = (e || {}).originalEvent || e;
-
- $log.debug("Initiating drag");
-
- try {
- // Set the data associated with the drag-drop operation
- event.dataTransfer.effectAllowed = 'move';
-
- // Support drop as plain-text (JSON); not used internally
- event.dataTransfer.setData(
- 'text/plain',
- JSON.stringify({
- id: domainObject.getId(),
- model: domainObject.getModel()
- })
- );
-
- // For internal use, pass the object's identifier as
- // part of the drag
- event.dataTransfer.setData(
- GestureConstants.MCT_DRAG_TYPE,
- domainObject.getId()
- );
-
- // Finally, also pass the id object instance via the
- // dndService, allowing inspection during drag as well
- // as retrieval of the original domain object.
- dndService.setData(
- GestureConstants.MCT_EXTENDED_DRAG_TYPE,
- domainObject
- );
- dndService.setData(
- GestureConstants.MCT_DRAG_TYPE,
- domainObject.getId()
- );
-
- } catch (err) {
- // Exceptions at this point indicate that the browser
- // do not fully support drag-and-drop (e.g. if
- // dataTransfer is undefined)
- $log.warn([
- "Could not initiate drag due to ",
- err.message
- ].join(""));
- }
-
- }
-
- function endDrag() {
- // Clear the drag data after the drag is complete
- dndService.removeData(GestureConstants.MCT_DRAG_TYPE);
- dndService.removeData(GestureConstants.MCT_EXTENDED_DRAG_TYPE);
- }
-
- // Mark the element as draggable, and handle the dragstart event
- $log.debug("Attaching drag gesture");
- element.attr('draggable', 'true');
- element.on('dragstart', startDrag);
- element.on('dragend', endDrag);
-
- this.element = element;
- this.startDragCallback = startDrag;
- this.endDragCallback = endDrag;
- }
-
- DragGesture.prototype.destroy = function () {
- // Detach listener
- this.element.removeAttr('draggable');
- this.element.off('dragstart', this.startDragCallback);
- this.element.off('dragend', this.endDragCallback);
- };
-
- return DragGesture;
- }
-);
diff --git a/platform/representation/src/gestures/DropGesture.js b/platform/representation/src/gestures/DropGesture.js
deleted file mode 100644
index 311ab3ec6..000000000
--- a/platform/representation/src/gestures/DropGesture.js
+++ /dev/null
@@ -1,129 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Module defining DropGesture. Created by vwoeltje on 11/17/14.
- */
-define(
- ['./GestureConstants'],
- function (GestureConstants) {
-
- /**
- * A DropGesture adds and maintains event handlers upon an element
- * such that it may act as a drop target for drag-drop composition.
- *
- * @memberof platform/representation
- * @constructor
- * @param $q Angular's $q, for promise handling
- * @param element the jqLite-wrapped representation element
- * @param {DomainObject} domainObject the domain object whose
- * composition should be modified as a result of the drop.
- */
- function DropGesture(dndService, $q, element, domainObject) {
- var actionCapability = domainObject.getCapability('action'),
- action; // Action for the drop, when it occurs
-
- function broadcastDrop(id, event) {
- // Find the relevant scope...
- var rect,
- scope = element.scope && element.scope();
-
- if (scope && scope.$broadcast) {
- // Get the representation's bounds, to convert
- // drop position
- rect = element[0].getBoundingClientRect();
-
- // ...and broadcast the event. This allows specific
- // views to have post-drop behavior which depends on
- // drop position.
- scope.$broadcast(
- GestureConstants.MCT_DROP_EVENT,
- id,
- {
- x: event.pageX - rect.left,
- y: event.pageY - rect.top
- }
- );
- }
- }
-
- function dragOver(e) {
- var event = (e || {}).originalEvent || e,
- selectedObject = dndService.getData(
- GestureConstants.MCT_EXTENDED_DRAG_TYPE
- );
-
- if (selectedObject) {
- // TODO: Vary this based on modifier keys
- action = actionCapability.getActions({
- key: 'compose',
- selectedObject: selectedObject
- })[0];
- if (action) {
- event.dataTransfer.dropEffect = 'move';
-
- // Indicate that we will accept the drag
- event.preventDefault(); // Required in Chrome?
-
- return false;
- }
- }
- }
-
- function drop(e) {
- var event = (e || {}).originalEvent || e,
- id = event.dataTransfer.getData(GestureConstants.MCT_DRAG_TYPE);
-
- // Handle the drop; add the dropped identifier to the
- // destination domain object's composition, and persist
- // the change.
- if (id) {
- e.preventDefault();
- $q.when(action && action.perform()).then(function () {
- broadcastDrop(id, event);
- });
-
- }
- }
-
- // We can only handle drops if we have access to actions...
- if (actionCapability) {
- // Listen for dragover, to indicate we'll accept a drag
- element.on('dragover', dragOver);
-
- // Listen for the drop itself
- element.on('drop', drop);
- }
-
- this.element = element;
- this.dragOverCallback = dragOver;
- this.dropCallback = drop;
- }
-
- DropGesture.prototype.destroy = function () {
- this.element.off('dragover', this.dragOverCallback);
- this.element.off('drop', this.dropCallback);
- };
-
- return DropGesture;
- }
-);
diff --git a/platform/representation/src/gestures/GestureConstants.js b/platform/representation/src/gestures/GestureConstants.js
deleted file mode 100644
index 1463126c7..000000000
--- a/platform/representation/src/gestures/GestureConstants.js
+++ /dev/null
@@ -1,53 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Constants used by domain object gestures.
- * @class platform/representation.GestureConstants
- */
-define({
- /**
- * The string identifier for the data type used for drag-and-drop
- * composition of domain objects. (e.g. in event.dataTransfer.setData
- * calls.)
- * @memberof platform/representation.GestureConstants
- */
- MCT_DRAG_TYPE: 'mct-domain-object-id',
- /**
- * The string identifier for the data type used for drag-and-drop
- * composition of domain objects, by object instance (passed through
- * the dndService)
- * @memberof platform/representation.GestureConstants
- */
- MCT_EXTENDED_DRAG_TYPE: 'mct-domain-object',
- /**
- * An estimate for the dimensions of a context menu, used for
- * positioning.
- * @memberof platform/representation.GestureConstants
- */
- MCT_MENU_DIMENSIONS: [170, 200],
- /**
- * Identifier for drop events.
- * @memberof platform/representation.GestureConstants
- */
- MCT_DROP_EVENT: 'mctDrop'
-});
diff --git a/platform/representation/src/gestures/GestureProvider.js b/platform/representation/src/gestures/GestureProvider.js
deleted file mode 100644
index 284f34ecf..000000000
--- a/platform/representation/src/gestures/GestureProvider.js
+++ /dev/null
@@ -1,133 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Module defining GestureProvider. Created by vwoeltje on 11/22/14.
- */
-define(
- [],
- function () {
-
- /**
- * Handles the attachment of gestures (responses to DOM events,
- * generally) to DOM elements which represent domain objects.
- *
- * @interface GestureService
- */
- /**
- * Attach a set of gestures (indicated by key) to a
- * DOM element which represents a specific domain object.
- * @method GestureService#attachGestures
- * @param element the jqLite-wrapped DOM element which the
- * user will interact with
- * @param {DomainObject} domainObject the domain object which
- * is represented by that element
- * @param {string[]} gestureKeys an array of keys identifying
- * which gestures should apply; these will be matched
- * against the keys defined in the gestures' extension
- * definitions
- * @return {Destroyable} an object with a `destroy`
- * method which can (and should) be used when
- * gestures should no longer be applied to an element.
- */
-
- /**
- * The GestureProvider exposes defined gestures. Gestures are used
- * do describe and handle general-purpose interactions with the DOM
- * that should be interpreted as interactions with domain objects,
- * such as right-clicking to expose context menus.
- *
- * Gestures are defined individually as extensions of the
- * `gestures` category. The gesture provider merely serves as an
- * intermediary between these and the `mct-representation` directive
- * where they are used.
- *
- * @memberof platform/representation
- * @implements {GestureService}
- * @constructor
- * @param {Gesture[]} gestures an array of all gestures which are
- * available as extensions
- */
- function GestureProvider(gestures) {
- var gestureMap = {};
-
- // Assemble all gestures into a map, for easy look up
- gestures.forEach(function (gesture) {
- gestureMap[gesture.key] = gestureMap[gesture.key] || gesture;
- });
-
- this.gestureMap = gestureMap;
- }
-
- function releaseGesture(gesture) {
- // Invoke the gesture's "destroy" method (if there is one)
- // to release any held resources and detach event handlers.
- if (gesture && gesture.destroy) {
- gesture.destroy();
- }
- }
-
- GestureProvider.prototype.attachGestures = function attachGestures(element, domainObject, gestureKeys) {
- // Look up the desired gestures, filter for applicability,
- // and instantiate them. Maintain a reference to allow them
- // to be destroyed as a group later.
- var gestureMap = this.gestureMap,
- attachedGestures = gestureKeys.map(function (key) {
- return gestureMap[key];
- }).filter(function (Gesture) {
- return Gesture !== undefined && (Gesture.appliesTo
- ? Gesture.appliesTo(domainObject)
- : true);
- }).map(function (Gesture) {
- return new Gesture(element, domainObject);
- });
-
- return {
- destroy: function () {
- // Just call all the individual "destroy" methods
- attachedGestures.forEach(releaseGesture);
- }
- };
- };
-
- /**
- * A destroyable object may have resources allocated which require
- * explicit release.
- *
- * @interface Destroyable
- */
- /**
- * Release any resources associated with this object.
- *
- * @method Destroyable#destroy
- */
-
- /**
- * A gesture describes manners in which certain representations of
- * domain objects may respond to DOM events upon those representations.
- * @interface Gesture
- * @augments Destroyable
- */
-
- return GestureProvider;
- }
-);
diff --git a/platform/representation/src/gestures/GestureRepresenter.js b/platform/representation/src/gestures/GestureRepresenter.js
deleted file mode 100644
index 8ed69638f..000000000
--- a/platform/representation/src/gestures/GestureRepresenter.js
+++ /dev/null
@@ -1,68 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- /**
- * The GestureRepresenter is responsible for installing predefined
- * gestures upon mct-representation instances.
- * Gestures themselves are pulled from the gesture service; this
- * simply wraps that behavior in a Representer interface, such that
- * it may be included among other such Representers used to prepare
- * specific representations.
- * @param {GestureService} gestureService the service which provides
- * gestures
- * @param {Scope} scope the Angular scope for this representation
- * @param element the JQLite-wrapped mct-representation element
- * @constructor
- * @implements {Representer}
- * @memberof platform/representation
- */
- function GestureRepresenter(gestureService, scope, element) {
- this.gestureService = gestureService;
- this.element = element;
- }
-
- GestureRepresenter.prototype.represent = function represent(representation, domainObject) {
- // Clear out any existing gestures
- this.destroy();
-
- // Attach gestures - by way of the service.
- this.gestureHandle = this.gestureService.attachGestures(
- this.element,
- domainObject,
- (representation || {}).gestures || []
- );
- };
-
- GestureRepresenter.prototype.destroy = function () {
- // Release any resources associated with these gestures
- if (this.gestureHandle) {
- this.gestureHandle.destroy();
- }
- };
-
- return GestureRepresenter;
- }
-);
diff --git a/platform/representation/src/services/DndService.js b/platform/representation/src/services/DndService.js
deleted file mode 100644
index de5d01f60..000000000
--- a/platform/representation/src/services/DndService.js
+++ /dev/null
@@ -1,73 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- /**
- * Drag-and-drop service.
- * Supplements HTML5 drag-and-drop support by:
- * * Storing arbitrary JavaScript objects (not just strings.)
- * * Allowing inspection of dragged objects during `dragover` events,
- * etc. (which cannot be done in Chrome for security reasons)
- * @memberof platform/representation
- * @constructor
- * @param $log Angular's $log service
- */
- function DndService($log) {
- var data = {};
-
- return {
- /**
- * Set drag data associated with a given type.
- * @param {string} key the type's identiifer
- * @param {*} value the data being dragged
- * @memberof platform/representation.DndService#
- */
- setData: function (key, value) {
- $log.debug("Setting drag data for " + key);
- data[key] = value;
- },
- /**
- * Get drag data associated with a given type.
- * @returns {*} the data being dragged
- * @memberof platform/representation.DndService#
- */
- getData: function (key) {
- return data[key];
- },
- /**
- * Remove data associated with active drags.
- * @param {string} key the type to remove
- * @memberof platform/representation.DndService#
- */
- removeData: function (key) {
- $log.debug("Clearing drag data for " + key);
- delete data[key];
- }
- };
- }
-
- return DndService;
- }
-);
diff --git a/platform/representation/test/MCTIncludeSpec.js b/platform/representation/test/MCTIncludeSpec.js
deleted file mode 100644
index bee7ca4d4..000000000
--- a/platform/representation/test/MCTIncludeSpec.js
+++ /dev/null
@@ -1,108 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * MCTIncudeSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../src/MCTInclude"],
- function (MCTInclude) {
-
- describe("The mct-include directive", function () {
- var testTemplates,
- testUrls,
- mockLinker,
- mockScope,
- mockElement,
- mockChangeTemplate,
- mctInclude;
-
- function fireWatch(expr, value) {
- mockScope.$watch.calls.all().forEach(function (call) {
- if (call.args[0] === expr) {
- call.args[1](value);
- }
- });
- }
-
- beforeEach(function () {
- testTemplates = [
- {
- key: "abc",
- bundle: {
- path: "a",
- resources: "b"
- },
- templateUrl: "c/template.html"
- },
- {
- key: "xyz",
- bundle: {
- path: "x",
- resources: "y"
- },
- templateUrl: "z/template.html"
- }
- ];
- testUrls = {};
- testTemplates.forEach(function (t, i) {
- testUrls[t.key] = "some URL " + String(i);
- });
- mockLinker = jasmine.createSpyObj(
- 'templateLinker',
- ['link', 'getPath']
- );
- mockScope = jasmine.createSpyObj('$scope', ['$watch', '$on']);
- mockElement = jasmine.createSpyObj('element', ['empty']);
- mockChangeTemplate = jasmine.createSpy('changeTemplate');
- mockLinker.link.and.returnValue(mockChangeTemplate);
- mockLinker.getPath.and.callFake(function (template) {
- return testUrls[template.key];
- });
- mctInclude = new MCTInclude(testTemplates, mockLinker);
- mctInclude.link(mockScope, mockElement, {});
- });
-
- it("is restricted to elements", function () {
- expect(mctInclude.restrict).toEqual("E");
- });
-
- it("exposes templates via the templateLinker", function () {
- expect(mockLinker.link)
- .toHaveBeenCalledWith(mockScope, mockElement, undefined);
- });
-
- it("reads a template location from a scope's key variable", function () {
- mockScope.key = 'abc';
- fireWatch('key', mockScope.key);
- expect(mockChangeTemplate)
- .toHaveBeenCalledWith(testTemplates[0]);
-
- mockScope.key = 'xyz';
- fireWatch('key', mockScope.key);
- expect(mockChangeTemplate)
- .toHaveBeenCalledWith(testTemplates[1]);
- });
-
- });
- }
-);
diff --git a/platform/representation/test/MCTRepresentationSpec.js b/platform/representation/test/MCTRepresentationSpec.js
deleted file mode 100644
index 195a51133..000000000
--- a/platform/representation/test/MCTRepresentationSpec.js
+++ /dev/null
@@ -1,343 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * MCTRepresentationSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../src/MCTRepresentation"],
- function (MCTRepresentation) {
-
- var JQLITE_FUNCTIONS = ["on", "off", "attr", "removeAttr"],
- LOG_FUNCTIONS = ["error", "warn", "info", "debug"],
- DOMAIN_OBJECT_METHODS = ["getId", "getModel", "getCapability", "hasCapability", "useCapability"];
-
- describe("The mct-representation directive", function () {
- var testRepresentations,
- testViews,
- testUrls,
- mockRepresenters,
- mockMutationCapability,
- mockQ,
- mockLinker,
- mockLog,
- mockChangeTemplate,
- mockScope,
- mockElement,
- mockDomainObject,
- testModel,
- mctRepresentation;
-
- function mockPromise(value) {
- return (value && value.then) ? value : {
- then: function (callback) {
- return mockPromise(callback(value));
- }
- };
- }
-
- function fireWatch(expr, value) {
- mockScope.$watch.calls.all().forEach(function (call) {
- if (call.args[0] === expr) {
- call.args[1](value);
- }
- });
- }
-
- beforeEach(function () {
- testUrls = {};
-
- testRepresentations = [
- {
- key: "abc",
- bundle: {
- path: "a",
- resources: "b"
- },
- templateUrl: "c/template.html"
- },
- {
- key: "def",
- bundle: {
- path: "d",
- resources: "e"
- },
- templateUrl: "f/template.html",
- uses: ["testCapability", "otherTestCapability"]
- }
- ];
-
- testViews = [
- {
- key: "uvw",
- bundle: {
- path: "u",
- resources: "v"
- },
- templateUrl: "w/template.html",
- gestures: ["testGesture", "otherTestGesture"]
- },
- {
- key: "xyz",
- bundle: {
- path: "x",
- resources: "y"
- },
- templateUrl: "z/template.html"
- }
- ];
-
- testModel = { someKey: "some value" };
-
- testUrls = {};
- testViews.concat(testRepresentations).forEach(function (t, i) {
- testUrls[t.key] = "some URL " + String(i);
- });
-
- mockRepresenters = ["A", "B"].map(function (name) {
- var constructor = jasmine.createSpy("Representer" + name),
- representer = jasmine.createSpyObj(
- "representer" + name,
- ["represent", "destroy"]
- );
- constructor.and.returnValue(representer);
-
- return constructor;
- });
-
- mockQ = { when: mockPromise };
- mockLinker = jasmine.createSpyObj(
- 'templateLinker',
- ['link', 'getPath']
- );
- mockChangeTemplate = jasmine.createSpy('changeTemplate');
- mockLog = jasmine.createSpyObj("$log", LOG_FUNCTIONS);
-
- mockMutationCapability =
- jasmine.createSpyObj("mutation", ["listen"]);
-
- mockScope = jasmine.createSpyObj("scope", ["$watch", "$on"]);
- mockElement = jasmine.createSpyObj("element", JQLITE_FUNCTIONS);
- mockDomainObject = jasmine.createSpyObj("domainObject", DOMAIN_OBJECT_METHODS);
-
- mockDomainObject.getModel.and.returnValue(testModel);
- mockLinker.link.and.returnValue(mockChangeTemplate);
- mockLinker.getPath.and.callFake(function (ext) {
- return testUrls[ext.key];
- });
-
- mockDomainObject.getCapability.and.callFake(function (c) {
- return c === 'mutation' && mockMutationCapability;
- });
-
- mctRepresentation = new MCTRepresentation(
- testRepresentations,
- testViews,
- mockRepresenters,
- mockQ,
- mockLinker,
- mockLog
- );
- mctRepresentation.link(mockScope, mockElement);
- });
-
- it("is restricted to elements", function () {
- expect(mctRepresentation.restrict).toEqual("E");
- });
-
- it("exposes templates via the templateLinker", function () {
- expect(mockLinker.link)
- .toHaveBeenCalledWith(mockScope, mockElement);
- });
-
- it("watches scope when linked", function () {
- expect(mockScope.$watch).toHaveBeenCalledWith(
- "key",
- jasmine.any(Function)
- );
- expect(mockScope.$watch).toHaveBeenCalledWith(
- "domainObject",
- jasmine.any(Function)
- );
- });
-
- it("recognizes keys for representations", function () {
- mockScope.key = "abc";
- mockScope.domainObject = mockDomainObject;
-
- // Trigger the watch
- fireWatch('key', mockScope.key);
- fireWatch('domainObject', mockDomainObject);
-
- expect(mockChangeTemplate)
- .toHaveBeenCalledWith(testRepresentations[0]);
- });
-
- it("recognizes keys for views", function () {
- mockScope.key = "xyz";
- mockScope.domainObject = mockDomainObject;
-
- // Trigger the watches
- fireWatch('key', mockScope.key);
- fireWatch('domainObject', mockDomainObject);
-
- expect(mockChangeTemplate)
- .toHaveBeenCalledWith(testViews[1]);
- });
-
- it("does not load templates until there is an object", function () {
- mockScope.key = "xyz";
-
- // Trigger the watch
- fireWatch('key', mockScope.key);
-
- expect(mockChangeTemplate)
- .not.toHaveBeenCalledWith(jasmine.any(Object));
-
- mockScope.domainObject = mockDomainObject;
- fireWatch('domainObject', mockDomainObject);
-
- expect(mockChangeTemplate)
- .toHaveBeenCalledWith(jasmine.any(Object));
- });
-
- it("loads declared capabilities", function () {
- mockScope.key = "def";
- mockScope.domainObject = mockDomainObject;
-
- // Trigger the watch
- mockScope.$watch.calls.all()[0].args[1]();
-
- expect(mockDomainObject.useCapability)
- .toHaveBeenCalledWith("testCapability");
- expect(mockDomainObject.useCapability)
- .toHaveBeenCalledWith("otherTestCapability");
- });
-
- it("logs when no representation is available for a key", function () {
- mockScope.key = "someUnknownThing";
-
- // Verify precondition
- expect(mockLog.warn).not.toHaveBeenCalled();
-
- // Trigger the watch
- mockScope.$watch.calls.all()[0].args[1]();
-
- // Should have gotten a warning - that's an unknown key
- expect(mockLog.warn).toHaveBeenCalled();
- });
-
- it("clears out obsolete properties from scope", function () {
- mockScope.key = "def";
- mockScope.domainObject = mockDomainObject;
- mockDomainObject.useCapability.and.returnValue("some value");
-
- // Trigger the watch
- mockScope.$watch.calls.all()[0].args[1]();
- expect(mockScope.testCapability).toBeDefined();
-
- // Change the view
- mockScope.key = "xyz";
-
- // Trigger the watch again; should clear capability from scope
- mockScope.$watch.calls.all()[0].args[1]();
- expect(mockScope.testCapability).toBeUndefined();
- });
-
- describe("when a domain object has been observed", function () {
- var mockContext,
- mockContext2,
- mockLink,
- mockParent;
-
- beforeEach(function () {
- mockContext = jasmine.createSpyObj('context', ['getPath']);
- mockContext2 = jasmine.createSpyObj('context', ['getPath']);
- mockLink = jasmine.createSpyObj(
- 'linkedObject',
- DOMAIN_OBJECT_METHODS
- );
- mockParent = jasmine.createSpyObj(
- 'parentObject',
- DOMAIN_OBJECT_METHODS
- );
-
- mockDomainObject.getCapability.and.callFake(function (c) {
- return {
- context: mockContext,
- mutation: mockMutationCapability
- }[c];
- });
- mockLink.getCapability.and.callFake(function (c) {
- return {
- context: mockContext2,
- mutation: mockMutationCapability
- }[c];
- });
- mockDomainObject.hasCapability.and.callFake(function (c) {
- return c === 'context';
- });
- mockLink.hasCapability.and.callFake(function (c) {
- return c === 'context';
- });
- mockLink.getModel.and.returnValue({});
-
- mockContext.getPath.and.returnValue([mockDomainObject]);
- mockContext2.getPath.and.returnValue([mockParent, mockLink]);
-
- mockLink.getId.and.returnValue('test-id');
- mockDomainObject.getId.and.returnValue('test-id');
-
- mockParent.getId.and.returnValue('parent-id');
-
- mockScope.key = "abc";
- mockScope.domainObject = mockDomainObject;
-
- mockScope.$watch.calls.all()[0].args[1]();
- });
-
- it("listens for mutation of that object", function () {
- expect(mockMutationCapability.listen)
- .toHaveBeenCalledWith(jasmine.any(Function));
- });
-
- it("detects subsequent changes among linked instances", function () {
- var callCount = mockChangeTemplate.calls.count();
-
- mockScope.domainObject = mockLink;
- mockScope.$watch.calls.all()[0].args[1]();
-
- expect(mockChangeTemplate.calls.count())
- .toEqual(callCount + 1);
- });
-
- it("does not trigger excess template changes for same instances", function () {
- var callCount = mockChangeTemplate.calls.count();
- mockScope.$watch.calls.all()[0].args[1]();
- expect(mockChangeTemplate.calls.count()).toEqual(callCount);
- });
-
- });
-
- });
- }
-);
diff --git a/platform/representation/test/TemplateLinkerSpec.js b/platform/representation/test/TemplateLinkerSpec.js
deleted file mode 100644
index aabe33d26..000000000
--- a/platform/representation/test/TemplateLinkerSpec.js
+++ /dev/null
@@ -1,239 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../src/TemplateLinker"],
- function (TemplateLinker) {
-
- var JQLITE_METHODS = ['replaceWith', 'empty', 'html', 'contents'],
- SCOPE_METHODS = ['$on', '$new', '$destroy'];
-
- describe("TemplateLinker", function () {
- var mockTemplateRequest,
- mockSce,
- mockCompile,
- mockLog,
- mockScope,
- mockElement,
- mockTemplates,
- mockElements,
- mockContents,
- mockNewScope,
- mockPromise,
- linker;
-
- function testExtension(path, res, templatePath) {
- return {
- bundle: {
- path: path,
- resources: res
- },
- templateUrl: templatePath
- };
- }
-
- beforeEach(function () {
- mockTemplateRequest = jasmine.createSpy('$templateRequest');
- mockSce = jasmine.createSpyObj('$sce', ['trustAsResourceUrl']);
- mockCompile = jasmine.createSpy('$compile');
- mockLog = jasmine.createSpyObj('$log', ['error', 'warn']);
- mockScope = jasmine.createSpyObj('$scope', SCOPE_METHODS);
- mockNewScope = jasmine.createSpyObj('$scope', SCOPE_METHODS);
- mockElement = jasmine.createSpyObj('element', JQLITE_METHODS);
- mockPromise = jasmine.createSpyObj('promise', ['then']);
- mockTemplates = {};
- mockElements = {};
- mockContents = {};
-
- mockTemplateRequest.and.returnValue(mockPromise);
- mockCompile.and.callFake(function (toCompile) {
- var html = typeof toCompile === 'string'
- ? toCompile : toCompile.testHtml;
- mockTemplates[html] = jasmine.createSpy('template');
- mockElements[html] =
- jasmine.createSpyObj('templateEl', JQLITE_METHODS);
- mockTemplates[html].and.returnValue(mockElements[html]);
-
- return mockTemplates[html];
- });
- mockSce.trustAsResourceUrl.and.callFake(function (url) {
- return { trusted: url };
- });
- mockScope.$new.and.returnValue(mockNewScope);
- mockElement.html.and.callFake(function (html) {
- mockContents[html] =
- jasmine.createSpyObj('contentsEl', JQLITE_METHODS);
- mockContents[html].testHtml = html;
- });
- mockElement.contents.and.callFake(function () {
- return mockContents[
- mockElement.html.calls.mostRecent().args[0]
- ];
- });
-
- linker = new TemplateLinker(
- mockTemplateRequest,
- mockSce,
- mockCompile,
- mockLog
- );
- });
-
- it("resolves extension paths", function () {
- var testExt = testExtension('a', 'b', 'c/d.html');
- expect(linker.getPath(testExt)).toEqual('a/b/c/d.html');
- });
-
- describe("when linking elements", function () {
- var changeTemplate,
- commentElement;
-
- function findCommentElement() {
- mockCompile.calls.all().forEach(function (call) {
- var html = call.args[0];
- if (html.indexOf("<!--") > -1) {
- commentElement = mockElements[html];
- }
- });
- }
-
- beforeEach(function () {
- changeTemplate = linker.link(mockScope, mockElement);
- findCommentElement();
- });
-
- it("compiles a comment to use to replace element", function () {
- expect(commentElement).toBeDefined();
- });
-
- it("initially replaces elements with comments", function () {
- expect(mockElement.replaceWith)
- .toHaveBeenCalledWith(commentElement);
- });
-
- it("provides a function to change templates", function () {
- expect(changeTemplate).toEqual(jasmine.any(Function));
- });
-
- describe("and then changing templates", function () {
- var testExt,
- testUrl,
- testTemplate;
-
- beforeEach(function () {
- testExt = testExtension('some', 'url', 'template.html');
- testUrl = linker.getPath(testExt);
- testTemplate = "<div>Some template!</div>";
- changeTemplate(testExt);
- mockPromise.then.calls.mostRecent()
- .args[0](testTemplate);
- });
-
- it("loads templates using $templateRequest", function () {
- expect(mockTemplateRequest).toHaveBeenCalledWith({
- trusted: testUrl
- }, false);
- });
-
- it("compiles element contents with a new scope", function () {
- expect(mockCompile)
- .toHaveBeenCalledWith(mockContents[testTemplate]);
- expect(mockTemplates[testTemplate])
- .toHaveBeenCalledWith(mockNewScope);
- });
-
- it("replaces comments with specified element", function () {
- expect(commentElement.replaceWith)
- .toHaveBeenCalledWith(mockElement);
- });
-
- it("inserts HTML content into the specified element", function () {
- expect(mockElement.html)
- .toHaveBeenCalledWith(testTemplate);
- });
-
- it("clears templates when called with undefined", function () {
- expect(mockElement.replaceWith.calls.count())
- .toEqual(1);
- changeTemplate(undefined);
- expect(mockElement.replaceWith.calls.count())
- .toEqual(2);
- expect(mockElement.replaceWith.calls.mostRecent().args[0])
- .toEqual(commentElement);
- });
-
- it("logs no warnings for nominal changes", function () {
- expect(mockLog.warn).not.toHaveBeenCalled();
- });
-
- describe("which cannot be found", function () {
- beforeEach(function () {
- changeTemplate(
- testExtension("some", "bad", "template.html")
- );
- // Reject the template promise
- mockPromise.then.calls.mostRecent().args[1]();
- });
-
- it("removes the element from the DOM", function () {
- expect(mockElement.replaceWith.calls.count())
- .toEqual(2);
- expect(
- mockElement.replaceWith.calls.mostRecent().args[0]
- ).toEqual(commentElement);
- });
-
- it("logs a warning", function () {
- expect(mockLog.warn)
- .toHaveBeenCalledWith(jasmine.any(String));
- });
-
- });
- });
-
- });
-
- describe("when an initial template URL is provided", function () {
- var testExt,
- testUrl;
-
- beforeEach(function () {
- testExt = testExtension('some', 'test', 'template.html');
- testUrl = linker.getPath(testExt);
- linker.link(mockScope, mockElement, testExt);
- });
-
- it("does not remove the element initially", function () {
- expect(mockElement.replaceWith)
- .not.toHaveBeenCalled();
- });
-
- it("loads the specified template", function () {
- expect(mockTemplateRequest).toHaveBeenCalledWith({
- trusted: testUrl
- }, false);
- });
- });
-
- });
- }
-);
diff --git a/platform/representation/test/TemplatePrefetcherSpec.js b/platform/representation/test/TemplatePrefetcherSpec.js
deleted file mode 100644
index a23373c29..000000000
--- a/platform/representation/test/TemplatePrefetcherSpec.js
+++ /dev/null
@@ -1,73 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../src/TemplatePrefetcher"],
- function (TemplatePrefetcher) {
-
- describe("TemplatePrefetcher", function () {
- var mockTemplateLinker,
- testExtensions,
- testPathPrefix,
- prefetcher; // eslint-disable-line
-
- beforeEach(function () {
- testPathPrefix = "some/path/";
-
- mockTemplateLinker = jasmine.createSpyObj(
- 'templateLinker',
- ['getPath', 'load']
- );
-
- mockTemplateLinker.getPath.and.callFake(function (ext) {
- return testPathPrefix + ext.templateUrl;
- });
-
- testExtensions = ['a', 'b', 'c'].map(function (category) {
- return ['x', 'y', 'z'].map(function (ext) {
- return {
- templateUrl: category + '/' + ext + '.html'
- };
- });
- });
-
- prefetcher = new TemplatePrefetcher(
- mockTemplateLinker,
- testExtensions[0],
- testExtensions[1],
- testExtensions[2]
- );
- });
-
- it("loads all templates when run", function () {
- testExtensions.forEach(function (category) {
- category.forEach(function (extension) {
- expect(mockTemplateLinker.load).toHaveBeenCalledWith(
- mockTemplateLinker.getPath(extension)
- );
- });
- });
- });
-
- });
- }
-);
diff --git a/platform/representation/test/gestures/DragGestureSpec.js b/platform/representation/test/gestures/DragGestureSpec.js
deleted file mode 100644
index 125dab741..000000000
--- a/platform/representation/test/gestures/DragGestureSpec.js
+++ /dev/null
@@ -1,136 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * DragGestureSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/gestures/DragGesture", "../../src/gestures/GestureConstants"],
- function (DragGesture, GestureConstants) {
-
- var JQLITE_FUNCTIONS = ["on", "off", "attr", "removeAttr"],
- LOG_FUNCTIONS = ["error", "warn", "info", "debug"],
- DND_FUNCTIONS = ["setData", "getData", "removeData"],
- DOMAIN_OBJECT_METHODS = ["getId", "getModel", "getCapability", "hasCapability", "useCapability"],
- TEST_ID = "test-id";
-
- describe("The drag gesture", function () {
- var mockLog,
- mockDndService,
- mockElement,
- mockDomainObject,
- mockDataTransfer,
- handlers,
- gesture;
-
- beforeEach(function () {
- mockLog = jasmine.createSpyObj("$log", LOG_FUNCTIONS);
- mockDndService = jasmine.createSpyObj("dndService", DND_FUNCTIONS);
- mockElement = jasmine.createSpyObj("element", JQLITE_FUNCTIONS);
- mockDomainObject = jasmine.createSpyObj("domainObject", DOMAIN_OBJECT_METHODS);
- mockDataTransfer = jasmine.createSpyObj("dataTransfer", ["setData"]);
-
- mockDomainObject.getId.and.returnValue(TEST_ID);
- mockDomainObject.getModel.and.returnValue({});
-
- handlers = {};
-
- gesture = new DragGesture(mockLog, mockDndService, mockElement, mockDomainObject);
-
- // Look up all handlers registered by the gesture
- mockElement.on.calls.all().forEach(function (call) {
- handlers[call.args[0]] = call.args[1];
- });
- });
-
- it("listens for dragstart on the element", function () {
- expect(handlers.dragstart).toEqual(jasmine.any(Function));
- });
-
- it("marks an element as draggable", function () {
- expect(mockElement.attr).toHaveBeenCalledWith("draggable", "true");
- });
-
- it("places data in a dataTransfer object", function () {
- handlers.dragstart({ dataTransfer: mockDataTransfer });
- expect(mockDataTransfer.setData).toHaveBeenCalledWith(
- GestureConstants.MCT_DRAG_TYPE,
- TEST_ID
- );
- });
-
- it("places domain object in the dnd service", function () {
- handlers.dragstart({ dataTransfer: mockDataTransfer });
- expect(mockDndService.setData).toHaveBeenCalledWith(
- GestureConstants.MCT_DRAG_TYPE,
- TEST_ID
- );
- expect(mockDndService.setData).toHaveBeenCalledWith(
- GestureConstants.MCT_EXTENDED_DRAG_TYPE,
- mockDomainObject
- );
- });
-
- it("clears domain object from the dnd service on drag end", function () {
- // Start dragging
- handlers.dragstart({ dataTransfer: mockDataTransfer });
-
- // Verify precondition
- expect(mockDndService.removeData).not.toHaveBeenCalled();
-
- // End the drag
- handlers.dragend({ dataTransfer: mockDataTransfer });
-
- // Should have removed the data that was attached
- expect(mockDndService.removeData)
- .toHaveBeenCalledWith(GestureConstants.MCT_DRAG_TYPE);
- expect(mockDndService.removeData)
- .toHaveBeenCalledWith(GestureConstants.MCT_EXTENDED_DRAG_TYPE);
- });
-
- it("logs a warning if dataTransfer cannot be set", function () {
- // Verify precondition
- expect(mockLog.warn).not.toHaveBeenCalled();
-
- // Fire the gesture without a dataTransfer field
- handlers.dragstart({});
-
- // Should have logged a warning
- expect(mockLog.warn).toHaveBeenCalled();
- });
-
- it("removes draggable attribute and listener when destroyed", function () {
- // Verify preconditions
- expect(mockElement.removeAttr).not.toHaveBeenCalled();
- expect(mockElement.off).not.toHaveBeenCalled();
-
- // Notify the gesture that its scope is being destroyed
- gesture.destroy();
-
- // Verify that attribute/listener were removed
- expect(mockElement.removeAttr).toHaveBeenCalledWith("draggable");
- expect(mockElement.off).toHaveBeenCalledWith("dragstart", handlers.dragstart);
- });
-
- });
- }
-);
diff --git a/platform/representation/test/gestures/DropGestureSpec.js b/platform/representation/test/gestures/DropGestureSpec.js
deleted file mode 100644
index f4268aef0..000000000
--- a/platform/representation/test/gestures/DropGestureSpec.js
+++ /dev/null
@@ -1,186 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * DropGestureSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/gestures/DropGesture"],
- function (DropGesture) {
-
- // Methods to mock
- var JQLITE_FUNCTIONS = ["on", "off", "attr", "removeAttr", "scope"],
- DOMAIN_OBJECT_METHODS = ["getId", "getModel", "getCapability", "hasCapability", "useCapability"],
- TEST_ID = "test-id",
- DROP_ID = "drop-id";
-
- describe("The drop gesture", function () {
- var mockDndService,
- mockQ,
- mockElement,
- mockDomainObject,
- mockPersistence,
- mockAction,
- mockEvent,
- mockScope,
- mockUnwrappedElement,
- mockDraggedObject,
- mockCompose,
- testModel,
- testRect,
- gesture,
- callbacks;
-
- function mockPromise(value) {
- return (value && value.then) ? value : {
- then: function (callback) {
- return mockPromise(callback(value));
- }
- };
- }
-
- beforeEach(function () {
- testModel = { composition: [] };
- testRect = {};
-
- mockDndService = jasmine.createSpyObj('dndService', ['getData']);
- mockQ = { when: mockPromise };
- mockElement = jasmine.createSpyObj("element", JQLITE_FUNCTIONS);
- mockDomainObject = jasmine.createSpyObj("domainObject", DOMAIN_OBJECT_METHODS);
- mockDraggedObject = jasmine.createSpyObj("draggedObject", DOMAIN_OBJECT_METHODS);
- mockPersistence = jasmine.createSpyObj("persistence", ["persist"]);
- mockEvent = jasmine.createSpyObj("event", ["preventDefault"]);
- mockEvent.dataTransfer = jasmine.createSpyObj("dataTransfer", ["getData"]);
- mockScope = jasmine.createSpyObj("$scope", ["$broadcast"]);
- mockUnwrappedElement = jasmine.createSpyObj("unwrapped", ["getBoundingClientRect"]);
- mockAction = jasmine.createSpyObj('action', ['getActions']);
- mockCompose = jasmine.createSpyObj('compose', ['perform']);
-
- mockDomainObject.getId.and.returnValue(TEST_ID);
- mockDomainObject.getModel.and.returnValue(testModel);
- mockDomainObject.getCapability.and.callFake(function (c) {
- return {
- persistence: mockPersistence,
- action: mockAction
- }[c];
- });
- mockDomainObject.useCapability.and.returnValue(true);
- mockEvent.dataTransfer.getData.and.returnValue(DROP_ID);
- mockElement[0] = mockUnwrappedElement;
- mockElement.scope.and.returnValue(mockScope);
- mockUnwrappedElement.getBoundingClientRect.and.returnValue(testRect);
- mockDndService.getData.and.returnValue(mockDraggedObject);
- mockAction.getActions.and.returnValue([mockCompose]);
-
- gesture = new DropGesture(
- mockDndService,
- mockQ,
- mockElement,
- mockDomainObject
- );
-
- // Get a reference to all callbacks registered during constructor
- callbacks = {};
- mockElement.on.calls.all().forEach(function (call) {
- callbacks[call.args[0]] = call.args[1];
- });
- });
-
- it("attaches dragover and drop listeners", function () {
- expect(callbacks.dragover).toBeDefined();
- expect(callbacks.drop).toBeDefined();
- });
-
- it("removes all listeners when destroyed", function () {
- // Verify precondition
- expect(mockElement.off).not.toHaveBeenCalled();
-
- // Destroy
- gesture.destroy();
-
- // Verify all callbacks were unregistered
- Object.keys(callbacks).forEach(function (k) {
- expect(mockElement.off).toHaveBeenCalledWith(k, callbacks[k]);
- });
- });
-
- it("accepts data transfer and prevents default behavior on dragover", function () {
- expect(callbacks.dragover(mockEvent)).toEqual(false);
- expect(mockEvent.preventDefault).toHaveBeenCalled();
- expect(mockEvent.dataTransfer.dropEffect).toBeDefined();
- });
-
- it("invokes compose on drop in edit mode", function () {
- // Set the mockDomainObject to have the editor capability
- mockDomainObject.hasCapability.and.returnValue(true);
-
- callbacks.dragover(mockEvent);
- expect(mockAction.getActions).toHaveBeenCalledWith({
- key: 'compose',
- selectedObject: mockDraggedObject
- });
- callbacks.drop(mockEvent);
- expect(mockCompose.perform).toHaveBeenCalled();
- });
-
- it("invokes compose on drop in browse mode for folders", function () {
- // Set the mockDomainObject to not have the editor capability
- mockDomainObject.hasCapability.and.returnValue(false);
- // Set the mockDomainObject to have a type of folder
- mockDomainObject.getModel.and.returnValue({type: 'folder'});
-
- callbacks.dragover(mockEvent);
- expect(mockAction.getActions).toHaveBeenCalledWith({
- key: 'compose',
- selectedObject: mockDraggedObject
- });
- callbacks.drop(mockEvent);
- expect(mockCompose.perform).toHaveBeenCalled();
- });
-
- it("broadcasts drop position (in edit mode)", function () {
- // Set the mockDomainObject to have the editor capability
- mockDomainObject.hasCapability.and.returnValue(true);
-
- testRect.left = 42;
- testRect.top = 36;
- mockEvent.pageX = 52;
- mockEvent.pageY = 64;
- callbacks.drop(mockEvent);
- expect(mockScope.$broadcast).toHaveBeenCalledWith(
- 'mctDrop',
- DROP_ID,
- {
- x: 10,
- y: 28
- }
- );
- });
-
- it("invokes preventDefault on drop", function () {
- callbacks.drop(mockEvent);
- expect(mockEvent.preventDefault).toHaveBeenCalled();
- });
-
- });
- }
-);
diff --git a/platform/representation/test/gestures/GestureProviderSpec.js b/platform/representation/test/gestures/GestureProviderSpec.js
deleted file mode 100644
index 9dacf412b..000000000
--- a/platform/representation/test/gestures/GestureProviderSpec.js
+++ /dev/null
@@ -1,89 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * GestureProviderSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/gestures/GestureProvider"],
- function (GestureProvider) {
-
- var JQLITE_FUNCTIONS = ["on", "off", "attr", "removeAttr"],
- GESTURE_KEYS = ["a", "b", "c", "d", "e"],
- DOMAIN_OBJECT_METHODS = ["getId", "getModel", "getCapability", "hasCapability", "useCapability"];
-
- describe("The gesture provider", function () {
- var mockGestures,
- mockDestroys,
- mockElement,
- mockDomainObject,
- provider;
-
- beforeEach(function () {
- mockElement = jasmine.createSpyObj("element", JQLITE_FUNCTIONS);
- mockDomainObject = jasmine.createSpyObj("domainObject", DOMAIN_OBJECT_METHODS);
-
- mockDestroys = {};
- mockGestures = {};
- GESTURE_KEYS.forEach(function (key) {
- mockDestroys[key] = jasmine.createSpy("destroy-" + key);
- mockGestures[key] = jasmine.createSpy("gesture-" + key);
- mockGestures[key].and.returnValue({ destroy: mockDestroys[key] });
- mockGestures[key].key = key;
- });
-
- provider = new GestureProvider(GESTURE_KEYS.map(function (key) {
- return mockGestures[key];
- }));
- });
-
- it("attaches matching to an element", function () {
- provider.attachGestures(mockElement, mockDomainObject, ["a", "c", "e"]);
-
- expect(mockGestures.a).toHaveBeenCalledWith(mockElement, mockDomainObject);
- expect(mockGestures.c).toHaveBeenCalledWith(mockElement, mockDomainObject);
- expect(mockGestures.e).toHaveBeenCalledWith(mockElement, mockDomainObject);
- expect(mockGestures.b).not.toHaveBeenCalled();
- expect(mockGestures.d).not.toHaveBeenCalled();
-
- // No destroys should have been called - let's check
- GESTURE_KEYS.forEach(function (key) {
- expect(mockDestroys[key]).not.toHaveBeenCalled();
- });
- });
-
- it("detaches gestures on request", function () {
- provider.attachGestures(
- mockElement,
- mockDomainObject,
- ["b", "d", "e"]
- ).destroy();
-
- expect(mockDestroys.b).toHaveBeenCalled();
- expect(mockDestroys.d).toHaveBeenCalled();
- expect(mockDestroys.e).toHaveBeenCalled();
- expect(mockDestroys.a).not.toHaveBeenCalled();
- expect(mockDestroys.c).not.toHaveBeenCalled();
- });
- });
- }
-);
diff --git a/platform/representation/test/gestures/GestureRepresenterSpec.js b/platform/representation/test/gestures/GestureRepresenterSpec.js
deleted file mode 100644
index 242ae7596..000000000
--- a/platform/representation/test/gestures/GestureRepresenterSpec.js
+++ /dev/null
@@ -1,80 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../../src/gestures/GestureRepresenter"],
- function (GestureRepresenter) {
-
- describe("A gesture representer", function () {
- var mockGestureService,
- mockGestureHandle,
- mockElement,
- representer;
-
- beforeEach(function () {
- mockGestureService = jasmine.createSpyObj(
- "gestureService",
- ["attachGestures"]
- );
- mockGestureHandle = jasmine.createSpyObj(
- "gestureHandle",
- ["destroy"]
- );
-
- mockElement = { someKey: "some value" };
-
- mockGestureService.attachGestures.and.returnValue(mockGestureHandle);
-
- representer = new GestureRepresenter(
- mockGestureService,
- undefined, // Scope is not used
- mockElement
- );
- });
-
- it("attaches declared gestures, and detaches on request", function () {
- // Pass in some objects, which we expect to be passed into the
- // gesture service accordingly.
- var domainObject = { someOtherKey: "some other value" },
- representation = { gestures: ["a", "b", "c"] };
-
- representer.represent(representation, domainObject);
-
- expect(mockGestureService.attachGestures).toHaveBeenCalledWith(
- mockElement,
- domainObject,
- ["a", "b", "c"]
- );
-
- // Should not have been destroyed yet...
- expect(mockGestureHandle.destroy).not.toHaveBeenCalled();
-
- // Destroy
- representer.destroy();
-
- // Should have destroyed those old gestures
- expect(mockGestureHandle.destroy).toHaveBeenCalled();
- });
-
- });
- }
-);
diff --git a/platform/representation/test/services/DndServiceSpec.js b/platform/representation/test/services/DndServiceSpec.js
deleted file mode 100644
index 405c0b28e..000000000
--- a/platform/representation/test/services/DndServiceSpec.js
+++ /dev/null
@@ -1,65 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../../src/services/DndService"],
- function (DndService) {
-
- describe("The drag-and-drop service", function () {
- var service;
-
- beforeEach(function () {
- var mockLog = jasmine.createSpyObj("$log", ['debug']);
- service = new DndService(mockLog);
- });
-
- it("allows setting of arbitrary objects", function () {
- var foo = {
- bar: function () {
- return 42;
- }
- };
-
- service.setData('xyz', foo);
-
- // Test that we can get back callable data, since this is
- // a key reason for having a service separate from HTML5 DnD.
- expect(service.getData('xyz').bar()).toEqual(42);
- });
-
- it("stores data under specified keys", function () {
- service.setData('abc', 42);
- service.setData('def', "some data");
-
- expect(service.getData('abc')).toEqual(42);
- expect(service.getData('def')).toEqual("some data");
- });
-
- it("removes data", function () {
- service.setData('abc', 42);
- service.removeData('abc');
- expect(service.getData('abc')).toBeUndefined();
- });
-
- });
- }
-);
diff --git a/platform/status/README.md b/platform/status/README.md
deleted file mode 100644
index 4d8cb7e6f..000000000
--- a/platform/status/README.md
+++ /dev/null
@@ -1,2 +0,0 @@
-Facilitates tracking states associated with specific domain
-objects.
diff --git a/platform/status/bundle.js b/platform/status/bundle.js
deleted file mode 100644
index ce44561d7..000000000
--- a/platform/status/bundle.js
+++ /dev/null
@@ -1,63 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
- "./src/StatusRepresenter",
- "./src/StatusCapability",
- "./src/StatusService"
-], function (
- StatusRepresenter,
- StatusCapability,
- StatusService
-) {
-
- return {
- name: "platform/status",
- definition: {
- "extensions": {
- "representers": [
- {
- "implementation": StatusRepresenter
- }
- ],
- "capabilities": [
- {
- "key": "status",
- "implementation": StatusCapability,
- "depends": [
- "statusService"
- ]
- }
- ],
- "services": [
- {
- "key": "statusService",
- "implementation": StatusService,
- "depends": [
- "topic"
- ]
- }
- ]
- }
- }
- };
-});
diff --git a/platform/status/src/StatusCapability.js b/platform/status/src/StatusCapability.js
deleted file mode 100644
index 5f959da66..000000000
--- a/platform/status/src/StatusCapability.js
+++ /dev/null
@@ -1,98 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- /**
- * The `status` capability can be used to attach information
- * about the state of a domain object, expressed as simple
- * string flags.
- *
- * Representations of domain objects will also receive CSS
- * classes which reflect their current status.
- * (@see platform/status.StatusRepresenter)
- *
- * @param {platform/status.StatusService} statusService
- * the service which will track domain object status
- * within the application.
- * @param {DomainObject} the domain object whose status will
- * be tracked.
- * @constructor
- * @memberof platform/status
- */
- function StatusCapability(statusService, domainObject) {
- this.statusService = statusService;
- this.domainObject = domainObject;
- }
-
- /**
- * List all status flags currently set for this domain object.
- * @returns {string[]} all current status flags.
- */
- StatusCapability.prototype.list = function () {
- return this.statusService.listStatuses(this.domainObject.getId());
- };
-
- /**
- * Check if a status flag is currently set for this domain object.
- * @param {string} status the status to get
- * @returns {boolean} true if the flag is present, otherwise false
- */
- StatusCapability.prototype.get = function (status) {
- return this.list().indexOf(status) !== -1;
- };
-
- /**
- * Set a status flag on this domain object.
- * @param {string} status the status to set
- * @param {boolean} state true if the domain object should
- * possess this status, false if it should not
- */
- StatusCapability.prototype.set = function (status, state) {
- return this.statusService.setStatus(
- this.domainObject.getId(),
- status,
- state
- );
- };
-
- /**
- * Listen for changes in this domain object's status.
- * @param {Function} callback function to invoke on changes;
- * called with the new status of the domain object, as an
- * array of strings
- * @returns {Function} a function which can be used to stop
- * listening to status changes for this domain object.
- */
- StatusCapability.prototype.listen = function (callback) {
- return this.statusService.listen(
- this.domainObject.getId(),
- callback
- );
- };
-
- return StatusCapability;
-
- }
-);
diff --git a/platform/status/src/StatusConstants.js b/platform/status/src/StatusConstants.js
deleted file mode 100644
index 514a2158e..000000000
--- a/platform/status/src/StatusConstants.js
+++ /dev/null
@@ -1,25 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-define({
- CSS_CLASS_PREFIX: 's-status-',
- TOPIC_PREFIX: 'status:'
-});
diff --git a/platform/status/src/StatusRepresenter.js b/platform/status/src/StatusRepresenter.js
deleted file mode 100644
index bdb118c90..000000000
--- a/platform/status/src/StatusRepresenter.js
+++ /dev/null
@@ -1,93 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ['./StatusConstants'],
- function (StatusConstants) {
-
- var STATUS_CLASS_PREFIX = StatusConstants.CSS_CLASS_PREFIX;
-
- /**
- * Adds/removes CSS classes to `mct-representation`s to reflect the
- * current status of represented domain objects, as reported by
- * their `status` capability.
- *
- * Statuses are prefixed with `s-status-` to build CSS class names.
- * As such, when a domain object has the status "pending", its
- * representations will have the CSS class `s-status-pending`.
- *
- * @param {angular.Scope} scope the representation's scope object
- * @param element the representation's jqLite-wrapped DOM element
- * @implements {Representer}
- * @constructor
- * @memberof platform/status
- */
- function StatusRepresenter(scope, element) {
- this.element = element;
- this.lastClasses = [];
- }
-
- /**
- * Remove any status-related classes from this representation.
- * @private
- */
- StatusRepresenter.prototype.clearClasses = function () {
- var element = this.element;
- this.lastClasses.forEach(function (c) {
- element.removeClass(c);
- });
- };
-
- StatusRepresenter.prototype.represent = function (representation, domainObject) {
- var self = this,
- statusCapability = domainObject.getCapability('status');
-
- function updateStatus(flags) {
- var newClasses = flags.map(function (flag) {
- return STATUS_CLASS_PREFIX + flag;
- });
-
- self.clearClasses();
-
- newClasses.forEach(function (c) {
- self.element.addClass(c);
- });
-
- self.lastClasses = newClasses;
- }
-
- updateStatus(statusCapability.list());
- this.unlisten = statusCapability.listen(updateStatus);
- };
-
- StatusRepresenter.prototype.destroy = function () {
- this.clearClasses();
- if (this.unlisten) {
- this.unlisten();
- this.unlisten = undefined;
- }
- };
-
- return StatusRepresenter;
-
- }
-);
diff --git a/platform/status/src/StatusService.js b/platform/status/src/StatusService.js
deleted file mode 100644
index 7380fa54e..000000000
--- a/platform/status/src/StatusService.js
+++ /dev/null
@@ -1,91 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ['./StatusConstants'],
- function (StatusConstants) {
-
- var STATUS_PREFIX = StatusConstants.TOPIC_PREFIX;
-
- /**
- * The `statusService` maintains information about the current
- * status of specific domain objects within the system. Status
- * is represented as string flags which are present when a
- * domain object possesses that status, and false when it does
- * not.
- *
- * @param {platform/core.Topic} topic the `topic` service, used
- * to create/use named listeners.
- * @constructor
- * @memberof platform/status
- */
- function StatusService(topic) {
- this.statusTable = {};
- this.topic = topic;
- }
-
- /**
- * Get all status flags currently set for a domain object.
- * @param {string} id the identifier of the domain object
- * @returns {string[]} an array containing all status flags currently
- * applicable to the object with this identifier
- */
- StatusService.prototype.listStatuses = function (id) {
- return this.statusTable[id] || [];
- };
-
- /**
- * Set a status flag for a domain object.
- * @param {string} id the identifier of the domain object
- * @param {string} status the status to set
- * @param {boolean} state true if the domain object should
- * possess this status, false if it should not
- */
- StatusService.prototype.setStatus = function (id, status, state) {
- this.statusTable[id] = this.statusTable[id] || [];
- this.statusTable[id] = this.statusTable[id].filter(function (s) {
- return s !== status;
- });
- if (state) {
- this.statusTable[id].push(status);
- }
-
- this.topic(STATUS_PREFIX + id).notify(this.statusTable[id]);
- };
-
- /**
- * Listen for changes in a domain object's status.
- * @param {string} id the identifier of the domain object
- * @param {Function} callback function to invoke on changes;
- * called with the new status of the domain object, as an
- * array of strings
- * @returns {Function} a function which can be used to stop
- * listening to status changes for this domain object.
- */
- StatusService.prototype.listen = function (id, callback) {
- return this.topic(STATUS_PREFIX + id).listen(callback);
- };
-
- return StatusService;
-
- }
-);
diff --git a/platform/status/test/StatusCapabilitySpec.js b/platform/status/test/StatusCapabilitySpec.js
deleted file mode 100644
index 539d17f54..000000000
--- a/platform/status/test/StatusCapabilitySpec.js
+++ /dev/null
@@ -1,87 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../src/StatusCapability"],
- function (StatusCapability) {
-
- describe("The status capability", function () {
- var mockStatusService,
- mockDomainObject,
- mockUnlisten,
- testId,
- testStatusFlags,
- capability;
-
- beforeEach(function () {
- testId = "some-id";
- testStatusFlags = ['a', 'b', 'c'];
-
- mockStatusService = jasmine.createSpyObj(
- 'statusService',
- ['listen', 'setStatus', 'listStatuses']
- );
- mockDomainObject = jasmine.createSpyObj(
- 'domainObject',
- ['getId', 'getCapability', 'getModel']
- );
- mockUnlisten = jasmine.createSpy('unlisten');
-
- mockStatusService.listen.and.returnValue(mockUnlisten);
- mockStatusService.listStatuses.and.returnValue(testStatusFlags);
- mockDomainObject.getId.and.returnValue(testId);
-
- capability = new StatusCapability(
- mockStatusService,
- mockDomainObject
- );
- });
-
- it("sets status with the statusService", function () {
- var testStatus = "some-test-status";
- capability.set(testStatus, true);
- expect(mockStatusService.setStatus)
- .toHaveBeenCalledWith(testId, testStatus, true);
- capability.set(testStatus, false);
- expect(mockStatusService.setStatus)
- .toHaveBeenCalledWith(testId, testStatus, false);
- });
-
- it("gets status from the statusService", function () {
- expect(capability.list()).toBe(testStatusFlags);
- });
-
- it("listens to changes from the statusService", function () {
- var mockCallback = jasmine.createSpy();
- expect(capability.listen(mockCallback))
- .toBe(mockUnlisten);
- expect(mockStatusService.listen)
- .toHaveBeenCalledWith(testId, mockCallback);
- });
-
- it("allows statuses to be checked individually", function () {
- expect(capability.get('some-unset-status')).toBe(false);
- expect(capability.get(testStatusFlags[0])).toBe(true);
- });
- });
- }
-);
diff --git a/platform/status/test/StatusRepresenterSpec.js b/platform/status/test/StatusRepresenterSpec.js
deleted file mode 100644
index cf4bec92f..000000000
--- a/platform/status/test/StatusRepresenterSpec.js
+++ /dev/null
@@ -1,116 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../src/StatusRepresenter", "../src/StatusConstants"],
- function (StatusRepresenter, StatusConstants) {
-
- describe("The status representer", function () {
- var mockScope,
- mockElement,
- testRepresentation,
- mockDomainObject,
- mockStatusCapability,
- mockUnlisten,
- elementClasses,
- testStatusFlags,
- representer;
-
- function verifyClasses() {
- expect(Object.keys(elementClasses).sort())
- .toEqual(testStatusFlags.map(function (s) {
- return StatusConstants.CSS_CLASS_PREFIX + s;
- }).sort());
- }
-
- function updateStatus(newFlags) {
- testStatusFlags = newFlags;
- mockStatusCapability.get.and.returnValue(newFlags);
- mockStatusCapability.listen.calls.mostRecent()
- .args[0](newFlags);
- }
-
- beforeEach(function () {
- testStatusFlags = ['x', 'y', 'z'];
-
- mockScope = {};
- mockElement = jasmine.createSpyObj('element', [
- 'addClass',
- 'removeClass'
- ]);
- testRepresentation = { key: "someKey" };
- mockDomainObject = jasmine.createSpyObj(
- 'domainObject',
- ['getModel', 'getId', 'getCapability']
- );
- mockStatusCapability = jasmine.createSpyObj(
- 'status',
- ['list', 'get', 'set', 'listen']
- );
- mockUnlisten = jasmine.createSpy();
-
- elementClasses = {};
-
- mockElement.addClass.and.callFake(function (c) {
- elementClasses[c] = true;
- });
- mockElement.removeClass.and.callFake(function (c) {
- delete elementClasses[c];
- });
-
- mockStatusCapability.list.and.returnValue(testStatusFlags);
- mockStatusCapability.listen.and.returnValue(mockUnlisten);
-
- mockDomainObject.getCapability.and.callFake(function (c) {
- return c === 'status' && mockStatusCapability;
- });
-
- representer = new StatusRepresenter(mockScope, mockElement);
- representer.represent(testRepresentation, mockDomainObject);
- });
-
- it("listens for status changes", function () {
- expect(mockStatusCapability.listen)
- .toHaveBeenCalledWith(jasmine.any(Function));
- });
-
- it("initially sets classes to reflect status", verifyClasses);
-
- it("changes classes on status change callbacks", function () {
- updateStatus(['a', 'x', '123']);
- verifyClasses();
- });
-
- it("stops listening when destroyed", function () {
- expect(mockUnlisten).not.toHaveBeenCalled();
- representer.destroy();
- expect(mockUnlisten).toHaveBeenCalled();
- });
-
- it("removes status classes when destroyed", function () {
- expect(elementClasses).not.toEqual({});
- representer.destroy();
- expect(elementClasses).toEqual({});
- });
- });
- }
-);
diff --git a/platform/status/test/StatusServiceSpec.js b/platform/status/test/StatusServiceSpec.js
deleted file mode 100644
index efbdf2796..000000000
--- a/platform/status/test/StatusServiceSpec.js
+++ /dev/null
@@ -1,92 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../src/StatusService"],
- function (StatusService) {
-
- describe("The status service", function () {
- var mockTopic,
- mockTopicInstance,
- mockUnlisten,
- mockCallback,
- testId,
- testStatus,
- statusService;
-
- beforeEach(function () {
- testId = "some-domain-object-identifier";
- testStatus = "test-status";
-
- mockTopic = jasmine.createSpy('topic');
- mockTopicInstance = jasmine.createSpyObj(
- 'topicInstance',
- ['notify', 'listen']
- );
- mockUnlisten = jasmine.createSpy('unlisten');
- mockCallback = jasmine.createSpy('callback');
-
- mockTopic.and.returnValue(mockTopicInstance);
- mockTopicInstance.listen.and.returnValue(mockUnlisten);
-
- statusService = new StatusService(mockTopic);
- });
-
- it("initially contains no flags for an object", function () {
- expect(statusService.listStatuses(testId)).toEqual([]);
- });
-
- it("stores and clears status flags", function () {
- statusService.setStatus(testId, testStatus, true);
- expect(statusService.listStatuses(testId)).toEqual([testStatus]);
- statusService.setStatus(testId, testStatus, false);
- expect(statusService.listStatuses(testId)).toEqual([]);
- });
-
- it("uses topic to listen for changes", function () {
- expect(statusService.listen(testId, mockCallback))
- .toEqual(mockUnlisten);
- expect(mockTopic)
- .toHaveBeenCalledWith(jasmine.any(String));
- // Just care that the topic was somehow unique to the object
- expect(mockTopic.calls.mostRecent().args[0].indexOf(testId))
- .not.toEqual(-1);
- });
-
- it("notifies listeners of changes", function () {
- statusService.setStatus(testId, testStatus, true);
- expect(mockTopicInstance.notify)
- .toHaveBeenCalledWith([testStatus]);
- statusService.setStatus(testId, testStatus, false);
- expect(mockTopicInstance.notify)
- .toHaveBeenCalledWith([]);
-
- expect(mockTopic)
- .toHaveBeenCalledWith(jasmine.any(String));
- // Just care that the topic was somehow unique to the object
- expect(mockTopic.calls.mostRecent().args[0].indexOf(testId))
- .not.toEqual(-1);
- });
-
- });
- }
-);
diff --git a/platform/telemetry/README.md b/platform/telemetry/README.md
deleted file mode 100644
index dfcf85e2c..000000000
--- a/platform/telemetry/README.md
+++ /dev/null
@@ -1,2 +0,0 @@
-This bundle is responsible for introducing a reusable infrastructure
-and set of APIs for using time-series data in Open MCT.
diff --git a/platform/telemetry/bundle.js b/platform/telemetry/bundle.js
deleted file mode 100644
index 8edd0d7a3..000000000
--- a/platform/telemetry/bundle.js
+++ /dev/null
@@ -1,130 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
- "./src/TelemetryAggregator",
- "./src/TelemetryController",
- "./src/TelemetryCapability",
- "./src/TelemetryFormatter",
- "./src/TelemetrySubscriber",
- "./src/TelemetryHandler"
-], function (
- TelemetryAggregator,
- TelemetryController,
- TelemetryCapability,
- TelemetryFormatter,
- TelemetrySubscriber,
- TelemetryHandler
-) {
-
- return {
- name: "platform/telemetry",
- definition: {
- "name": "Data bundle",
- "description": "Interfaces and infrastructure for real-time and historical data",
- "configuration": {
- "paths": {
- "moment": "moment.min"
- },
- "shim": {
- "moment": {
- "exports": "moment"
- }
- }
- },
- "extensions": {
- "components": [
- {
- "provides": "telemetryService",
- "type": "aggregator",
- "implementation": TelemetryAggregator,
- "depends": [
- "$q"
- ]
- }
- ],
- "controllers": [
- {
- "key": "TelemetryController",
- "implementation": TelemetryController,
- "depends": [
- "$scope",
- "$q",
- "$timeout",
- "$log"
- ]
- }
- ],
- "capabilities": [
- {
- "key": "telemetry",
- "implementation": TelemetryCapability,
- "depends": [
- "openmct",
- "$injector",
- "$q",
- "$log"
- ]
- }
- ],
- "services": [
- {
- "key": "telemetryFormatter",
- "implementation": TelemetryFormatter,
- "depends": [
- "formatService",
- "DEFAULT_TIME_FORMAT"
- ]
- },
- {
- "key": "telemetrySubscriber",
- "implementation": TelemetrySubscriber,
- "depends": [
- "$q",
- "$timeout"
- ]
- },
- {
- "key": "telemetryHandler",
- "implementation": TelemetryHandler,
- "depends": [
- "$q",
- "telemetrySubscriber"
- ]
- }
- ],
- "licenses": [
- {
- "name": "Moment.js",
- "version": "2.11.1",
- "author": "Tim Wood, Iskren Chernev, Moment.js contributors",
- "description": "Time/date parsing/formatting",
- "website": "http://momentjs.com",
- "copyright": "Copyright (c) 2011-2014 Tim Wood, Iskren Chernev, Moment.js contributors",
- "license": "license-mit",
- "link": "https://raw.githubusercontent.com/moment/moment/develop/LICENSE"
- }
- ]
- }
- }
- };
-});
diff --git a/platform/telemetry/src/TelemetryAggregator.js b/platform/telemetry/src/TelemetryAggregator.js
deleted file mode 100644
index 444c7120a..000000000
--- a/platform/telemetry/src/TelemetryAggregator.js
+++ /dev/null
@@ -1,135 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * This bundle provides infrastructure and utility services for handling
- * telemetry data.
- * @namespace platform/telemetry
- */
-define(
- [],
- function () {
-
- /**
- * Describes a request for telemetry data. Note that responses
- * may contain either a sub- or superset of the requested data.
- * @typedef TelemetryRequest
- * @property {string} source an identifier for the relevant
- * source of telemetry data
- * @property {string} key an identifier for the specific
- * series of telemetry data provided by that source
- * @property {number} [start] the earliest domain value of
- * interest for that telemetry data; for time-based
- * domains, this is in milliseconds since the start
- * of 1970
- * @property {number} [end] the latest domain value of interest
- * for that telemetry data; for time-based domains,
- * this is in milliseconds since 1970
- * @property {string} [domain] the domain for the query; if
- * omitted, this will be whatever the "normal"
- * domain is for a given telemetry series (the
- * first domain from its metadata)
- * @property {number} [size] if set, indicates the maximum number
- * of data points of interest for this request (more
- * recent domain values will be preferred)
- */
-
- /**
- * Request telemetry data.
- * @param {TelemetryRequest[]} requests and array of
- * requests to be handled
- * @returns {Promise} a promise for telemetry data
- * which may (or may not, depending on
- * availability) satisfy the requests
- * @method TelemetryService#requestTelemetry
- */
- /**
- * Subscribe to streaming updates to telemetry data.
- * The provided callback will be invoked as new
- * telemetry becomes available; as an argument, it
- * will receive an object of key-value pairs, where
- * keys are source identifiers and values are objects
- * of key-value pairs, where keys are point identifiers
- * and values are TelemetrySeries objects containing
- * the latest streaming telemetry.
- * @param {Function} callback the callback to invoke
- * @param {TelemetryRequest[]} requests an array of
- * requests to be subscribed upon
- * @returns {Function} a function which can be called
- * to unsubscribe
- * @method TelmetryService#subscribe
- */
-
- /**
- * A telemetry aggregator makes many telemetry providers
- * appear as one.
- *
- * @memberof platform/telemetry
- * @constructor
- */
- function TelemetryAggregator($q, telemetryProviders) {
- this.$q = $q;
- this.telemetryProviders = telemetryProviders;
- }
-
- // Merge the results from many providers into one
- // result object.
- function mergeResults(results) {
- var merged = {};
-
- results.forEach(function (result) {
- Object.keys(result).forEach(function (k) {
- merged[k] = result[k];
- });
- });
-
- return merged;
- }
-
- // Request telemetry from all providers; once they've
- // responded, merge the results into one result object.
- TelemetryAggregator.prototype.requestTelemetry = function (requests) {
- return this.$q.all(this.telemetryProviders.map(function (provider) {
- return provider.requestTelemetry(requests);
- })).then(mergeResults);
- };
-
- // Subscribe to updates from all providers
- TelemetryAggregator.prototype.subscribe = function (callback, requests) {
- var unsubscribes = this.telemetryProviders.map(function (provider) {
- return provider.subscribe(callback, requests);
- });
-
- // Return an unsubscribe function that invokes unsubscribe
- // for all providers.
- return function () {
- unsubscribes.forEach(function (unsubscribe) {
- if (unsubscribe) {
- unsubscribe();
- }
- });
- };
- };
-
- return TelemetryAggregator;
- }
-);
diff --git a/platform/telemetry/src/TelemetryCapability.js b/platform/telemetry/src/TelemetryCapability.js
deleted file mode 100644
index cbba2e440..000000000
--- a/platform/telemetry/src/TelemetryCapability.js
+++ /dev/null
@@ -1,343 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Module defining TelemetryCapability. Created by vwoeltje on 11/12/14.
- */
-define(
- [
- 'objectUtils',
- 'lodash'
- ],
- function (
- objectUtils,
- _
- ) {
-
- const EMPTY_SERIES = {
- getPointCount: () => 0,
- getDomainValue: () => 0,
- getRangeValue: () => 0
- };
-
- /**
- * Provides metadata about telemetry associated with a
- * given domain object.
- *
- * @typedef TelemetryMetadata
- * @property {string} source the machine-readable identifier for
- * the source of telemetry data for this object; used by
- * {@link TelemetryService} implementations to determine
- * whether or not they provide data for this object.
- * @property {string} key the machine-readable identifier for
- * telemetry data associated with this specific object,
- * within that `source`.
- * @property {TelemetryDomainMetadata[]} domains supported domain
- * options for telemetry data associated with this object,
- * to use in interpreting a {@link TelemetrySeries}
- * @property {TelemetryRangeMetadata[]} ranges supported range
- * options for telemetry data associated with this object,
- * to use in interpreting a {@link TelemetrySeries}
- */
-
- /**
- * Provides metadata about range options within a telemetry series.
- * Range options describe distinct properties within any given datum
- * of a telemetry series; for instance, a telemetry series containing
- * both raw and uncalibrated values may provide separate ranges for
- * each.
- *
- * @typedef TelemetryRangeMetadata
- * @property {string} key machine-readable identifier for this range
- * @property {string} name human-readable name for this range
- * @property {string} [units] human-readable units for this range
- * @property {string} [format] data format for this range; usually,
- * one of `number`, or `string`. If `undefined`,
- * should presume to be a `number`. Custom formats
- * may be indicated here.
- */
-
- /**
- * Provides metadata about domain options within a telemetry series.
- * Domain options describe distinct properties within any given datum
- * of a telemtry series; for instance, a telemetry series containing
- * both spacecraft event time and earth received times may provide
- * separate domains for each.
- *
- * Domains are typically used to represent timestamps in a telemetry
- * series, but more generally may express any property which will
- * have unique values for each datum in a series. It is this property
- * which makes domains distinct from ranges, as it makes these values
- * appropriate and meaningful for use to sort and bound a series.
- *
- * @typedef TelemetryDomainMetadata
- * @property {string} key machine-readable identifier for this range
- * @property {string} name human-readable name for this range
- * @property {string} [system] machine-readable identifier for the
- * time/date system associated with this domain;
- * used by {@link DateService}
- */
-
- /**
- * A telemetry capability provides a means of requesting telemetry
- * for a specific object, and for unwrapping the response (to get
- * at the specific data which is appropriate to the domain object.)
- *
- * @memberof platform/telemetry
- * @implements {Capability}
- * @constructor
- */
- function TelemetryCapability(openmct, $injector, $q, $log, domainObject) {
- // We could depend on telemetryService directly, but
- // there isn't a platform implementation of this.
- this.initializeTelemetryService = function () {
- try {
- return (this.telemetryService =
- $injector.get("telemetryService"));
- } catch (e) {
- $log.info("Telemetry service unavailable");
-
- return (this.telemetryService = null);
- }
- };
-
- this.openmct = openmct;
- this.$q = $q;
- this.$log = $log;
- this.domainObject = domainObject;
- }
-
- // Build a request object. This takes the request that was
- // passed to the capability, and adds source, id, and key
- // fields associated with the object (from its type definition
- // and/or its model)
- TelemetryCapability.prototype.buildRequest = function (request) {
- // Start with any "telemetry" field in type; use that as a
- // basis for the request.
- var domainObject = this.domainObject,
- type = domainObject.getCapability("type"),
- typeRequest = (type && type.getDefinition().telemetry) || {},
- modelTelemetry = domainObject.getModel().telemetry,
- fullRequest = Object.create(typeRequest),
- newObject = objectUtils.toNewFormat(
- domainObject.getModel(),
- domainObject.getId()
- ),
- metadata = this.openmct.telemetry.getMetadata(newObject),
- bounds,
- timeSystem;
-
- // Add properties from the telemetry field of this
- // specific domain object.
- Object.keys(modelTelemetry).forEach(function (k) {
- fullRequest[k] = modelTelemetry[k];
- });
-
- // Add properties from this specific requestData call.
- Object.keys(request).forEach(function (k) {
- fullRequest[k] = request[k];
- });
-
- // Ensure an ID and key are present
- if (!fullRequest.id) {
- fullRequest.id = domainObject.getId();
- }
-
- if (!fullRequest.key) {
- fullRequest.key = domainObject.getId();
- }
-
- if (request.start === undefined && request.end === undefined) {
- bounds = this.openmct.time.bounds();
- fullRequest.start = bounds.start;
- fullRequest.end = bounds.end;
- }
-
- if (request.domain === undefined) {
- timeSystem = this.openmct.time.timeSystem();
- if (timeSystem !== undefined) {
- fullRequest.domain = timeSystem.key;
- }
- }
-
- if (!fullRequest.ranges) {
- fullRequest.ranges = metadata.valuesForHints(['range']);
- }
-
- if (!fullRequest.domains) {
- fullRequest.domains = metadata.valuesForHints(['domain']);
- }
-
- return fullRequest;
- };
-
- function asSeries(telemetry, defaultDomain, defaultRange, sourceMap) {
- function getValue(index, key) {
- return telemetry[index][sourceMap[key].source];
- }
-
- return {
- getRangeValue: function (index, range) {
- return getValue(index, range || defaultRange);
- },
- getDomainValue: function (index, domain) {
- return getValue(index, domain || defaultDomain);
- },
- getPointCount: function () {
- return telemetry.length;
- },
- getData: function () {
- return telemetry;
- }
- };
- }
-
- /**
- * Request telemetry data for this specific domain object.
- * @param {TelemetryRequest} [request] parameters for this
- * specific request
- * @returns {Promise} a promise for the resulting telemetry
- * object
- */
- TelemetryCapability.prototype.requestData = function requestTelemetry(request) {
- // Bring in any defaults from the object model
- var fullRequest = this.buildRequest(request || {});
- var source = fullRequest.source;
- var key = fullRequest.key;
- var telemetryService = this.telemetryService
- || this.initializeTelemetryService(); // Lazy initialization
-
- var domainObject = objectUtils.toNewFormat(this.domainObject.getModel(), this.domainObject.getId());
- var telemetryAPI = this.openmct.telemetry;
-
- var metadata = telemetryAPI.getMetadata(domainObject);
- var defaultDomain = metadata.valuesForHints(['domain'])[0].key;
- var defaultRange = metadata.valuesForHints(['range'])[0];
- defaultRange = defaultRange ? defaultRange.key : undefined;
-
- var sourceMap = _.keyBy(metadata.values(), 'key');
-
- var isLegacyProvider = telemetryAPI.findRequestProvider(domainObject)
- === telemetryAPI.legacyProvider;
-
- // Pull out the relevant field from the larger,
- // structured response.
- function getRelevantResponse(response) {
- return ((response || {})[source] || {})[key]
- || EMPTY_SERIES;
- }
-
- // Issue a request to the service
- function requestTelemetryFromService() {
- return telemetryService.requestTelemetry([fullRequest]);
- }
-
- if (isLegacyProvider) {
- // If a telemetryService is not available,
- // getTelemetryService() should reject, and this should
- // bubble through subsequent then calls.
- if (!telemetryService) {
- return Promise.reject(new Error('TelemetryService is not available'));
- }
-
- return requestTelemetryFromService().then(getRelevantResponse);
- } else {
- return telemetryAPI.request(domainObject, fullRequest).then(function (telemetry) {
- return asSeries(telemetry, defaultDomain, defaultRange, sourceMap);
- });
- }
- };
-
- /**
- * Get metadata about this domain object's associated
- * telemetry.
- * @returns {TelemetryMetadata} metadata about this object's telemetry
- */
- TelemetryCapability.prototype.getMetadata = function () {
- // metadata just looks like a request,
- // so use buildRequest to bring in both
- // type-level and object-level telemetry
- // properties
- return (this.metadata = this.metadata || this.buildRequest({}));
- };
-
- /**
- * Subscribe to updates to telemetry data for this domain
- * object.
- * @param {Function} callback a function to call when new
- * data becomes available; the telemetry series
- * containing the data will be given as an argument.
- * @param {TelemetryRequest} [request] parameters for the
- * subscription request
- */
- TelemetryCapability.prototype.subscribe = function subscribe(callback, request) {
- var fullRequest = this.buildRequest(request || {});
- var telemetryService = this.telemetryService
- || this.initializeTelemetryService(); // Lazy initialization
-
- var domainObject = objectUtils.toNewFormat(this.domainObject.getModel(), this.domainObject.getId());
- var telemetryAPI = this.openmct.telemetry;
-
- var metadata = telemetryAPI.getMetadata(domainObject);
- var defaultDomain = metadata.valuesForHints(['domain'])[0].key;
- var defaultRange = metadata.valuesForHints(['range'])[0];
- defaultRange = defaultRange ? defaultRange.key : undefined;
-
- var sourceMap = _.keyBy(metadata.values(), 'key');
-
- var isLegacyProvider = telemetryAPI.findSubscriptionProvider(domainObject)
- === telemetryAPI.legacyProvider;
-
- function update(telemetry) {
- callback(asSeries([telemetry], defaultDomain, defaultRange, sourceMap));
- }
-
- // Unpack the relevant telemetry series
- function updateLegacy(telemetries) {
- var source = fullRequest.source,
- key = fullRequest.key,
- result = ((telemetries || {})[source] || {})[key];
- if (result) {
- callback(result);
- }
- }
-
- // Avoid a loop here...
- if (isLegacyProvider) {
- return telemetryService
- && telemetryService.subscribe(updateLegacy, [fullRequest]);
- } else {
- return telemetryAPI.subscribe(domainObject, update, fullRequest);
- }
- };
-
- /**
- * The telemetry capability is applicable when a
- * domain object model has a "telemetry" field.
- */
- TelemetryCapability.appliesTo = function (model) {
- return Boolean(model && model.telemetry);
- };
-
- return TelemetryCapability;
- }
-);
diff --git a/platform/telemetry/src/TelemetryController.js b/platform/telemetry/src/TelemetryController.js
deleted file mode 100644
index beebbb186..000000000
--- a/platform/telemetry/src/TelemetryController.js
+++ /dev/null
@@ -1,404 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-/**
- * Module defining TelemetryController. Created by vwoeltje on 11/12/14.
- */
-define(
- [],
- function () {
-
- /**
- * Serves as a reusable controller for views (or parts of views)
- * which need to issue requests for telemetry data and use the
- * results
- *
- * @memberof platform/telemetry
- * @constructor
- * @deprecated use platform/telemetry.TelemetryHandler instead
- */
- function TelemetryController($scope, $q, $timeout, $log) {
-
- // Private to maintain in this scope
- var self = {
- // IDs of domain objects with telemetry
- ids: [],
-
- // Containers for latest responses (id->response)
- // Used internally; see buildResponseContainer
- // for format
- response: {},
-
- // Request fields (id->requests)
- request: {},
-
- // Number of outstanding requests
- pending: 0,
-
- // Array of object metadatas, for easy retrieval
- metadatas: [],
-
- // Interval at which to poll for new data
- interval: 1000,
-
- // Flag tracking whether or not a request
- // is in progress
- refreshing: false,
-
- // Used to track whether a new telemetryUpdate
- // is being issued.
- broadcasting: false,
-
- // Active subscriptions
- subscriptions: [],
-
- // Used for getTelemetryObjects; a reference is
- // stored so that this can be called in a watch
- telemetryObjects: [],
-
- // Whether or not this controller is active; once
- // scope is destroyed, polling should stop.
- active: true
- };
-
- // Broadcast that a telemetryUpdate has occurred.
- function doBroadcast() {
- // This may get called multiple times from
- // multiple objects, so set a flag to suppress
- // multiple simultaneous events from being
- // broadcast, then issue the actual broadcast
- // later (via $timeout)
- if (!self.broadcasting) {
- self.broadcasting = true;
- $timeout(function () {
- self.broadcasting = false;
- $scope.$broadcast("telemetryUpdate");
- });
- }
- }
-
- // Issue a request for new telemetry for one of the
- // objects being tracked by this controller
- function requestTelemetryForId(id, trackPending) {
- var responseObject = self.response[id],
- domainObject = responseObject.domainObject,
- telemetry = domainObject.getCapability('telemetry');
-
- // Callback for when data comes back
- function storeData(data) {
- self.pending -= trackPending ? 1 : 0;
- responseObject.data = data;
- doBroadcast();
- }
-
- self.pending += trackPending ? 1 : 0;
-
- // Shouldn't happen, but isn't fatal,
- // so warn.
- if (!telemetry) {
- $log.warn([
- "Expected telemetry capability for ",
- id,
- " but found none. Cannot request data."
- ].join(""));
-
- // Request won't happen, so don't
- // mark it as pending.
- self.pending -= trackPending ? 1 : 0;
-
- return;
- }
-
- // Issue the request using the object's telemetry capability
- return $q.when(telemetry.requestData(self.request))
- .then(storeData);
- }
-
- // Request telemetry for all objects tracked by this
- // controller. A flag is passed to indicate whether the
- // pending counter should be incremented (this will
- // cause isRequestPending() to change, which we only
- // want to happen for requests which have originated
- // outside of this controller's polling action.)
- function requestTelemetry(trackPending) {
- return $q.all(self.ids.map(function (id) {
- return requestTelemetryForId(id, trackPending);
- }));
- }
-
- // Subscribe to streaming telemetry updates
- function subscribe(telemetryCapability, id) {
- return telemetryCapability.subscribe(function () {
- requestTelemetryForId(id, false);
- });
- }
-
- // Stop listening to active subscriptions
- function unsubscribe() {
- self.subscriptions.forEach(function (s) {
- return s && s();
- });
- self.subscriptions = [];
- }
-
- // Look up domain objects which have telemetry capabilities.
- // This will either be the object in view, or object that
- // this object delegates its telemetry capability to.
- function promiseRelevantDomainObjects(domainObject) {
- // If object has been cleared, there are no relevant
- // telemetry-providing domain objects.
- if (!domainObject) {
- return $q.when([]);
- }
-
- // Otherwise, try delegation first, and attach the
- // object itself if it has a telemetry capability.
- return $q.when(domainObject.useCapability(
- "delegation",
- "telemetry"
- )).then(function (result) {
- var head = domainObject.hasCapability("telemetry")
- ? [domainObject] : [],
- tail = result || [];
-
- return head.concat(tail);
- });
- }
-
- // Build the response containers that are used internally
- // by this controller to track latest responses, etc, for
- // a given domain object.
- function buildResponseContainer(domainObject) {
- var telemetry = domainObject
- && domainObject.getCapability("telemetry"),
- id,
- metadata;
-
- if (telemetry) {
- id = domainObject.getId();
-
- self.subscriptions.push(subscribe(telemetry, id));
-
- metadata = telemetry.getMetadata();
-
- self.response[id] = {
- name: domainObject.getModel().name,
- domainObject: domainObject,
- metadata: metadata,
- pending: 0,
- data: {}
- };
- } else {
- // Shouldn't happen, as we've checked for
- // telemetry capabilities previously, but
- // be defensive.
- $log.warn([
- "Expected telemetry capability for ",
- domainObject.getId(),
- " but none was found."
- ].join(""));
-
- // Create an empty container so subsequent
- // behavior won't hit an exception.
- self.response[domainObject.getId()] = {
- name: domainObject.getModel().name,
- domainObject: domainObject,
- metadata: {},
- pending: 0,
- data: {}
- };
- }
- }
-
- // Build response containers (as above) for all
- // domain objects, and update some controller-internal
- // state to support subsequent calls.
- function buildResponseContainers(domainObjects) {
- // Clear out any existing subscriptions
- unsubscribe();
-
- // Build the containers
- domainObjects.forEach(buildResponseContainer);
-
- // Store the reference to support getTelemetryObjects
- self.telemetryObjects = domainObjects;
-
- // Maintain a list of relevant ids, to convert
- // back from dictionary-like container objects to arrays.
- self.ids = domainObjects.map(function (obj) {
- return obj.getId();
- });
-
- // Keep a reference to all applicable metadata
- // to return from getMetadata
- self.metadatas = self.ids.map(function (id) {
- return self.response[id].metadata;
- });
-
- // Issue a request for the new objects, if we
- // know what our request looks like
- if (self.request) {
- requestTelemetry(true);
- }
- }
-
- // Get relevant telemetry-providing domain objects
- // for the domain object which is represented in this
- // scope. This will be the domain object itself, or
- // its telemetry delegates, or both.
- function getTelemetryObjects(domainObject) {
- unsubscribe();
- promiseRelevantDomainObjects(domainObject)
- .then(buildResponseContainers);
- }
-
- // Handle a polling refresh interval
- function startTimeout() {
- if (!self.refreshing && self.interval !== undefined) {
- self.refreshing = true;
- $timeout(function () {
- if (self.request) {
- requestTelemetry(false);
- }
-
- self.refreshing = false;
-
- if (self.active) {
- startTimeout();
- }
- }, self.interval);
- }
- }
-
- // Stop polling for changes
- function deactivate() {
- unsubscribe();
- self.active = false;
- }
-
- // Watch for a represented domain object
- $scope.$watch("domainObject", getTelemetryObjects);
-
- // Stop polling when destroyed
- $scope.$on("$destroy", deactivate);
-
- // Begin polling for data changes
- startTimeout();
-
- return {
- /**
- * Get all telemetry metadata associated with
- * telemetry-providing domain objects managed by
- * this controller.
- *
- * This will ordered in the
- * same manner as `getTelemetryObjects()` or
- * `getResponse()`; that is, the metadata at a
- * given index will correspond to the telemetry-providing
- * domain object at the same index.
- * @returns {Array} an array of metadata objects
- * @memberof platform/telemetry.TelemetryController#
- */
- getMetadata: function () {
- return self.metadatas;
- },
- /**
- * Get all telemetry-providing domain objects managed by
- * this controller.
- *
- * This will ordered in the
- * same manner as `getMetadata()` or
- * `getResponse()`; that is, the metadata at a
- * given index will correspond to the telemetry-providing
- * domain object at the same index.
- * @returns {DomainObject[]} an array of metadata objects
- * @memberof platform/telemetry.TelemetryController#
- */
- getTelemetryObjects: function () {
- return self.telemetryObjects;
- },
- /**
- * Get the latest telemetry response for a specific
- * domain object (if an argument is given) or for all
- * objects managed by this controller (if no argument
- * is supplied.)
- *
- * In the first form, this returns a single object; in
- * the second form, it returns an array ordered in
- * same manner as `getMetadata()` or
- * `getTelemetryObjects()`; that is, the telemetry
- * response at a given index will correspond to the
- * telemetry-providing domain object at the same index.
- * @returns {Array} an array of responses
- * @memberof platform/telemetry.TelemetryController#
- */
- getResponse: function getResponse(arg) {
- var id = arg && (typeof arg === 'string'
- ? arg : arg.getId());
-
- if (id) {
- return (self.response[id] || {}).data;
- }
-
- return (self.ids || []).map(getResponse);
- },
- /**
- * Check if the latest request (not counting
- * requests from TelemtryController's own polling)
- * is still outstanding. Users of the TelemetryController
- * may use this method as a condition upon which to
- * show user feedback, such as a wait spinner.
- *
- * @returns {boolean} true if the request is still outstanding
- * @memberof platform/telemetry.TelemetryController#
- */
- isRequestPending: function () {
- return self.pending > 0;
- },
- /**
- * Issue a new data request. This will change the
- * request parameters that are passed along to all
- * telemetry capabilities managed by this controller.
- * @memberof platform/telemetry.TelemetryController#
- */
- requestData: function (request) {
- self.request = request || {};
-
- return requestTelemetry(true);
- },
- /**
- * Change the interval at which this controller will
- * perform its polling activity.
- * @param {number} durationMillis the interval at
- * which to poll, in milliseconds
- * @memberof platform/telemetry.TelemetryController#
- */
- setRefreshInterval: function (durationMillis) {
- self.interval = durationMillis;
- startTimeout();
- }
- };
- }
-
- return TelemetryController;
- }
-);
diff --git a/platform/telemetry/src/TelemetryDelegator.js b/platform/telemetry/src/TelemetryDelegator.js
deleted file mode 100644
index f284f63a0..000000000
--- a/platform/telemetry/src/TelemetryDelegator.js
+++ /dev/null
@@ -1,71 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- /**
- * Used to handle telemetry delegation associated with a
- * given domain object.
- * @constructor
- * @memberof platform/telemetry
- */
- function TelemetryDelegator($q) {
- this.$q = $q;
- }
-
- /**
- * Promise telemetry-providing objects associated with
- * this domain object (either the domain object itself,
- * or the objects it delegates)
- * @param {DomainObject} the domain object which may have
- * or delegate telemetry
- * @returns {Promise.<DomainObject[]>} domain objects with
- * a telemetry capability
- */
- TelemetryDelegator.prototype.promiseTelemetryObjects = function (domainObject) {
- var $q = this.$q;
-
- // If object has been cleared, there are no relevant
- // telemetry-providing domain objects.
- if (!domainObject) {
- return $q.when([]);
- }
-
- // Otherwise, try delegation first, and attach the
- // object itself if it has a telemetry capability.
- return $q.when(domainObject.useCapability(
- "delegation",
- "telemetry"
- )).then(function (result) {
- var head = domainObject.hasCapability("telemetry")
- ? [domainObject] : [],
- tail = result || [];
-
- return head.concat(tail);
- });
- };
-
- return TelemetryDelegator;
- }
-);
diff --git a/platform/telemetry/src/TelemetryFormatter.js b/platform/telemetry/src/TelemetryFormatter.js
deleted file mode 100644
index 09389bd3d..000000000
--- a/platform/telemetry/src/TelemetryFormatter.js
+++ /dev/null
@@ -1,75 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- /**
- * The TelemetryFormatter is responsible for formatting (as text
- * for display) values along either the domain (usually time) or
- * the range (usually value) of a data series.
- * @memberof platform/telemetry
- * @constructor
- * @param {FormatService} formatService the service to user to format
- * domain values
- * @param {string} defaultFormatKey the format to request when no
- * format has been otherwise specified
- */
- function TelemetryFormatter(formatService, defaultFormatKey) {
- this.formatService = formatService;
- this.defaultFormat = formatService.getFormat(defaultFormatKey);
- }
-
- /**
- * Format a domain value.
- * @param {number} v the domain value; usually, a timestamp
- * in milliseconds since start of 1970
- * @param {string} [key] a key which identifies the format
- * to use
- * @returns {string} a textual representation of the
- * data and time, suitable for display.
- */
- TelemetryFormatter.prototype.formatDomainValue = function (v, key) {
- var formatter = (key === undefined)
- ? this.defaultFormat
- : this.formatService.getFormat(key);
-
- return isNaN(v) ? "" : formatter.format(v);
- };
-
- /**
- * Format a range value.
- * @param {number} v the range value; a numeric value
- * @param {string} [key] the key which identifies the
- * range; if unspecified or unknown, this will
- * be treated as a numeric value.
- * @returns {string} a textual representation of the
- * value, suitable for display.
- */
- TelemetryFormatter.prototype.formatRangeValue = function (v, key) {
- return String(v);
- };
-
- return TelemetryFormatter;
- }
-);
diff --git a/platform/telemetry/src/TelemetryHandle.js b/platform/telemetry/src/TelemetryHandle.js
deleted file mode 100644
index 64d1fdfb6..000000000
--- a/platform/telemetry/src/TelemetryHandle.js
+++ /dev/null
@@ -1,147 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- /**
- * A telemetry handle acts as a helper in issuing requests for
- * new telemetry as well as subscribing to real-time updates
- * for the same telemetry series. This is exposed through the
- * `telemetryHandler` service.
- * @param $q Angular's $q, for promises
- * @param {TelemetrySubscription} subscription a subscription
- * to supplied telemetry
- * @constructor
- * @memberof platform/telemetry
- */
- function TelemetryHandle($q, subscription) {
- var seriesMap = {},
- active = true,
- self = Object.create(subscription);
-
- // Request a telemetry series for this specific object
- function requestSeries(telemetryObject, request, callback) {
- var id = telemetryObject.getId(),
- telemetry = telemetryObject.getCapability('telemetry');
-
- function receiveSeries(series) {
- // Store it for subsequent lookup
- seriesMap[id] = series;
- // Notify callback of new series data, if there is one
- if (callback && active) {
- callback(telemetryObject, series);
- }
-
- // Pass it along for promise-chaining
- return series;
- }
-
- // Issue the request via the object's telemetry capability
- return telemetry.requestData(request).then(receiveSeries);
- }
-
- self.unsubscribe = function () {
- active = false;
-
- return subscription.unsubscribe();
- };
-
- /**
- * Get the most recently obtained telemetry data series associated
- * with this domain object.
- * @param {DomainObject} the domain object which has telemetry
- * data associated with it
- * @return {TelemetrySeries} the most recent telemetry series
- * (or undefined if there is not one)
- * @memberof platform/telemetry.TelemetryHandle#
- */
- self.getSeries = function (domainObject) {
- var id = domainObject.getId();
-
- return seriesMap[id];
- };
-
- /**
- * Change the request duration.
- * @param {TelemetryRequest} request the request to issue
- * @param {Function} [callback] a callback that will be
- * invoked as new data becomes available, with the
- * domain object for which new data is available.
- * @memberof platform/telemetry.TelemetryHandle#
- */
- self.request = function (request, callback) {
- // Issue (and handle) the new request from this object
- function issueRequest(telemetryObject) {
- return requestSeries(telemetryObject, request, callback);
- }
-
- // Map the request to all telemetry objects
- function issueRequests(telemetryObjects) {
- return $q.all(telemetryObjects.map(issueRequest));
- }
-
- // If the request is a simple number, treat it as a duration
- request = (typeof request === 'number')
- ? { duration: request } : request;
-
- // Look up telemetry-providing objects from the subscription,
- // then issue new requests.
- return subscription.promiseTelemetryObjects()
- .then(issueRequests);
- };
-
- /**
- * Get the latest telemetry datum for this domain object. This
- * will be from real-time telemetry, unless an index is specified,
- * in which case it will be pulled from the historical telemetry
- * series at the specified index. If there is no latest available
- * datum, this will return undefined.
- *
- * @param {DomainObject} domainObject the object of interest
- * @param {number} [index] the index of the data of interest
- * @returns {TelemetryDatum} the most recent datum
- */
- self.getDatum = function (telemetryObject, index) {
- function makeNewDatum(series) {
- if (series) {
- if (series.getDatum) {
- return series.getDatum(index);
- }
-
- return subscription.makeDatum(telemetryObject, series, index);
- }
- }
-
- return typeof index !== 'number'
- ? subscription.getDatum(telemetryObject)
- : makeNewDatum(this.getSeries(telemetryObject));
- };
-
- return self;
- }
-
- return TelemetryHandle;
-
- }
-);
diff --git a/platform/telemetry/src/TelemetryHandler.js b/platform/telemetry/src/TelemetryHandler.js
deleted file mode 100644
index 9bb818543..000000000
--- a/platform/telemetry/src/TelemetryHandler.js
+++ /dev/null
@@ -1,64 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ['./TelemetryHandle'],
- function (TelemetryHandle) {
-
- /**
- * A TelemetryRequester provides an easy interface to request
- * telemetry associated with a set of domain objects.
- *
- * @memberof platform/telemetry
- * @constructor
- * @param $q Angular's $q
- */
- function TelemetryHandler($q, telemetrySubscriber) {
- this.$q = $q;
- this.telemetrySubscriber = telemetrySubscriber;
- }
-
- /**
- * Start receiving telemetry associated with this domain object
- * (either directly, or via delegation.)
- * @param {DomainObject} domainObject the domain object
- * @param {Function} callback callback to invoke when new data is
- * available
- * @param {boolean} lossless true if the callback should be invoked
- * one separate time for each new latest value
- * @returns {TelemetryHandle} a handle to telemetry data
- * associated with this object
- */
- TelemetryHandler.prototype.handle = function (domainObject, callback, lossless) {
- var subscription = this.telemetrySubscriber.subscribe(
- domainObject,
- callback,
- lossless
- );
-
- return new TelemetryHandle(this.$q, subscription);
- };
-
- return TelemetryHandler;
-
- }
-);
diff --git a/platform/telemetry/src/TelemetryQueue.js b/platform/telemetry/src/TelemetryQueue.js
deleted file mode 100644
index 298c4eed3..000000000
--- a/platform/telemetry/src/TelemetryQueue.js
+++ /dev/null
@@ -1,121 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- /**
- * Supports TelemetrySubscription. Provides a simple data structure
- * (with a pool-like interface) that aggregates key-value pairs into
- * a queued series of large objects, ensuring that no value is
- * overwritten (but consolidated non-overlapping keys into single
- * objects.)
- * @memberof platform/telemetry
- * @constructor
- * @implements {platform/telemetry.TelemetryPool}
- */
- function TelemetryQueue() {
- // General approach here:
- // * Maintain a queue as an array of objects containing key-value
- // pairs. Putting values into the queue will assign to the
- // earliest-available queue position for the associated key
- // (appending to the array if necessary.)
- // * Maintain a set of counts for each key, such that determining
- // the next available queue position is easy; O(1) insertion.
- // * When retrieving objects, pop off the queue and decrement
- // counts. This provides O(n+k) or O(k) retrieval for a queue
- // of length n with k unique keys; this depends on whether
- // the browser's implementation of Array.prototype.shift is
- // O(n) or O(1).
-
- // Graphically (indexes at top, keys along side, values as *'s),
- // if we have a queue that looks like:
- // 0 1 2 3 4
- // a * * * * *
- // b * *
- // c * * *
- //
- // And we put a new value for b, we expect:
- // 0 1 2 3 4
- // a * * * * *
- // b * * *
- // c * * *
-
- this.queue = [];
- this.counts = {};
- }
-
- TelemetryQueue.prototype.isEmpty = function () {
- return this.queue.length < 1;
- };
-
- TelemetryQueue.prototype.poll = function () {
- var counts = this.counts;
-
- // Decrement counts for a specific key
- function decrementCount(key) {
- if (counts[key] < 2) {
- delete counts[key];
- } else {
- counts[key] -= 1;
- }
- }
-
- // Decrement counts for the object that will be popped
- Object.keys(counts).forEach(decrementCount);
-
- return this.queue.shift();
- };
-
- TelemetryQueue.prototype.put = function (key, value) {
- var queue = this.queue,
- counts = this.counts;
-
- // Look up an object in the queue that does not have a value
- // assigned to this key (or, add a new one)
- function getFreeObject(k) {
- var index = counts[k] || 0, object;
-
- // Track the largest free position for this key
- counts[k] = index + 1;
-
- // If it's before the end of the queue, add it there
- if (index < queue.length) {
- return queue[index];
- }
-
- // Otherwise, values have been assigned
- // to that key in all queued containers, so we need to queue
- // up a new container for key-value pairs.
- object = {};
- queue.push(object);
-
- return object;
- }
-
- getFreeObject(key)[key] = value;
- };
-
- return TelemetryQueue;
- }
-);
diff --git a/platform/telemetry/src/TelemetrySubscriber.js b/platform/telemetry/src/TelemetrySubscriber.js
deleted file mode 100644
index e57fd9f47..000000000
--- a/platform/telemetry/src/TelemetrySubscriber.js
+++ /dev/null
@@ -1,77 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["./TelemetrySubscription"],
- function (TelemetrySubscription) {
-
- /**
- * The TelemetrySubscriber is a service which allows
- * subscriptions to be made for new data associated with
- * domain objects. It is exposed as a service named
- * `telemetrySubscriber`.
- *
- * Subscriptions may also be made directly using the
- * `telemetry` capability of a domain object; the subscriber
- * uses this as well, but additionally handles delegation
- * (e.g. for telemetry panels) as well as latest-value
- * extraction.
- *
- * @memberof platform/telemetry
- * @constructor
- * @param $q Angular's $q
- * @param $timeout Angular's $timeout
- */
- function TelemetrySubscriber($q, $timeout) {
- this.$q = $q;
- this.$timeout = $timeout;
- }
-
- /**
- * Subscribe to streaming telemetry updates
- * associated with this domain object (either
- * directly or via capability delegation.)
- *
- * @param {DomainObject} domainObject the object whose
- * associated telemetry data is of interest
- * @param {Function} callback a function to invoke
- * when new data has become available.
- * @param {boolean} lossless flag to indicate whether the
- * callback should be notified for all values
- * (otherwise, multiple values in quick succession
- * will call back with only the latest value.)
- * @returns {platform/telemetry.TelemetrySubscription} the
- * subscription, which will provide access to latest values.
- */
- TelemetrySubscriber.prototype.subscribe = function (domainObject, callback, lossless) {
- return new TelemetrySubscription(
- this.$q,
- this.$timeout,
- domainObject,
- callback,
- lossless
- );
- };
-
- return TelemetrySubscriber;
- }
-);
diff --git a/platform/telemetry/src/TelemetrySubscription.js b/platform/telemetry/src/TelemetrySubscription.js
deleted file mode 100644
index 28b1b7734..000000000
--- a/platform/telemetry/src/TelemetrySubscription.js
+++ /dev/null
@@ -1,388 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ['./TelemetryQueue', './TelemetryTable', './TelemetryDelegator'],
- function (TelemetryQueue, TelemetryTable, TelemetryDelegator) {
-
- /**
- * A pool of telemetry values.
- * @interface platform/telemetry.TelemetryPool
- * @private
- */
- /**
- * Check if any value groups remain in this pool.
- * @return {boolean} true if value groups remain
- * @method platform/telemetry.TelemetryPool#isEmpty
- */
- /**
- * Retrieve the next value group from this pool.
- * This gives an object containing key-value pairs,
- * where keys and values correspond to the arguments
- * given to previous put functions.
- * @return {object} key-value pairs
- * @method platform/telemetry.TelemetryPool#poll
- */
- /**
- * Put a key-value pair into the pool.
- * @param {string} key the key to store the value under
- * @param {*} value the value to store
- * @method platform/telemetry.TelemetryPool#put
- */
-
- /**
- * A TelemetrySubscription tracks latest values for streaming
- * telemetry data and handles notifying interested observers.
- * It implements the interesting behavior behind the
- * `telemetrySubscriber` service.
- *
- * Subscriptions may also be made directly using the
- * `telemetry` capability of a domain object; the subscriber
- * uses this as well, but additionally handles delegation
- * (e.g. for telemetry panels) as well as latest-value
- * extraction.
- *
- * @memberof platform/telemetry
- * @constructor
- * @param $q Angular's $q
- * @param $timeout Angular's $timeout
- * @param {DomainObject} domainObject the object whose
- * associated telemetry data is of interest
- * @param {Function} callback a function to invoke
- * when new data has become available.
- * @param {boolean} lossless true if callback should be invoked
- * once with every data point available; otherwise, multiple
- * data events in a short period of time will only invoke
- * the callback once, with access to the latest data
- */
- function TelemetrySubscription($q, $timeout, domainObject, callback, lossless) {
- var self = this,
- delegator = new TelemetryDelegator($q),
- pool = lossless ? new TelemetryQueue() : new TelemetryTable(),
- updatePending;
-
- // Look up domain objects which have telemetry capabilities.
- // This will either be the object in view, or object that
- // this object delegates its telemetry capability to.
- function promiseRelevantObjects(domainObj) {
- return delegator.promiseTelemetryObjects(domainObj);
- }
-
- function updateValuesFromPool() {
- var values = pool.poll();
- Object.keys(values).forEach(function (k) {
- self.latestValues[k] = values[k];
- });
- }
-
- // Invoke the observer callback to notify that new streaming
- // data has become available.
- function fireCallback() {
- // Play back from queue if we are lossless
- while (!pool.isEmpty()) {
- updateValuesFromPool();
- // Fire callback, if one was provided
- if (callback) {
- callback();
- }
- }
-
- // Clear the pending flag so that future updates will
- // schedule this callback.
- updatePending = false;
- }
-
- // Look up metadata associated with an object's telemetry
- function lookupMetadata(domainObj) {
- var telemetryCapability =
- domainObj.getCapability("telemetry");
-
- return telemetryCapability
- && telemetryCapability.getMetadata();
- }
-
- // Update the latest telemetry data for a specific
- // domain object. This will notify listeners.
- function update(domainObj, series) {
- var count = series && series.getPointCount();
-
- // Only schedule notification if there isn't already
- // a notification pending (and if we actually have
- // data)
- if (!updatePending && count) {
- updatePending = true;
- $timeout(fireCallback, 0);
- }
-
- // Update the latest-value table
- if (count > 0) {
- pool.put(domainObj.getId(), {
- domain: series.getDomainValue(count - 1),
- range: series.getRangeValue(count - 1),
- datum: self.makeDatum(domainObj, series, count - 1)
- });
- }
- }
-
- // Prepare a subscription to a specific telemetry-providing
- // domain object.
- function subscribe(domainObj) {
- var telemetryCapability =
- domainObj.getCapability("telemetry");
-
- return telemetryCapability.subscribe(function (telemetry) {
- update(domainObj, telemetry);
- });
- }
-
- // Prepare subscriptions to all relevant telemetry-providing
- // domain objects.
- function subscribeAll(domainObjects) {
- return domainObjects.map(subscribe);
- }
-
- // Cache a reference to all relevant telemetry-providing
- // domain objects. This will be called during the
- // initial subscription chain; this allows `getTelemetryObjects()`
- // to return a non-Promise to simplify usage elsewhere.
- function cacheObjectReferences(objects) {
- self.telemetryObjects = objects;
- self.metadatas = objects.map(lookupMetadata);
-
- self.metadataById = {};
- objects.forEach(function (obj, i) {
- self.metadataById[obj.getId()] = self.metadatas[i];
- });
- // Fire callback, as this will be the first time that
- // telemetry objects are available, or these objects
- // will have changed.
- if (callback) {
- callback();
- }
-
- return objects;
- }
-
- function initialize() {
- // Get a reference to relevant objects (those with telemetry
- // capabilities) and subscribe to their telemetry updates.
- // Keep a reference to their promised return values, as these
- // will be unsubscribe functions. (This must be a promise
- // because delegation is supported, and retrieving delegate
- // telemetry-capable objects may be an asynchronous operation.)
- self.telemetryObjectPromise = promiseRelevantObjects(domainObject);
- self.unsubscribePromise = self.telemetryObjectPromise
- .then(cacheObjectReferences)
- .then(subscribeAll);
- }
-
- function idsMatch(ids) {
- return ids.length === self.telemetryObjects.length
- && ids.every(function (id, index) {
- return self.telemetryObjects[index].getId() === id;
- });
- }
-
- function modelChange(model) {
- if (!idsMatch((model || {}).composition || [])) {
- // Reinitialize if composition has changed
- self.unsubscribeAll().then(initialize);
- }
- }
-
- function addMutationListener() {
- var mutation = domainObject
- && domainObject.getCapability('mutation');
- if (mutation) {
- return mutation.listen(modelChange);
- }
- }
-
- this.$q = $q;
- this.latestValues = {};
- this.telemetryObjects = [];
- this.metadatas = [];
-
- initialize();
- this.unlistenToMutation = addMutationListener();
- }
-
- /**
- * From a telemetry series, retrieve a single data point
- * containing all fields for domains/ranges
- * @private
- */
- TelemetrySubscription.prototype.makeDatum = function (domainObject, series, index) {
- var id = domainObject && domainObject.getId(),
- metadata = (id && this.metadataById[id]) || {},
- result = {};
-
- (metadata.domains || []).forEach(function (domain) {
- result[domain.key] =
- series.getDomainValue(index, domain.key);
- });
-
- (metadata.ranges || []).forEach(function (range) {
- result[range.key] =
- series.getRangeValue(index, range.key);
- });
-
- return result;
- };
-
- /**
- * Terminate all underlying subscriptions.
- * @private
- */
- TelemetrySubscription.prototype.unsubscribeAll = function () {
- var $q = this.$q;
-
- return this.unsubscribePromise.then(function (unsubscribes) {
- return $q.all(unsubscribes.map(function (unsubscribe) {
- return unsubscribe();
- }));
- });
- };
-
- /**
- * Terminate all underlying subscriptions associated
- * with this object.
- */
- TelemetrySubscription.prototype.unsubscribe = function () {
- if (this.unlistenToMutation) {
- this.unlistenToMutation();
- }
-
- return this.unsubscribeAll();
- };
-
- /**
- * Get the most recent domain value that has been observed
- * for the specified domain object. This will typically be
- * a timestamp.
- *
- * The domain object passed here should be one that is
- * subscribed-to here; that is, it should be one of the
- * domain objects returned by `getTelemetryObjects()`.
- *
- * @param {DomainObject} domainObject the object of interest
- * @param {string} [key] the symbolic identifier of the domain
- * to look up; if omitted, the value for this object's
- * default domain will be used
- * @returns the most recent domain value observed
- */
- TelemetrySubscription.prototype.getDomainValue = function (domainObject, key) {
- var id = domainObject.getId(),
- latestValue = this.latestValues[id];
-
- return latestValue && (key
- ? latestValue.datum[key]
- : latestValue.domain);
- };
-
- /**
- * Get the most recent range value that has been observed
- * for the specified domain object. This will typically
- * be a numeric measurement.
- *
- * The domain object passed here should be one that is
- * subscribed-to here; that is, it should be one of the
- * domain objects returned by `getTelemetryObjects()`.
- *
- * @param {DomainObject} domainObject the object of interest
- * @param {string} [key] the symbolic identifier of the range
- * to look up; if omitted, the value for this object's
- * default range will be used
- * @returns the most recent range value observed
- */
- TelemetrySubscription.prototype.getRangeValue = function (domainObject, key) {
- var id = domainObject.getId(),
- latestValue = this.latestValues[id];
-
- return latestValue && (key
- ? latestValue.datum[key]
- : latestValue.range);
- };
-
- /**
- * Get the latest telemetry datum for this domain object.
- *
- * @param {DomainObject} domainObject the object of interest
- * @returns {TelemetryDatum} the most recent datum
- */
- TelemetrySubscription.prototype.getDatum = function (domainObject) {
- var id = domainObject.getId();
-
- return (this.latestValues[id] || {}).datum;
- };
-
- /**
- * Get all telemetry-providing domain objects which are
- * being observed as part of this subscription.
- *
- * Capability delegation will be taken into account (so, if
- * a Telemetry Panel was passed in the constructor, this will
- * return its contents.) Capability delegation is resolved
- * asynchronously so the return value here may change over
- * time; while this resolution is pending, this method will
- * return an empty array.
- *
- * @returns {DomainObject[]} all subscribed-to domain objects
- */
- TelemetrySubscription.prototype.getTelemetryObjects = function () {
- return this.telemetryObjects;
- };
-
- /**
- * Get all telemetry metadata associated with
- * telemetry-providing domain objects managed by
- * this controller.
- *
- * This will ordered in the
- * same manner as `getTelemetryObjects()` or
- * `getResponse()`; that is, the metadata at a
- * given index will correspond to the telemetry-providing
- * domain object at the same index.
- * @returns {TelemetryMetadata[]} an array of metadata objects
- */
- TelemetrySubscription.prototype.getMetadata = function () {
- return this.metadatas;
- };
-
- /**
- * Get a promise for all telemetry-providing objects
- * associated with this subscription.
- * @returns {Promise.<DomainObject[]>} a promise for
- * telemetry-providing objects
- * @memberof platform/telemetry.TelemetrySubscription#
- */
- TelemetrySubscription.prototype.promiseTelemetryObjects = function () {
- // Unsubscribe promise is available after objects
- // are loaded.
- return this.telemetryObjectPromise;
- };
-
- return TelemetrySubscription;
-
- }
-);
-
diff --git a/platform/telemetry/src/TelemetryTable.js b/platform/telemetry/src/TelemetryTable.js
deleted file mode 100644
index 363755444..000000000
--- a/platform/telemetry/src/TelemetryTable.js
+++ /dev/null
@@ -1,58 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [],
- function () {
-
- /**
- * Supports TelemetrySubscription. Provides a simple data structure
- * (with a pool-like interface) that aggregates key-value pairs into
- * one large object, overwriting new values as necessary. Stands
- * in contrast to the TelemetryQueue, which will avoid overwriting
- * values.
- * @memberof platform/telemetry
- * @constructor
- * @implements {platform/telemetry.TelemetryPool}
- */
- function TelemetryTable() {
- }
-
- TelemetryTable.prototype.isEmpty = function () {
- return !this.table;
- };
-
- TelemetryTable.prototype.poll = function () {
- var t = this.table;
- this.table = undefined;
-
- return t;
- };
-
- TelemetryTable.prototype.put = function (key, value) {
- this.table = this.table || {};
- this.table[key] = value;
- };
-
- return TelemetryTable;
- }
-);
diff --git a/platform/telemetry/test/TelemetryAggregatorSpec.js b/platform/telemetry/test/TelemetryAggregatorSpec.js
deleted file mode 100644
index 9c3115c53..000000000
--- a/platform/telemetry/test/TelemetryAggregatorSpec.js
+++ /dev/null
@@ -1,125 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../src/TelemetryAggregator"],
- function (TelemetryAggregator) {
-
- describe("The telemetry aggregator", function () {
- var mockQ,
- mockProviders,
- mockUnsubscribes,
- aggregator;
-
- function mockPromise(value) {
- return {
- then: function (callback) {
- return mockPromise(callback(value));
- }
- };
- }
-
- function makeMockProvider(key, index) {
- var provider = jasmine.createSpyObj(
- "provider" + index,
- ["requestTelemetry", "subscribe"]
- ),
- unsubscribe = jasmine.createSpy("unsubscribe" + index);
- provider.requestTelemetry.and.returnValue({ someKey: key });
- provider.subscribe.and.returnValue(unsubscribe);
-
- // Store to verify interactions later
- mockUnsubscribes[index] = unsubscribe;
-
- return provider;
- }
-
- beforeEach(function () {
- mockQ = jasmine.createSpyObj("$q", ["all"]);
- mockQ.all.and.returnValue(mockPromise([]));
-
- mockUnsubscribes = [];
- mockProviders = ["a", "b", "c"].map(makeMockProvider);
-
- aggregator = new TelemetryAggregator(mockQ, mockProviders);
- });
-
- it("passes requests to aggregated providers", function () {
- var requests = [
- { someKey: "some value" },
- { someKey: "some other value" }
- ];
-
- aggregator.requestTelemetry(requests);
-
- mockProviders.forEach(function (mockProvider) {
- expect(mockProvider.requestTelemetry)
- .toHaveBeenCalledWith(requests);
- });
- });
-
- it("merges results from all providers", function () {
- var capture = jasmine.createSpy("capture");
-
- mockQ.all.and.returnValue(mockPromise([
- { someKey: "some value" },
- { someOtherKey: "some other value" }
- ]));
-
- aggregator.requestTelemetry().then(capture);
-
- // Verify that aggregator results were run through
- // $q.all
- expect(mockQ.all).toHaveBeenCalledWith([
- { someKey: 'a' },
- { someKey: 'b' },
- { someKey: 'c' }
- ]);
-
- expect(capture).toHaveBeenCalledWith({
- someKey: "some value",
- someOtherKey: "some other value"
- });
- });
-
- it("broadcasts subscriptions from all providers", function () {
- var mockCallback = jasmine.createSpy("callback"),
- subscription = aggregator.subscribe(mockCallback);
-
- // Make sure all providers got subscribed to
- mockProviders.forEach(function (mockProvider) {
- expect(mockProvider.subscribe).toHaveBeenCalled();
- });
-
- // Verify that unsubscription gets broadcast too
- mockUnsubscribes.forEach(function (mockUnsubscribe) {
- expect(mockUnsubscribe).not.toHaveBeenCalled();
- });
- subscription(); // unsubscribe
- mockUnsubscribes.forEach(function (mockUnsubscribe) {
- expect(mockUnsubscribe).toHaveBeenCalled();
- });
- });
-
- });
- }
-);
diff --git a/platform/telemetry/test/TelemetryCapabilitySpec.js b/platform/telemetry/test/TelemetryCapabilitySpec.js
deleted file mode 100644
index b34a2dac3..000000000
--- a/platform/telemetry/test/TelemetryCapabilitySpec.js
+++ /dev/null
@@ -1,367 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../src/TelemetryCapability"],
- function (TelemetryCapability) {
-
- describe("The telemetry capability", function () {
- var mockInjector,
- mockQ,
- mockLog,
- mockDomainObject,
- mockTelemetryService,
- mockReject,
- mockUnsubscribe,
- telemetry,
- mockTelemetryAPI,
- mockMetadata,
- mockAPI;
-
- function mockPromise(value) {
- return {
- then: function (callback) {
- return mockPromise(callback(value));
- },
- catch: (rejected) => {
- return Promise.reject(rejected);
- }
- };
- }
-
- function noop() {
- }
-
- beforeEach(function () {
- mockInjector = jasmine.createSpyObj("$injector", ["get"]);
- mockQ = jasmine.createSpyObj("$q", ["when", "reject"]);
- mockLog = jasmine.createSpyObj("$log", ["warn", "info", "debug"]);
- mockDomainObject = jasmine.createSpyObj(
- "domainObject",
- ["getId", "getCapability", "getModel"]
- );
- mockTelemetryService = jasmine.createSpyObj(
- "telemetryService",
- ["requestTelemetry", "subscribe"]
- );
- mockReject = jasmine.createSpyObj("reject", ["then"]);
- mockUnsubscribe = jasmine.createSpy("unsubscribe");
-
- mockInjector.get.and.returnValue(mockTelemetryService);
-
- mockQ.when.and.callFake(mockPromise);
- mockQ.reject.and.returnValue(mockReject);
-
- mockDomainObject.getId.and.returnValue("testId");
- mockDomainObject.getModel.and.returnValue({
- telemetry: {
- source: "testSource",
- key: "testKey"
- }
- });
-
- mockTelemetryService.requestTelemetry
- .and.returnValue(mockPromise({}));
- mockTelemetryService.subscribe
- .and.returnValue(mockUnsubscribe);
-
- // Bubble up...
- mockReject.then.and.returnValue(mockReject);
-
- mockTelemetryAPI = jasmine.createSpyObj("telemetryAPI", [
- "getMetadata",
- "subscribe",
- "request",
- "findRequestProvider",
- "findSubscriptionProvider"
- ]);
-
- mockMetadata = jasmine.createSpyObj('telemetryMetadata', [
- 'valuesForHints',
- 'values'
- ]);
-
- mockMetadata.valuesForHints.and.callFake(function (hints) {
- var hint = hints[0];
- var metadatum = {
- key: 'default' + hint
- };
- metadatum[hint] = "foo";
-
- return [metadatum];
- });
-
- mockTelemetryAPI.getMetadata.and.returnValue(mockMetadata);
-
- mockAPI = {
- telemetry: mockTelemetryAPI,
- time: {
- bounds: function () {
- return {
- start: 0,
- end: 1
- };
- },
- timeSystem: function () {
- return {
- key: 'mockTimeSystem'
- };
- }
- }
- };
-
- telemetry = new TelemetryCapability(
- mockAPI,
- mockInjector,
- mockQ,
- mockLog,
- mockDomainObject
- );
- });
-
- it("applies only to objects with telemetry sources", function () {
- expect(TelemetryCapability.appliesTo({
- telemetry: { source: "testSource" }
- })).toBeTruthy();
- expect(TelemetryCapability.appliesTo({
- xtelemetry: { source: "testSource" }
- })).toBeFalsy();
- expect(TelemetryCapability.appliesTo({})).toBeFalsy();
- expect(TelemetryCapability.appliesTo()).toBeFalsy();
- });
-
- it("gets a telemetry service from the injector", function () {
- telemetry.requestData();
- expect(mockInjector.get)
- .toHaveBeenCalledWith("telemetryService");
- });
-
- it("applies request arguments", function () {
- telemetry.requestData({ start: 42 });
- expect(mockTelemetryService.requestTelemetry)
- .toHaveBeenCalledWith([{
- id: "testId", // from domain object
- source: "testSource", // from model
- key: "testKey", // from model
- start: 42, // from argument
- domain: 'mockTimeSystem',
- domains: [{
- domain: "foo",
- key: 'defaultdomain'
- }],
- ranges: [{
- range: "foo",
- key: 'defaultrange'
- }]
- }]);
- });
-
- it("provides an empty series when telemetry is missing", function () {
- var series;
- mockTelemetryService.requestTelemetry.and.returnValue(mockPromise({}));
- telemetry.requestData({}).then(function (s) {
- series = s;
- });
- expect(series.getPointCount()).toEqual(0);
- });
-
- it("provides telemetry metadata", function () {
- expect(telemetry.getMetadata()).toEqual({
- id: "testId", // from domain object
- source: "testSource",
- key: "testKey",
- start: 0,
- end: 1,
- domain: 'mockTimeSystem',
- domains: [{
- domain: "foo",
- key: 'defaultdomain'
- }],
- ranges: [{
- range: "foo",
- key: 'defaultrange'
- }]
- });
- });
-
- it("uses domain object as a key if needed", function () {
- // Don't include key in telemetry
- mockDomainObject.getModel.and.returnValue({
- telemetry: { source: "testSource" }
- });
-
- // Should have used the domain object's ID
- expect(telemetry.getMetadata()).toEqual({
- id: "testId", // from domain object
- source: "testSource", // from model
- key: "testId", // from domain object
- start: 0,
- end: 1,
- domain: 'mockTimeSystem',
- domains: [{
- domain: "foo",
- key: 'defaultdomain'
- }],
- ranges: [{
- range: "foo",
- key: 'defaultrange'
- }]
- });
- });
-
- it("if a new style telemetry source is available, use it", function () {
- var mockProvider = {};
- mockTelemetryAPI.findSubscriptionProvider.and.returnValue(mockProvider);
- telemetry.subscribe(noop, {});
- expect(mockTelemetryService.subscribe).not.toHaveBeenCalled();
- expect(mockTelemetryAPI.subscribe).toHaveBeenCalled();
- });
-
- it("if a new style telemetry source is not available, revert to old API", function () {
- mockTelemetryAPI.findSubscriptionProvider.and.returnValue(undefined);
- telemetry.subscribe(noop, {});
- expect(mockTelemetryAPI.subscribe).not.toHaveBeenCalled();
- expect(mockTelemetryService.subscribe).toHaveBeenCalled();
- });
-
- it("Wraps telemetry returned from the new API as a telemetry series", function () {
- var returnedTelemetry;
- var mockTelemetry = [{
- prop1: "val1",
- prop2: "val2",
- prop3: "val3"
- },
- {
- prop1: "val4",
- prop2: "val5",
- prop3: "val6"
- }];
- var mockProvider = {};
-
- mockMetadata.values.and.returnValue([
- {
- key: 'defaultrange',
- source: 'prop1'
- },
- {
- key: 'defaultdomain',
- source: 'prop2'
- },
- {
- key: 'prop3',
- source: 'prop3'
- }
- ]);
-
- mockTelemetryAPI.findRequestProvider.and.returnValue(mockProvider);
- mockTelemetryAPI.request.and.returnValue(Promise.resolve(mockTelemetry));
-
- return telemetry.requestData({}).then(function (data) {
- returnedTelemetry = data;
-
- expect(returnedTelemetry.getPointCount).toBeDefined();
- expect(returnedTelemetry.getDomainValue).toBeDefined();
- expect(returnedTelemetry.getRangeValue).toBeDefined();
- expect(returnedTelemetry.getPointCount()).toBe(2);
- // Default domain + remap should work.
- expect(returnedTelemetry.getDomainValue(0)).toBe('val2');
- expect(returnedTelemetry.getDomainValue(1)).toBe('val5');
- // explicit domain should work
- expect(returnedTelemetry.getDomainValue(0, 'prop3')).toBe('val3');
- expect(returnedTelemetry.getDomainValue(1, 'prop3')).toBe('val6');
- // default range + remap should work
- expect(returnedTelemetry.getRangeValue(0)).toBe('val1');
- expect(returnedTelemetry.getRangeValue(1)).toBe('val4');
- // explicit range should work
- expect(returnedTelemetry.getRangeValue(0, 'prop3')).toBe('val3');
- expect(returnedTelemetry.getRangeValue(1, 'prop3')).toBe('val6');
- });
-
- });
-
- it("allows subscriptions to updates", function () {
- var mockCallback = jasmine.createSpy("callback"),
- subscription = telemetry.subscribe(mockCallback);
-
- // Verify subscription to the appropriate object
- expect(mockTelemetryService.subscribe).toHaveBeenCalledWith(
- jasmine.any(Function),
- [{
- id: "testId", // from domain object
- source: "testSource",
- key: "testKey",
- start: 0,
- end: 1,
- domain: 'mockTimeSystem',
- domains: [{
- domain: "foo",
- key: "defaultdomain"
- }],
- ranges: [{
- range: "foo",
- key: "defaultrange"
- }]
- }]
- );
-
- // Check that the callback gets invoked
- expect(mockCallback).not.toHaveBeenCalled();
- mockTelemetryService.subscribe.calls.mostRecent().args[0]({
- testSource: { testKey: { someKey: "some value" } }
- });
- expect(mockCallback).toHaveBeenCalledWith(
- { someKey: "some value" }
- );
-
- // Finally, unsubscribe
- expect(mockUnsubscribe).not.toHaveBeenCalled();
- subscription(); // should be an unsubscribe function
- expect(mockUnsubscribe).toHaveBeenCalled();
- });
-
- it("applies time conductor bounds if request bounds not defined", function () {
- var fullRequest = telemetry.buildRequest({});
- var mockBounds = mockAPI.time.bounds();
-
- expect(fullRequest.start).toBe(mockBounds.start);
- expect(fullRequest.end).toBe(mockBounds.end);
-
- fullRequest = telemetry.buildRequest({
- start: 10,
- end: 20
- });
-
- expect(fullRequest.start).toBe(10);
- expect(fullRequest.end).toBe(20);
- });
-
- it("applies domain from time system if none defined", function () {
- var fullRequest = telemetry.buildRequest({});
- var mockTimeSystem = mockAPI.time.timeSystem();
- expect(fullRequest.domain).toBe(mockTimeSystem.key);
-
- fullRequest = telemetry.buildRequest({domain: 'someOtherDomain'});
- expect(fullRequest.domain).toBe('someOtherDomain');
- });
- });
- }
-);
diff --git a/platform/telemetry/test/TelemetryControllerSpec.js b/platform/telemetry/test/TelemetryControllerSpec.js
deleted file mode 100644
index 3be305e75..000000000
--- a/platform/telemetry/test/TelemetryControllerSpec.js
+++ /dev/null
@@ -1,245 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../src/TelemetryController"],
- function (TelemetryController) {
-
- describe("The telemetry controller", function () {
- var mockScope,
- mockQ,
- mockTimeout,
- mockLog,
- mockDomainObject,
- mockTelemetry,
- mockUnsubscribe,
- controller;
-
- function mockPromise(value) {
- return (value && value.then) ? value : {
- then: function (callback) {
- return mockPromise(callback(value));
- }
- };
- }
-
- beforeEach(function () {
- mockScope = jasmine.createSpyObj(
- "$scope",
- ["$on", "$broadcast", "$watch"]
- );
- mockQ = jasmine.createSpyObj("$q", ["all", "when"]);
- mockTimeout = jasmine.createSpy("$timeout");
- mockLog = jasmine.createSpyObj("$log", ["warn", "info", "debug"]);
-
- mockDomainObject = jasmine.createSpyObj(
- "domainObject",
- [
- "getId",
- "getCapability",
- "getModel",
- "hasCapability",
- "useCapability"
- ]
- );
-
- mockTelemetry = jasmine.createSpyObj(
- "telemetry",
- ["requestData", "subscribe", "getMetadata"]
- );
- mockUnsubscribe = jasmine.createSpy("unsubscribe");
-
- mockQ.when.and.callFake(mockPromise);
- mockQ.all.and.returnValue(mockPromise([mockDomainObject]));
-
- mockDomainObject.getId.and.returnValue("testId");
- mockDomainObject.getModel.and.returnValue({ name: "TEST" });
- mockDomainObject.useCapability.and.returnValue([]);
- mockDomainObject.hasCapability.and.returnValue(true);
- mockDomainObject.getCapability.and.returnValue(mockTelemetry);
-
- mockTelemetry.getMetadata.and.returnValue({
- source: "testSource",
- key: "testKey"
- });
- mockTelemetry.requestData.and.returnValue(mockPromise({
- telemetryKey: "some value"
- }));
- mockTelemetry.subscribe.and.returnValue(mockUnsubscribe);
-
- controller = new TelemetryController(
- mockScope,
- mockQ,
- mockTimeout,
- mockLog
- );
- });
-
- it("watches the domain object in scope", function () {
- expect(mockScope.$watch).toHaveBeenCalledWith(
- "domainObject",
- jasmine.any(Function)
- );
- });
-
- it("starts a refresh interval", function () {
- expect(mockTimeout).toHaveBeenCalledWith(
- jasmine.any(Function),
- jasmine.any(Number)
- );
- });
-
- it("changes refresh interval on request", function () {
- controller.setRefreshInterval(42);
-
- // Tick the clock; should issue a new request, with
- // the new interval
- mockTimeout.calls.mostRecent().args[0]();
-
- expect(mockTimeout).toHaveBeenCalledWith(
- jasmine.any(Function),
- 42
- );
- });
-
- it("requests data from domain objects", function () {
- // Push into the scope...
- mockScope.$watch.calls.mostRecent().args[1](mockDomainObject);
-
- expect(mockTelemetry.requestData).toHaveBeenCalled();
- });
-
- it("logs a warning if no telemetry capability exists", function () {
- mockDomainObject.getCapability.and.returnValue(undefined);
-
- // Push into the scope...
- mockScope.$watch.calls.mostRecent().args[1](mockDomainObject);
-
- expect(mockLog.warn).toHaveBeenCalled();
- });
-
- it("provides telemetry metadata", function () {
- // Push into the scope...
- mockScope.$watch.calls.mostRecent().args[1](mockDomainObject);
-
- expect(controller.getMetadata()).toEqual([
- {
- source: "testSource",
- key: "testKey"
- }
- ]);
- });
-
- it("provides telemetry-possessing domain objects", function () {
- // Push into the scope...
- mockScope.$watch.calls.mostRecent().args[1](mockDomainObject);
-
- expect(controller.getTelemetryObjects())
- .toEqual([mockDomainObject]);
- });
-
- it("provides telemetry data", function () {
- // Push into the scope...
- mockScope.$watch.calls.mostRecent().args[1](mockDomainObject);
-
- expect(controller.getResponse())
- .toEqual([{telemetryKey: "some value"}]);
- });
-
- it("provides telemetry data per-id", function () {
- // Push into the scope...
- mockScope.$watch.calls.mostRecent().args[1](mockDomainObject);
-
- expect(controller.getResponse("testId"))
- .toEqual({telemetryKey: "some value"});
- });
-
- it("provides a check for pending requests", function () {
- expect(controller.isRequestPending()).toBeFalsy();
- });
-
- it("allows a request to be specified", function () {
- // Push into the scope...
- mockScope.$watch.calls.mostRecent().args[1](mockDomainObject);
-
- controller.requestData({ someKey: "some request" });
-
- expect(mockTelemetry.requestData).toHaveBeenCalledWith({
- someKey: "some request"
- });
- });
-
- it("allows an object to be removed from scope", function () {
- // Push into the scope...
- mockScope.$watch.calls.mostRecent().args[1](undefined);
-
- expect(controller.getTelemetryObjects())
- .toEqual([]);
- });
-
- it("broadcasts when telemetry is available", function () {
- // Push into the scope...
- mockScope.$watch.calls.mostRecent().args[1](mockDomainObject);
- controller.requestData({ someKey: "some request" });
-
- // Verify precondition
- expect(mockScope.$broadcast).not.toHaveBeenCalled();
-
- // Call the broadcast timeout
- mockTimeout.calls.mostRecent().args[0]();
-
- // Should have broadcast a telemetryUpdate
- expect(mockScope.$broadcast)
- .toHaveBeenCalledWith("telemetryUpdate");
- });
-
- it("subscribes for streaming telemetry updates", function () {
- // Push into scope to create subscriptions
- mockScope.$watch.calls.mostRecent().args[1](mockDomainObject);
- // Should have subscribed
- expect(mockTelemetry.subscribe)
- .toHaveBeenCalledWith(jasmine.any(Function));
- // Invoke the subscriber function (for coverage)
- mockTelemetry.subscribe.calls.mostRecent().args[0]({});
- });
-
- it("listens for scope destruction to clean up", function () {
- expect(mockScope.$on).toHaveBeenCalledWith(
- "$destroy",
- jasmine.any(Function)
- );
- mockScope.$on.calls.mostRecent().args[1]();
- });
-
- it("unsubscribes when destroyed", function () {
- // Push into scope to create subscriptions
- mockScope.$watch.calls.mostRecent().args[1](mockDomainObject);
-
- // Invoke "$destroy" listener
- mockScope.$on.calls.mostRecent().args[1]();
-
- // Should have unsubscribed
- expect(mockUnsubscribe).toHaveBeenCalled();
- });
- });
- }
-);
diff --git a/platform/telemetry/test/TelemetryFormatterSpec.js b/platform/telemetry/test/TelemetryFormatterSpec.js
deleted file mode 100644
index c8bb9391c..000000000
--- a/platform/telemetry/test/TelemetryFormatterSpec.js
+++ /dev/null
@@ -1,67 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../src/TelemetryFormatter"],
- function (TelemetryFormatter) {
-
- describe("The telemetry formatter", function () {
- var mockFormatService,
- mockFormat,
- formatter;
-
- beforeEach(function () {
- mockFormatService =
- jasmine.createSpyObj("formatService", ["getFormat"]);
- mockFormat = jasmine.createSpyObj("format", [
- "validate",
- "parse",
- "format"
- ]);
- mockFormatService.getFormat.and.returnValue(mockFormat);
- formatter = new TelemetryFormatter(mockFormatService);
- });
-
- it("formats domains using the formatService", function () {
- var testValue = 12321, testResult = "some result";
- mockFormat.format.and.returnValue(testResult);
-
- expect(formatter.formatDomainValue(testValue))
- .toEqual(testResult);
- expect(mockFormat.format).toHaveBeenCalledWith(testValue);
- });
-
- it("passes format keys to the formatService", function () {
- formatter.formatDomainValue(12321, "someKey");
- expect(mockFormatService.getFormat)
- .toHaveBeenCalledWith("someKey");
- });
-
- it("formats ranges as values", function () {
- var value = 3.14159265352979323846264338, // not pi
- formatted = formatter.formatRangeValue(value);
- // Make sure we don't lose information by formatting
- expect(parseFloat(formatted)).toEqual(value);
- });
- });
- }
-);
diff --git a/platform/telemetry/test/TelemetryHandleSpec.js b/platform/telemetry/test/TelemetryHandleSpec.js
deleted file mode 100644
index 9f94f1487..000000000
--- a/platform/telemetry/test/TelemetryHandleSpec.js
+++ /dev/null
@@ -1,141 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../src/TelemetryHandle"],
- function (TelemetryHandle) {
-
- describe("A telemetry handle", function () {
- var mockQ,
- mockSubscription,
- mockDomainObject,
- mockTelemetry,
- mockSeries,
- mockCallback,
- handle;
-
- function asPromise(v) {
- return (v || {}).then ? v : {
- then: function (callback) {
- return asPromise(callback(v));
- }
- };
- }
-
- beforeEach(function () {
- mockQ = jasmine.createSpyObj('$q', ['when', 'all']);
- mockSubscription = jasmine.createSpyObj(
- 'subscription',
- [
- 'makeDatum',
- 'getDatum',
- 'unsubscribe',
- 'getTelemetryObjects',
- 'promiseTelemetryObjects'
- ]
- );
- mockDomainObject = jasmine.createSpyObj(
- 'domainObject',
- ['getId', 'getCapability']
- );
- mockTelemetry = jasmine.createSpyObj(
- 'telemetry',
- ['requestData']
- );
- mockSeries = jasmine.createSpyObj(
- 'series',
- ['getPointCount', 'getDomainValue', 'getRangeValue']
- );
- mockCallback = jasmine.createSpy('callback');
-
- // Simulate $q.all, at least for asPromise-provided promises
- mockQ.all.and.callFake(function (values) {
- return values.map(function (v) {
- var r;
- asPromise(v).then(function (value) {
- r = value;
- });
-
- return r;
- });
- });
- mockQ.when.and.callFake(asPromise);
- mockSubscription.getTelemetryObjects
- .and.returnValue([mockDomainObject]);
- mockSubscription.promiseTelemetryObjects
- .and.returnValue(asPromise([mockDomainObject]));
- mockDomainObject.getId.and.returnValue('testId');
- mockDomainObject.getCapability.and.returnValue(mockTelemetry);
- mockTelemetry.requestData.and.returnValue(asPromise(mockSeries));
-
- handle = new TelemetryHandle(mockQ, mockSubscription);
- });
-
- it("exposes subscription API", function () {
- // Should still expose methods from the provided subscription
- // (though these may have been wrapped)
- expect(mockSubscription.getTelemetryObjects)
- .not.toHaveBeenCalled();
- handle.getTelemetryObjects();
- expect(mockSubscription.getTelemetryObjects)
- .toHaveBeenCalled();
-
- expect(mockSubscription.unsubscribe)
- .not.toHaveBeenCalled();
- handle.unsubscribe();
- expect(mockSubscription.unsubscribe)
- .toHaveBeenCalled();
- });
-
- it("provides an interface for historical requests", function () {
- handle.request({}, mockCallback);
- expect(mockCallback).toHaveBeenCalledWith(
- mockDomainObject,
- mockSeries
- );
- });
-
- it("provides the latest series for domain objects", function () {
- handle.request({});
- expect(handle.getSeries(mockDomainObject))
- .toEqual(mockSeries);
- });
-
- it("provides access to the datum objects by index", function () {
- var testDatum = {
- a: 1,
- b: 2
- }, testIndex = 42;
- mockSubscription.makeDatum.and.returnValue(testDatum);
- handle.request({});
- expect(handle.getDatum(mockDomainObject, testIndex))
- .toEqual(testDatum);
- expect(mockSubscription.makeDatum)
- .toHaveBeenCalledWith(
- mockDomainObject,
- mockSeries,
- testIndex
- );
- });
- });
- }
-);
diff --git a/platform/telemetry/test/TelemetryHandlerSpec.js b/platform/telemetry/test/TelemetryHandlerSpec.js
deleted file mode 100644
index ac231d8af..000000000
--- a/platform/telemetry/test/TelemetryHandlerSpec.js
+++ /dev/null
@@ -1,83 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../src/TelemetryHandler"],
- function (TelemetryHandler) {
-
- describe("The telemetry handler", function () {
- // TelemetryHandler just provides a factory
- // for TelemetryHandle, so most real testing
- // should happen there.
- var mockQ,
- mockSubscriber,
- mockDomainObject,
- mockCallback,
- mockSubscription,
- handler;
-
- beforeEach(function () {
- mockQ = jasmine.createSpyObj("$q", ["when"]);
- mockSubscriber = jasmine.createSpyObj(
- 'telemetrySubscriber',
- ['subscribe']
- );
- mockDomainObject = jasmine.createSpyObj(
- 'domainObject',
- ['getId', 'getCapability']
- );
- mockCallback = jasmine.createSpy('callback');
- mockSubscription = jasmine.createSpyObj(
- 'subscription',
- [
- 'unsubscribe',
- 'getTelemetryObjects',
- 'getRangeValue',
- 'getDomainValue'
- ]
- );
-
- mockSubscriber.subscribe.and.returnValue(mockSubscription);
-
- handler = new TelemetryHandler(mockQ, mockSubscriber);
- });
-
- it("acts as a factory for subscription objects", function () {
- var handle = handler.handle(
- mockDomainObject,
- mockCallback
- );
- // Just verify that this looks like a TelemetrySubscription
- [
- "unsubscribe",
- "getTelemetryObjects",
- "getRangeValue",
- "getDomainValue",
- "request"
- ].forEach(function (method) {
- expect(handle[method]).toEqual(jasmine.any(Function));
- });
- });
-
- });
- }
-);
diff --git a/platform/telemetry/test/TelemetryQueueSpec.js b/platform/telemetry/test/TelemetryQueueSpec.js
deleted file mode 100644
index a79872822..000000000
--- a/platform/telemetry/test/TelemetryQueueSpec.js
+++ /dev/null
@@ -1,79 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../src/TelemetryQueue"],
- function (TelemetryQueue) {
-
- describe("The telemetry queue", function () {
- var queue;
-
- beforeEach(function () {
- // put, isEmpty, dequeue
- queue = new TelemetryQueue();
- });
-
- it("stores elements by key", function () {
- queue.put("a", { someKey: "some value" });
- expect(queue.poll())
- .toEqual({ a: { someKey: "some value" }});
- });
-
- it("merges non-overlapping keys", function () {
- queue.put("a", { someKey: "some value" });
- queue.put("b", 42);
- expect(queue.poll())
- .toEqual({
- a: { someKey: "some value" },
- b: 42
- });
- });
-
- it("adds new objects for repeated keys", function () {
- queue.put("a", { someKey: "some value" });
- queue.put("a", { someKey: "some other value" });
- queue.put("b", 42);
- expect(queue.poll())
- .toEqual({
- a: { someKey: "some value" },
- b: 42
- });
- expect(queue.poll())
- .toEqual({ a: { someKey: "some other value" } });
- });
-
- it("reports emptiness", function () {
- expect(queue.isEmpty()).toBeTruthy();
- queue.put("a", { someKey: "some value" });
- queue.put("a", { someKey: "some other value" });
- queue.put("b", 42);
- expect(queue.isEmpty()).toBeFalsy();
- queue.poll();
- expect(queue.isEmpty()).toBeFalsy();
- queue.poll();
- expect(queue.isEmpty()).toBeTruthy();
- });
-
- });
-
- }
-);
diff --git a/platform/telemetry/test/TelemetrySubscriberSpec.js b/platform/telemetry/test/TelemetrySubscriberSpec.js
deleted file mode 100644
index 3959c8ddd..000000000
--- a/platform/telemetry/test/TelemetrySubscriberSpec.js
+++ /dev/null
@@ -1,73 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../src/TelemetrySubscriber"],
- function (TelemetrySubscriber) {
-
- describe("The telemetry subscriber", function () {
- // TelemetrySubscriber just provides a factory
- // for TelemetrySubscription, so most real testing
- // should happen there.
- var mockQ,
- mockTimeout,
- mockDomainObject,
- mockCallback,
- mockPromise,
- subscriber;
-
- beforeEach(function () {
- mockQ = jasmine.createSpyObj("$q", ["when"]);
- mockTimeout = jasmine.createSpy("$timeout");
- mockDomainObject = jasmine.createSpyObj(
- "domainObject",
- ["getCapability", "useCapability", "hasCapability"]
- );
- mockCallback = jasmine.createSpy("callback");
- mockPromise = jasmine.createSpyObj("promise", ["then"]);
-
- mockQ.when.and.returnValue(mockPromise);
- mockPromise.then.and.returnValue(mockPromise);
-
- subscriber = new TelemetrySubscriber(mockQ, mockTimeout);
- });
-
- it("acts as a factory for subscription objects", function () {
- var subscription = subscriber.subscribe(
- mockDomainObject,
- mockCallback
- );
- // Just verify that this looks like a TelemetrySubscription
- [
- "unsubscribe",
- "getTelemetryObjects",
- "getRangeValue",
- "getDomainValue"
- ].forEach(function (method) {
- expect(subscription[method])
- .toEqual(jasmine.any(Function));
- });
- });
-
- });
- }
-);
diff --git a/platform/telemetry/test/TelemetrySubscriptionSpec.js b/platform/telemetry/test/TelemetrySubscriptionSpec.js
deleted file mode 100644
index 4c96925a8..000000000
--- a/platform/telemetry/test/TelemetrySubscriptionSpec.js
+++ /dev/null
@@ -1,271 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../src/TelemetrySubscription"],
- function (TelemetrySubscription) {
-
- describe("A telemetry subscription", function () {
- var mockQ,
- mockTimeout,
- mockDomainObject,
- mockCallback,
- mockTelemetry,
- mockMutation,
- mockUnsubscribe,
- mockUnlisten,
- mockSeries,
- testMetadata,
- subscription;
-
- function mockPromise(value) {
- return (value && value.then) ? value : {
- then: function (callback) {
- return mockPromise(callback(value));
- }
- };
- }
-
- beforeEach(function () {
- testMetadata = { someKey: "some value" };
-
- mockQ = jasmine.createSpyObj("$q", ["when", "all"]);
- mockTimeout = jasmine.createSpy("$timeout");
- mockDomainObject = jasmine.createSpyObj(
- "domainObject",
- ["getCapability", "useCapability", "hasCapability", "getId"]
- );
- mockCallback = jasmine.createSpy("callback");
- mockTelemetry = jasmine.createSpyObj(
- "telemetry",
- ["subscribe", "getMetadata"]
- );
- mockMutation = jasmine.createSpyObj(
- "mutation",
- ["mutate", "listen"]
- );
- mockUnsubscribe = jasmine.createSpy("unsubscribe");
- mockUnlisten = jasmine.createSpy("unlisten");
- mockSeries = jasmine.createSpyObj(
- "series",
- ["getPointCount", "getDomainValue", "getRangeValue"]
- );
-
- mockQ.when.and.callFake(mockPromise);
-
- mockDomainObject.hasCapability.and.returnValue(true);
- mockDomainObject.getCapability.and.callFake(function (c) {
- return {
- telemetry: mockTelemetry,
- mutation: mockMutation
- }[c];
- });
- mockDomainObject.getId.and.returnValue('test-id');
-
- mockTelemetry.subscribe.and.returnValue(mockUnsubscribe);
- mockTelemetry.getMetadata.and.returnValue(testMetadata);
-
- mockMutation.listen.and.returnValue(mockUnlisten);
-
- mockSeries.getPointCount.and.returnValue(42);
- mockSeries.getDomainValue.and.returnValue(123456);
- mockSeries.getRangeValue.and.returnValue(789);
-
- subscription = new TelemetrySubscription(
- mockQ,
- mockTimeout,
- mockDomainObject,
- mockCallback
- );
- });
-
- it("subscribes to the provided object", function () {
- expect(mockTelemetry.subscribe).toHaveBeenCalled();
- });
-
- it("unsubscribes on request", function () {
- expect(mockUnsubscribe).not.toHaveBeenCalled();
- subscription.unsubscribe();
- expect(mockUnsubscribe).toHaveBeenCalled();
- });
-
- it("fires callbacks when subscriptions update", function () {
- // Callback fires when telemetry objects become available,
- // so track initial call count instead of verifying that
- // it hasn't been called at all.
- var initialCalls = mockCallback.calls.count();
- mockTelemetry.subscribe.calls.mostRecent().args[0](mockSeries);
- // This gets fired via a timeout, so trigger that
- expect(mockTimeout).toHaveBeenCalledWith(
- jasmine.any(Function),
- 0
- );
- mockTimeout.calls.mostRecent().args[0]();
- // Should have triggered the callback to alert that
- // new data was available
- expect(mockCallback.calls.count()).toEqual(initialCalls + 1);
- });
-
- it("fires subscription callbacks once per cycle", function () {
- var i;
-
- // Verify precondition - one call for telemetryObjects
- expect(mockCallback.calls.count()).toEqual(1);
-
- for (i = 0; i < 100; i += 1) {
- mockTelemetry.subscribe.calls.mostRecent().args[0](mockSeries);
- }
-
- // This gets fired via a timeout, so trigger any of those
- mockTimeout.calls.all().forEach(function (call) {
- call.args[0]();
- });
- // Should have only triggered the
- expect(mockCallback.calls.count()).toEqual(2);
- });
-
- it("reports its latest observed data values", function () {
- mockTelemetry.subscribe.calls.mostRecent().args[0](mockSeries);
- // This gets fired via a timeout, so trigger that
- mockTimeout.calls.mostRecent().args[0]();
- // Verify that the last sample was looked at
- expect(mockSeries.getDomainValue).toHaveBeenCalledWith(41);
- expect(mockSeries.getRangeValue).toHaveBeenCalledWith(41);
- // Domain and range values should now be available
- expect(subscription.getDomainValue(mockDomainObject))
- .toEqual(123456);
- expect(subscription.getRangeValue(mockDomainObject))
- .toEqual(789);
- });
-
- it("provides no objects if no domain object is provided", function () {
- // omit last arguments
- subscription = new TelemetrySubscription(mockQ, mockTimeout);
-
- // Should have no objects
- expect(subscription.getTelemetryObjects()).toEqual([]);
- });
-
- // This test case corresponds to plot usage of
- // telemetrySubscription, where failure to callback
- // once-per-update results in loss of data, WTD-784
- it("fires one event per update if requested", function () {
- var i, domains = [], ranges = [], lastCall, initialCalls;
-
- // Clear out the subscription from beforeEach
- subscription.unsubscribe();
- // Create a subscription which does not drop events
- subscription = new TelemetrySubscription(
- mockQ,
- mockTimeout,
- mockDomainObject,
- mockCallback,
- true // Don't drop updates!
- );
-
- // Track calls at this point
- initialCalls = mockCallback.calls.count();
-
- // Snapshot getDomainValue, getRangeValue at time of callback
- mockCallback.and.callFake(function () {
- domains.push(subscription.getDomainValue(mockDomainObject));
- ranges.push(subscription.getRangeValue(mockDomainObject));
- });
-
- // Send 100 updates
- for (i = 0; i < 100; i += 1) {
- // Return different values to verify later
- mockSeries.getDomainValue.and.returnValue(i);
- mockSeries.getRangeValue.and.returnValue(i * 2);
- mockTelemetry.subscribe.calls.mostRecent().args[0](mockSeries);
- }
-
- // Fire all timeouts that get scheduled
- while (mockTimeout.calls.mostRecent() !== lastCall) {
- lastCall = mockTimeout.calls.mostRecent();
- lastCall.args[0]();
- }
-
- // Should have only triggered the
- expect(mockCallback.calls.count()).toEqual(100 + initialCalls);
- });
-
- it("provides domain object metadata", function () {
- expect(subscription.getMetadata()[0])
- .toEqual(testMetadata);
- });
-
- it("fires callback when telemetry objects are available", function () {
- expect(mockCallback.calls.count()).toEqual(1);
- });
-
- it("exposes a promise for telemetry objects", function () {
- var mockCallback2 = jasmine.createSpy('callback');
- subscription.promiseTelemetryObjects().then(mockCallback2);
-
- expect(mockCallback2)
- .toHaveBeenCalledWith([mockDomainObject]);
- });
-
- it("reinitializes on mutation", function () {
- expect(mockTelemetry.subscribe.calls.count()).toEqual(1);
- // Notify of a mutation which appears to change composition
- mockMutation.listen.calls.mostRecent().args[0]({
- composition: ['Z']
- });
- // Use subscribe call as an indication of reinitialization
- expect(mockTelemetry.subscribe.calls.count()).toEqual(2);
- });
-
- it("stops listening for mutation on unsubscribe", function () {
- expect(mockUnlisten).not.toHaveBeenCalled();
- subscription.unsubscribe();
- expect(mockUnlisten).toHaveBeenCalled();
- });
-
- it("provides telemetry as datum objects", function () {
- var testDatum = {
- a: 1,
- b: 13,
- c: 42,
- d: -1977
- };
-
- function lookup(index, key) {
- return testDatum[key];
- }
-
- mockSeries.getDomainValue.and.callFake(lookup);
- mockSeries.getRangeValue.and.callFake(lookup);
-
- testMetadata.domains = [{ key: 'a' }, { key: 'b'}];
- testMetadata.ranges = [{ key: 'c' }, { key: 'd'}];
-
- mockTelemetry.subscribe.calls.mostRecent().args[0](mockSeries);
- mockTimeout.calls.mostRecent().args[0]();
-
- expect(subscription.getDatum(mockDomainObject))
- .toEqual(testDatum);
- });
- });
- }
-);
diff --git a/platform/telemetry/test/TelemetryTableSpec.js b/platform/telemetry/test/TelemetryTableSpec.js
deleted file mode 100644
index b01310507..000000000
--- a/platform/telemetry/test/TelemetryTableSpec.js
+++ /dev/null
@@ -1,77 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ["../src/TelemetryTable"],
- function (TelemetryTable) {
-
- describe("The telemetry table", function () {
- var queue;
-
- beforeEach(function () {
- // put, isEmpty, dequeue
- queue = new TelemetryTable();
- });
-
- it("stores elements by key", function () {
- queue.put("a", { someKey: "some value" });
- expect(queue.poll())
- .toEqual({ a: { someKey: "some value" }});
- });
-
- it("merges non-overlapping keys", function () {
- queue.put("a", { someKey: "some value" });
- queue.put("b", 42);
- expect(queue.poll())
- .toEqual({
- a: { someKey: "some value" },
- b: 42
- });
- });
-
- it("overwrites repeated keys", function () {
- queue.put("a", { someKey: "some value" });
- queue.put("a", { someKey: "some other value" });
- queue.put("b", 42);
- expect(queue.poll())
- .toEqual({
- a: { someKey: "some other value" },
- b: 42
- });
- expect(queue.poll())
- .toBeUndefined();
- });
-
- it("reports emptiness", function () {
- expect(queue.isEmpty()).toBeTruthy();
- queue.put("a", { someKey: "some value" });
- queue.put("a", { someKey: "some other value" });
- queue.put("b", 42);
- expect(queue.isEmpty()).toBeFalsy();
- queue.poll();
- expect(queue.isEmpty()).toBeTruthy();
- });
-
- });
-
- }
-);
diff --git a/src/MCT.js b/src/MCT.js
index 0630991db..595afa7b0 100644
--- a/src/MCT.js
+++ b/src/MCT.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -42,6 +42,7 @@ define([
'./plugins/duplicate/plugin',
'./plugins/importFromJSONAction/plugin',
'./plugins/exportAsJSONAction/plugin',
+ './ui/components/components',
'vue'
], function (
EventEmitter,
@@ -65,6 +66,7 @@ define([
DuplicateActionPlugin,
ImportFromJSONAction,
ExportAsJSONAction,
+ components,
Vue
) {
/**
@@ -94,147 +96,170 @@ define([
};
this.destroy = this.destroy.bind(this);
- /**
- * Tracks current selection state of the application.
- * @private
- */
- this.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
- */
- this.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
- */
- this.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
- */
- this.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
- */
- this.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
- */
- this.propertyEditors = new ViewRegistry();
-
- /**
- * Registry for views which should appear in the status indicator area.
- * @type {module:openmct.ViewRegistry}
- * @memberof module:openmct.MCT#
- * @name indicators
- */
- this.indicators = 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
- */
- this.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
- */
- this.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
- */
- this.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
- */
- this.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
- */
- this.indicators = new api.IndicatorAPI(this);
-
- this.notifications = new api.NotificationAPI();
-
- this.editor = new api.EditorAPI.default(this);
-
- this.overlays = new OverlayAPI.default();
-
- this.menus = new api.MenuAPI(this);
-
- this.actions = new api.ActionsAPI(this);
-
- this.status = new api.StatusAPI(this);
-
- this.priority = api.PriorityAPI;
-
- this.router = new ApplicationRouter(this);
- this.forms = new api.FormsAPI.default(this);
-
- this.branding = BrandingAPI.default;
+ [
+ /**
+ * 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.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()],
+
+ ['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
+ });
+ });
// Plugins that are installed by default
-
this.install(this.plugins.Plot());
- this.install(this.plugins.Chart());
this.install(this.plugins.TelemetryTable.default());
this.install(PreviewPlugin.default());
this.install(LicensesPlugin.default());
@@ -260,8 +285,9 @@ define([
this.install(this.plugins.ViewDatumAction());
this.install(this.plugins.ViewLargeAction());
this.install(this.plugins.ObjectInterceptors());
- this.install(this.plugins.NonEditableFolder());
this.install(this.plugins.DeviceClassifier());
+ this.install(this.plugins.UserIndicator());
+ this.install(this.plugins.Gauge());
}
MCT.prototype = Object.create(EventEmitter.prototype);
@@ -370,6 +396,7 @@ define([
};
MCT.prototype.plugins = plugins;
+ MCT.prototype.components = components.default;
return MCT;
});
diff --git a/src/MCTSpec.js b/src/MCTSpec.js
index 89900a796..8cca3e2be 100644
--- a/src/MCTSpec.js
+++ b/src/MCTSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/adapter/actions/LegacyContextMenuAction.js b/src/adapter/actions/LegacyContextMenuAction.js
deleted file mode 100644
index 58b4dcca0..000000000
--- a/src/adapter/actions/LegacyContextMenuAction.js
+++ /dev/null
@@ -1,94 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-import _ from 'lodash';
-const INSIDE_EDIT_PATH_BLACKLIST = ["copy", "follow", "link", "locate", "move", "link"];
-const OUTSIDE_EDIT_PATH_BLACKLIST = ["copy", "follow", "move", "link", "remove", "locate"];
-
-export default class LegacyContextMenuAction {
- constructor(openmct, LegacyAction) {
- this.openmct = openmct;
- this.key = LegacyAction.definition.key;
- this.name = LegacyAction.definition.name;
- this.description = LegacyAction.definition.description;
- this.cssClass = LegacyAction.definition.cssClass;
- this.LegacyAction = LegacyAction;
- this.group = LegacyAction.definition.group;
- this.priority = LegacyAction.definition.priority;
- }
-
- invoke(objectPath) {
- this.openmct.objects.getRoot().then((root) => {
- let pathWithRoot = objectPath.slice();
- pathWithRoot.push(root);
-
- let context = {
- category: 'contextual',
- domainObject: this.openmct.legacyObject(pathWithRoot)
- };
- let legacyAction = new this.LegacyAction(context);
-
- if (!legacyAction.getMetadata) {
- let metadata = Object.create(this.LegacyAction.definition);
- metadata.context = context;
- legacyAction.getMetadata = function () {
- return metadata;
- }.bind(legacyAction);
- }
-
- legacyAction.perform();
- });
- }
-
- appliesTo(objectPath) {
- let legacyObject = this.openmct.legacyObject(objectPath);
- let view;
-
- return (this.LegacyAction.appliesTo === undefined
- || this.LegacyAction.appliesTo({domainObject: legacyObject}, view, this.openmct))
- && !this.isBlacklisted(objectPath);
- }
-
- /**
- * @private
- */
- isBlacklisted(objectPath) {
- let navigatedObject = this.openmct.router.path[0];
- let isEditing = this.openmct.editor.isEditing();
-
- /**
- * Is the object being edited, or a child of the object being edited?
- */
- function isInsideEditPath() {
- return objectPath.some((object) => _.eq(object.identifier, navigatedObject.identifier));
- }
-
- if (isEditing) {
- if (isInsideEditPath()) {
- return INSIDE_EDIT_PATH_BLACKLIST.some(actionKey => this.LegacyAction.key === actionKey);
- } else {
- return OUTSIDE_EDIT_PATH_BLACKLIST.some(actionKey => this.LegacyAction.key === actionKey);
- }
- }
-
- return false;
- }
-}
diff --git a/src/adapter/bundle.js b/src/adapter/bundle.js
deleted file mode 100644
index a01efff8b..000000000
--- a/src/adapter/bundle.js
+++ /dev/null
@@ -1,195 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
- './capabilities/AdapterCapability',
- './directives/MCTView',
- './services/Instantiate',
- './capabilities/APICapabilityDecorator',
- './policies/AdaptedViewPolicy',
- './runs/AlternateCompositionInitializer',
- './runs/LegacyTelemetryProvider',
- './runs/RegisterLegacyTypes',
- './services/LegacyObjectAPIInterceptor',
- './views/installLegacyViews',
- './policies/LegacyCompositionPolicyAdapter',
- './actions/LegacyActionAdapter',
- './services/LegacyPersistenceAdapter'
-], function (
- AdapterCapability,
- MCTView,
- Instantiate,
- APICapabilityDecorator,
- AdaptedViewPolicy,
- AlternateCompositionInitializer,
- LegacyTelemetryProvider,
- RegisterLegacyTypes,
- LegacyObjectAPIInterceptor,
- installLegacyViews,
- legacyCompositionPolicyAdapter,
- LegacyActionAdapter,
- LegacyPersistenceAdapter
-) {
- return {
- name: 'src/adapter',
- definition: {
- "extensions": {
- "directives": [
- {
- key: "mctView",
- implementation: MCTView,
- depends: ["openmct"]
- }
- ],
- capabilities: [
- {
- key: "adapter",
- implementation: AdapterCapability
- }
- ],
- services: [
- {
- key: "instantiate",
- priority: "mandatory",
- implementation: Instantiate,
- depends: [
- "capabilityService",
- "identifierService",
- "cacheService"
- ]
- }
- ],
- components: [
- {
- type: "decorator",
- provides: "capabilityService",
- implementation: APICapabilityDecorator,
- depends: [
- "$injector"
- ]
- },
- {
- provides: "objectService",
- type: "decorator",
- priority: "mandatory",
- implementation: LegacyObjectAPIInterceptor,
- depends: [
- "openmct",
- "roots[]",
- "instantiate",
- "topic"
- ]
- },
- {
- provides: "persistenceService",
- type: "provider",
- priority: "fallback",
- implementation: function legacyPersistenceProvider(openmct) {
- return new LegacyPersistenceAdapter.default(openmct);
- },
- depends: ["openmct"]
- }
- ],
- policies: [
- {
- category: "view",
- implementation: AdaptedViewPolicy,
- depends: ["openmct"]
- }
- ],
- runs: [
- {
- implementation: AlternateCompositionInitializer,
- depends: ["openmct"]
- },
- {
- implementation: LegacyTelemetryProvider,
- depends: [
- "openmct",
- "instantiate"
- ]
- },
- {
- implementation: installLegacyViews,
- depends: [
- "openmct",
- "views[]",
- "instantiate"
- ]
- },
- {
- implementation: RegisterLegacyTypes,
- depends: [
- "types[]",
- "openmct"
- ]
- },
- {
- implementation: legacyCompositionPolicyAdapter.default,
- depends: [
- "openmct"
- ]
- },
- {
- implementation: LegacyActionAdapter.default,
- depends: [
- "openmct",
- "actions[]"
- ]
- }
- ],
- licenses: [
- {
- "name": "almond",
- "version": "0.3.3",
- "description": "Lightweight RequireJS replacement for builds",
- "author": "jQuery Foundation",
- "website": "https://github.com/requirejs/almond",
- "copyright": "Copyright jQuery Foundation and other contributors, https://jquery.org/",
- "license": "license-mit",
- "link": "https://github.com/requirejs/almond/blob/master/LICENSE"
- },
- {
- "name": "lodash",
- "version": "3.10.1",
- "description": "Utility functions",
- "author": "Dojo Foundation",
- "website": "https://lodash.com",
- "copyright": "Copyright 2012-2015 The Dojo Foundation",
- "license": "license-mit",
- "link": "https://raw.githubusercontent.com/lodash/lodash/3.10.1/LICENSE"
- },
- {
- "name": "EventEmitter3",
- "version": "1.2.0",
- "description": "Event-driven programming support",
- "author": "Arnout Kazemier",
- "website": "https://github.com/primus/eventemitter3",
- "copyright": "Copyright (c) 2014 Arnout Kazemier",
- "license": "license-mit",
- "link": "https://github.com/primus/eventemitter3/blob/1.2.0/LICENSE"
- }
- ]
- }
- }
- };
-});
diff --git a/src/adapter/capabilities/APICapabilityDecorator.js b/src/adapter/capabilities/APICapabilityDecorator.js
deleted file mode 100644
index e3598d600..000000000
--- a/src/adapter/capabilities/APICapabilityDecorator.js
+++ /dev/null
@@ -1,67 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
- './synchronizeMutationCapability',
- './AlternateCompositionCapability',
- './patchViewCapability'
-], function (
- synchronizeMutationCapability,
- AlternateCompositionCapability,
- patchViewCapability
-) {
-
- /**
- * Overrides certain capabilities to keep consistency between old API
- * and new API.
- */
- function APICapabilityDecorator($injector, capabilityService) {
- this.$injector = $injector;
- this.capabilityService = capabilityService;
- }
-
- APICapabilityDecorator.prototype.getCapabilities = function (
- model,
- id
- ) {
- const capabilities = this.capabilityService.getCapabilities(model, id);
- if (capabilities.mutation) {
- capabilities.mutation =
- synchronizeMutationCapability(capabilities.mutation);
- }
-
- if (capabilities.view) {
- capabilities.view = patchViewCapability(capabilities.view);
- }
-
- if (AlternateCompositionCapability.appliesTo(model, id)) {
- capabilities.composition = function (domainObject) {
- return new AlternateCompositionCapability(this.$injector, domainObject);
- }.bind(this);
- }
-
- return capabilities;
- };
-
- return APICapabilityDecorator;
-
-});
diff --git a/src/adapter/capabilities/AlternateCompositionCapability.js b/src/adapter/capabilities/AlternateCompositionCapability.js
deleted file mode 100644
index 234154019..000000000
--- a/src/adapter/capabilities/AlternateCompositionCapability.js
+++ /dev/null
@@ -1,107 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT Web 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.
- *****************************************************************************/
-
-/**
- * Module defining AlternateCompositionCapability. Created by vwoeltje on 11/7/14.
- */
-define([
- 'objectUtils',
- '../../../platform/core/src/capabilities/ContextualDomainObject'
-], function (objectUtils, ContextualDomainObject) {
- function AlternateCompositionCapability($injector, domainObject) {
- this.domainObject = domainObject;
- this.getDependencies = function () {
- this.instantiate = $injector.get("instantiate");
- this.getDependencies = undefined;
- this.openmct = $injector.get("openmct");
- }.bind(this);
- }
-
- AlternateCompositionCapability.prototype.add = function (child, index) {
- if (typeof index !== 'undefined') {
- // At first glance I don't see a location in the existing
- // codebase where add is called with an index. Won't support.
- throw new Error(
- 'Composition Capability does not support adding at index'
- );
- }
-
- function addChildToComposition(model) {
- const existingIndex = model.composition.indexOf(child.getId());
- if (existingIndex === -1) {
- model.composition.push(child.getId());
- }
- }
-
- return this.domainObject.useCapability(
- 'mutation',
- addChildToComposition
- )
- .then(this.invoke.bind(this))
- .then(function (children) {
- return children.filter(function (c) {
- return c.getId() === child.getId();
- })[0];
- });
- };
-
- AlternateCompositionCapability.prototype.contextualizeChild = function (
- child
- ) {
- if (this.getDependencies) {
- this.getDependencies();
- }
-
- const keyString = objectUtils.makeKeyString(child.identifier);
- const oldModel = objectUtils.toOldFormat(child);
- const newDO = this.instantiate(oldModel, keyString);
-
- return new ContextualDomainObject(newDO, this.domainObject);
-
- };
-
- AlternateCompositionCapability.prototype.invoke = function () {
- const newFormatDO = objectUtils.toNewFormat(
- this.domainObject.getModel(),
- this.domainObject.getId()
- );
-
- if (this.getDependencies) {
- this.getDependencies();
- }
-
- const collection = this.openmct.composition.get(newFormatDO);
-
- return collection.load()
- .then(function (children) {
- return children.map(this.contextualizeChild, this);
- }.bind(this));
- };
-
- AlternateCompositionCapability.appliesTo = function () {
- // Will get replaced by a runs exception to properly
- // bind to running openmct instance
- return false;
- };
-
- return AlternateCompositionCapability;
-});
diff --git a/src/adapter/capabilities/patchViewCapability.js b/src/adapter/capabilities/patchViewCapability.js
deleted file mode 100644
index 3c81b2841..000000000
--- a/src/adapter/capabilities/patchViewCapability.js
+++ /dev/null
@@ -1,64 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
- 'lodash'
-], function (
- _
-) {
-
- function patchViewCapability(viewConstructor) {
- return function makeCapability(domainObject) {
- const capability = viewConstructor(domainObject);
- const oldInvoke = capability.invoke.bind(capability);
-
- /* eslint-disable you-dont-need-lodash-underscore/map */
- capability.invoke = function () {
- const availableViews = oldInvoke();
- const newDomainObject = capability
- .domainObject
- .useCapability('adapter');
-
- return _(availableViews).map(function (v, i) {
- const vd = {
- view: v,
- priority: i + 100 // arbitrary to allow new views to
- // be defaults by returning priority less than 100.
- };
- if (v.provider && v.provider.priority) {
- vd.priority = v.provider.priority(newDomainObject);
- }
-
- return vd;
- })
- .sortBy('priority')
- .map('view')
- .value();
- };
- /* eslint-enable you-dont-need-lodash-underscore/map */
-
- return capability;
- };
- }
-
- return patchViewCapability;
-});
diff --git a/src/adapter/capabilities/synchronizeMutationCapability.js b/src/adapter/capabilities/synchronizeMutationCapability.js
deleted file mode 100644
index f79c8fe56..000000000
--- a/src/adapter/capabilities/synchronizeMutationCapability.js
+++ /dev/null
@@ -1,50 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
-
-], function (
-
-) {
-
- /**
- * Wraps the mutation capability and synchronizes the mutation
- */
- function synchronizeMutationCapability(mutationConstructor) {
-
- return function makeCapability(domainObject) {
- const capability = mutationConstructor(domainObject);
- const oldListen = capability.listen.bind(capability);
- capability.listen = function (listener) {
- return oldListen(function (newModel) {
- capability.domainObject.model =
- JSON.parse(JSON.stringify(newModel));
- listener(newModel);
- });
- };
-
- return capability;
- };
- }
-
- return synchronizeMutationCapability;
-});
diff --git a/src/adapter/directives/MCTView.js b/src/adapter/directives/MCTView.js
deleted file mode 100644
index edf798297..000000000
--- a/src/adapter/directives/MCTView.js
+++ /dev/null
@@ -1,46 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
-], function (
-) {
- function MCTView(openmct) {
- return {
- restrict: 'E',
- link: function (scope, element, attrs) {
- const provider = openmct.objectViews.getByProviderKey(attrs.mctProviderKey);
- const view = new provider.view(scope.domainObject.useCapability('adapter'));
- const domElement = element[0];
-
- view.show(domElement);
-
- if (view.destroy) {
- scope.$on('$destroy', function () {
- view.destroy(domElement);
- });
- }
- }
- };
- }
-
- return MCTView;
-});
diff --git a/src/adapter/indicators/legacy-indicators-plugin.js b/src/adapter/indicators/legacy-indicators-plugin.js
deleted file mode 100644
index ae6f1d514..000000000
--- a/src/adapter/indicators/legacy-indicators-plugin.js
+++ /dev/null
@@ -1,69 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-define([], function () {
- const LEGACY_INDICATOR_TEMPLATE =
- '<mct-include '
- + ' ng-model="indicator" '
- + ' class="h-indicator" '
- + ' key="template">'
- + ' </mct-include>';
-
- return function () {
- return function (openmct) {
- openmct.legacyExtension('runs', {
- depends: ['indicators[]'],
- implementation: addLegacyIndicators
- });
-
- function addLegacyIndicators(legacyIndicators) {
- legacyIndicators.forEach(function (legacyIndicatorDef) {
- const legacyIndicator = initializeIfNeeded(legacyIndicatorDef);
- const legacyIndicatorElement = buildLegacyIndicator(legacyIndicator, legacyIndicatorDef.template);
- openmct.indicators.add({
- element: legacyIndicatorElement
- });
- });
- }
-
- function initializeIfNeeded(LegacyIndicatorDef) {
- let legacyIndicator;
- if (typeof LegacyIndicatorDef === 'function') {
- legacyIndicator = new LegacyIndicatorDef();
- } else {
- legacyIndicator = LegacyIndicatorDef;
- }
-
- return legacyIndicator;
- }
-
- function buildLegacyIndicator(legacyIndicator, template) {
- const $compile = openmct.$injector.get('$compile');
- const $rootScope = openmct.$injector.get('$rootScope');
- const scope = $rootScope.$new(true);
- scope.indicator = legacyIndicator;
- scope.template = template || 'indicator';
-
- return $compile(LEGACY_INDICATOR_TEMPLATE)(scope)[0];
- }
- };
- };
-});
diff --git a/src/adapter/indicators/legacy-indicators-pluginSpec.js b/src/adapter/indicators/legacy-indicators-pluginSpec.js
deleted file mode 100644
index efa2eef5c..000000000
--- a/src/adapter/indicators/legacy-indicators-pluginSpec.js
+++ /dev/null
@@ -1,114 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- [
- "../../MCT",
- "../../../platform/commonUI/general/src/directives/MCTIndicators",
- "./legacy-indicators-plugin"
- ],
- function (
- MCT,
- MCTIndicators,
- LegacyIndicatorsPlugin
- ) {
- let openmct;
- let directive;
- let holderElement;
- const legacyExtensionFunction = MCT.prototype.legacyExtension;
- let legacyIndicatorsRunsFunction;
-
- xdescribe('The legacy indicators plugin', function () {
- beforeEach(function () {
- mockLegacyExtensionFunction();
-
- openmct = new MCT();
- directive = new MCTIndicators(openmct);
- holderElement = document.createElement('div');
-
- mockAngularComponents();
- LegacyIndicatorsPlugin()(openmct);
- });
-
- afterEach(function () {
- MCT.prototype.legacyExtension = legacyExtensionFunction;
- });
-
- function mockLegacyExtensionFunction() {
- spyOn(MCT.prototype, "legacyExtension");
- MCT.prototype.legacyExtension.and.callFake(function (extensionName, definition) {
- if (extensionName === 'runs') {
- legacyIndicatorsRunsFunction = definition.implementation;
- }
- });
- }
-
- function mockAngularComponents() {
- const mockInjector = jasmine.createSpyObj('$injector', ['get']);
- const mockCompile = jasmine.createSpy('$compile');
- const mockRootScope = jasmine.createSpyObj('rootScope', ['$new']);
- const mockScope = {};
-
- mockRootScope.$new.and.returnValue(mockScope);
- mockInjector.get.and.callFake(function (service) {
- return {
- '$compile': mockCompile,
- '$rootScope': mockRootScope
- }[service];
- });
-
- openmct.$injector = mockInjector;
- mockCompile.and.callFake(function () {
- return function () {
- return [document.createElement('div')];
- };
- });
- }
-
- it("Displays any legacy indicators ", function () {
- const legacyIndicators = [{}, {}, {}, {}];
-
- legacyIndicatorsRunsFunction(legacyIndicators);
- renderIndicators();
-
- expect(holderElement.children.length).toBe(legacyIndicators.length);
-
- });
-
- it("If legacy indicator is defined as a constructor function, executes function ", function () {
- const mockConstructorFunction = jasmine.createSpy('mockIndicatorConstructor');
- const legacyIndicators = [{}, mockConstructorFunction];
-
- mockConstructorFunction.and.returnValue({});
- legacyIndicatorsRunsFunction(legacyIndicators);
- renderIndicators();
-
- expect(holderElement.children.length).toBe(legacyIndicators.length);
- expect(mockConstructorFunction).toHaveBeenCalled();
- });
-
- function renderIndicators() {
- directive.link({}, holderElement);
- }
- });
- }
-);
diff --git a/src/adapter/policies/README.md b/src/adapter/policies/README.md
deleted file mode 100644
index f00ed0476..000000000
--- a/src/adapter/policies/README.md
+++ /dev/null
@@ -1,7 +0,0 @@
-# Espresso Theme
-Dark theme for the Open MCT user interface.
-
-## Installation
-```js
-openmct.install(openmct.plugins.Espresso());
-``` \ No newline at end of file
diff --git a/src/adapter/runs/AlternateCompositionInitializer.js b/src/adapter/runs/AlternateCompositionInitializer.js
deleted file mode 100644
index 19463fda1..000000000
--- a/src/adapter/runs/AlternateCompositionInitializer.js
+++ /dev/null
@@ -1,42 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT Web includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
- '../capabilities/AlternateCompositionCapability',
- 'objectUtils'
-], function (
- AlternateCompositionCapability,
- objectUtils
-) {
- // Present to work around the need for openmct to be used
- // from AlternateCompositionCapability.appliesTo, even though it
- // cannot be injected.
- function AlternateCompositionInitializer(openmct) {
- AlternateCompositionCapability.appliesTo = function (model, id) {
- model = objectUtils.toNewFormat(model, id || '');
-
- return Boolean(openmct.composition.get(model));
- };
- }
-
- return AlternateCompositionInitializer;
-});
diff --git a/src/adapter/runs/LegacyTelemetryProvider.js b/src/adapter/runs/LegacyTelemetryProvider.js
deleted file mode 100644
index 7ade22483..000000000
--- a/src/adapter/runs/LegacyTelemetryProvider.js
+++ /dev/null
@@ -1,193 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
- 'objectUtils'
-], function (
- utils
-) {
- /**
- * @implements module:openmct.TelemetryAPI~TelemetryProvider
- * @constructor
- */
- function LegacyTelemetryProvider(openmct, instantiate) {
- this.telemetryApi = openmct.telemetry;
- this.instantiate = instantiate;
- }
-
- /**
- * Can provide telemetry for all objects that have the "telemetry"
- * capability
- *
- * @see module:openmct.TelemetryAPI~TelemetryProvider#canProvideTelemetry
- */
- LegacyTelemetryProvider.prototype.canProvideTelemetry = function (domainObject) {
- return this.instantiate(utils.toOldFormat(domainObject),
- utils.makeKeyString(domainObject.identifier)).hasCapability("telemetry");
- };
-
- LegacyTelemetryProvider.prototype.supportsRequest =
- LegacyTelemetryProvider.prototype.supportsSubscribe =
- LegacyTelemetryProvider.prototype.canProvideTelemetry;
-
- function createDatum(domainObject, metadata, legacySeries, i) {
- let datum;
-
- if (legacySeries.getDatum) {
- datum = legacySeries.getDatum(i);
- } else {
- datum = {};
- metadata.valuesForHints(['domain']).forEach(function (metadatum) {
- datum[metadatum.key] = legacySeries.getDomainValue(i, metadatum.key);
- });
-
- metadata.valuesForHints(['range']).forEach(function (metadatum) {
- datum[metadatum.key] = legacySeries.getRangeValue(i, metadatum.key);
- });
- }
-
- /**
- * If telemetry metadata defines a 'name' field, and one is not present
- * on the datum, add it.
- */
- if (metadata.value('name') !== undefined && datum.name === undefined) {
- datum.name = domainObject.name;
- }
-
- return datum;
- }
-
- function adaptSeries(domainObject, metadata, legacySeries) {
- const series = [];
-
- for (let i = 0; i < legacySeries.getPointCount(); i++) {
- series.push(createDatum(domainObject, metadata, legacySeries, i));
- }
-
- return series;
- }
-
- /**
- * @typedef {object} ConvertedTelemetryObject
- * Telemetry data objects are converted from TelemetrySeries. Metadata is used
- * to populate the returned object with attributes corresponding to the keys
- * of domains and ranges. The attribute values are those returned by calls to
- * [TelemetrySeries.getDomainValue()]{@link TelemetrySeries#getDomainValue}
- * and [TelemetrySeries.getRangeValue()]{@link TelemetrySeries#getRangeValue}.
- */
-
- /**
- * @see module:openmct.TelemetryAPI~TelemetryProvider#request
- * @param {module:openmct.DomainObject}
- * @param {module:openmct.TelemetryAPI~TelemetryRequest} options
- * options for this request. Passed straight through to legacy provider
- * @returns {Promise.<ConvertedTelemetryObject[]>} a promise for an array of
- * telemetry data.
- */
- LegacyTelemetryProvider.prototype.request = function (domainObject, request) {
- const metadata = this.telemetryApi.getMetadata(domainObject);
- const oldObject = this.instantiate(utils.toOldFormat(domainObject), utils.makeKeyString(domainObject.identifier));
- const capability = oldObject.getCapability("telemetry");
-
- return capability.requestData(request).then(function (telemetrySeries) {
- return Promise.resolve(adaptSeries(domainObject, metadata, telemetrySeries));
- }).catch(function (error) {
- return Promise.reject(error);
- });
- };
-
- /**
- * @callback LegacyTelemetryProvider~SubscribeCallback
- * @param {ConvertedTelemetryObject}
- */
-
- /**
- * @see module:openmct.TelemetryAPI~TelemetryProvider#request
- * @param {module:openmct.DomainObject}
- * @param {LegacyTelemetryProvider~SubscribeCallback} callback will be called with a single datum when
- * new data is available.
- * @param {module:openmct.TelemetryAPI~TelemetryRequest} options
- * options for this request. Passed straight through to legacy provider
- * @returns {platform|telemetry.TelemetrySubscription|*}
- */
- LegacyTelemetryProvider.prototype.subscribe = function (domainObject, callback, request) {
- const metadata = this.telemetryApi.getMetadata(domainObject);
- const oldObject = this.instantiate(utils.toOldFormat(domainObject), utils.makeKeyString(domainObject.identifier));
- const capability = oldObject.getCapability("telemetry");
-
- function callbackWrapper(series) {
- callback(createDatum(domainObject, metadata, series, series.getPointCount() - 1));
- }
-
- return capability.subscribe(callbackWrapper, request) || function () {};
- };
-
- LegacyTelemetryProvider.prototype.supportsLimits = function (domainObject) {
- const oldObject = this.instantiate(
- utils.toOldFormat(domainObject),
- utils.makeKeyString(domainObject.identifier)
- );
-
- return oldObject.hasCapability("limit");
- };
-
- LegacyTelemetryProvider.prototype.getLimitEvaluator = function (domainObject) {
- const oldObject = this.instantiate(
- utils.toOldFormat(domainObject),
- utils.makeKeyString(domainObject.identifier)
- );
- const limitEvaluator = oldObject.getCapability("limit");
-
- return {
- evaluate: function (datum, property) {
- return limitEvaluator.evaluate(datum, property && property.key);
- }
-
- };
- };
-
- LegacyTelemetryProvider.prototype.getLimits = function (domainObject) {
- const oldObject = this.instantiate(
- utils.toOldFormat(domainObject),
- utils.makeKeyString(domainObject.identifier)
- );
- const limitEvaluator = oldObject.getCapability("limit");
-
- return {
- limits: () => {
- return limitEvaluator.limits.then !== undefined
- ? limitEvaluator.limits()
- : Promise.resolve(limitEvaluator.limits());
- }
- };
- };
-
- return function (openmct, instantiate) {
- // Legacy provider should always be the fallback.
- const provider = new LegacyTelemetryProvider(openmct, instantiate);
- openmct.telemetry.legacyProvider = provider;
- openmct.telemetry.requestProviders.push(provider);
- openmct.telemetry.subscriptionProviders.push(provider);
- openmct.telemetry.limitProviders.push(provider);
- };
-
-});
diff --git a/src/adapter/runs/RegisterLegacyTypes.js b/src/adapter/runs/RegisterLegacyTypes.js
deleted file mode 100644
index 35af77c91..000000000
--- a/src/adapter/runs/RegisterLegacyTypes.js
+++ /dev/null
@@ -1,11 +0,0 @@
-define([
-
-], function (
-
-) {
- function RegisterLegacyTypes(types, openmct) {
- openmct.types.importLegacyTypes(types);
- }
-
- return RegisterLegacyTypes;
-});
diff --git a/src/adapter/services/Instantiate.js b/src/adapter/services/Instantiate.js
deleted file mode 100644
index a719a4d11..000000000
--- a/src/adapter/services/Instantiate.js
+++ /dev/null
@@ -1,50 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(
- ['../../../platform/core/src/objects/DomainObjectImpl'],
- function (DomainObjectImpl) {
-
- /**
- * Overrides platform version of instantiate, passes Id with model such
- * that capability detection can utilize new format domain objects.
- */
- function Instantiate(
- capabilityService,
- identifierService,
- cacheService
- ) {
- return function (model, id) {
- id = id || identifierService.generate();
- const old_id = model.id;
- model.id = id;
- const capabilities = capabilityService.getCapabilities(model, id);
- model.id = old_id;
- cacheService.put(id, model);
-
- return new DomainObjectImpl(id, model, capabilities);
- };
- }
-
- return Instantiate;
- }
-);
diff --git a/src/adapter/services/LegacyObjectAPIInterceptor.js b/src/adapter/services/LegacyObjectAPIInterceptor.js
deleted file mode 100644
index 1615d6c46..000000000
--- a/src/adapter/services/LegacyObjectAPIInterceptor.js
+++ /dev/null
@@ -1,196 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define([
- 'objectUtils'
-], function (
- utils
-) {
- function ObjectServiceProvider(eventEmitter, objectService, instantiate, topic, $injector) {
- this.eventEmitter = eventEmitter;
- this.objectService = objectService;
- this.instantiate = instantiate;
- this.$injector = $injector;
-
- this.generalTopic = topic('mutation');
- this.bridgeEventBuses();
- }
-
- /**
- * Bridges old and new style mutation events to provide compatibility between the two APIs
- * @private
- */
- ObjectServiceProvider.prototype.bridgeEventBuses = function () {
- let removeGeneralTopicListener;
- let handleLegacyMutation;
-
- const handleMutation = function (newStyleObject) {
- const keyString = utils.makeKeyString(newStyleObject.identifier);
- const oldStyleObject = this.instantiate(utils.toOldFormat(newStyleObject), keyString);
-
- // Don't trigger self
- removeGeneralTopicListener();
-
- oldStyleObject.getCapability('mutation').mutate(function () {
- return utils.toOldFormat(newStyleObject);
- }, newStyleObject.modified);
-
- removeGeneralTopicListener = this.generalTopic.listen(handleLegacyMutation);
- }.bind(this);
-
- handleLegacyMutation = function (legacyObject) {
- const newStyleObject = utils.toNewFormat(legacyObject.getModel(), legacyObject.getId());
- const keystring = utils.makeKeyString(newStyleObject.identifier);
-
- this.eventEmitter.emit(keystring + ':$_synchronize_model', newStyleObject);
- this.eventEmitter.emit(keystring + ":*", newStyleObject);
- this.eventEmitter.emit('mutation', newStyleObject);
- }.bind(this);
-
- this.eventEmitter.on('mutation', handleMutation);
- removeGeneralTopicListener = this.generalTopic.listen(handleLegacyMutation);
- };
-
- ObjectServiceProvider.prototype.create = async function (object) {
- let model = utils.toOldFormat(object);
-
- let result = await this.getPersistenceService().createObject(
- this.getSpace(utils.makeKeyString(object.identifier)),
- object.identifier.key,
- model
- );
-
- return result;
- };
-
- ObjectServiceProvider.prototype.update = async function (object) {
- let model = utils.toOldFormat(object);
-
- let result = await this.getPersistenceService().updateObject(
- this.getSpace(utils.makeKeyString(object.identifier)),
- object.identifier.key,
- model
- );
-
- return result;
- };
-
- /**
- * Get the space in which this domain object is persisted;
- * this is useful when, for example, decided which space a
- * newly-created domain object should be persisted to (by
- * default, this should be the space of its containing
- * object.)
- *
- * @returns {string} the name of the space which should
- * be used to persist this object
- */
- ObjectServiceProvider.prototype.getSpace = function (keystring) {
- return this.getIdentifierService().parse(keystring).getSpace();
- };
-
- ObjectServiceProvider.prototype.getIdentifierService = function () {
- if (this.identifierService === undefined) {
- this.identifierService = this.$injector.get('identifierService');
- }
-
- return this.identifierService;
- };
-
- ObjectServiceProvider.prototype.getPersistenceService = function () {
- if (this.persistenceService === undefined) {
- this.persistenceService = this.$injector.get('persistenceService');
- }
-
- return this.persistenceService;
- };
-
- ObjectServiceProvider.prototype.delete = function (object) {
- // TODO!
- };
-
- ObjectServiceProvider.prototype.get = function (key) {
- let keyString = utils.makeKeyString(key);
-
- return this.objectService.getObjects([keyString])
- .then(function (results) {
- if (results[keyString]) {
- let model = results[keyString].getModel();
-
- return utils.toNewFormat(model, key);
- }
-
- return;
- });
- };
-
- ObjectServiceProvider.prototype.superSecretFallbackSearch = function (query, abortSignal) {
- const searchService = this.$injector.get('searchService');
-
- // need to pass the abortSignal down, so need to
- // pass in undefined for maxResults and filter on query
- return searchService.query(query, undefined, undefined, abortSignal);
- };
-
- // Injects new object API as a decorator so that it hijacks all requests.
- // Object providers implemented on new API should just work, old API should just work, many things may break.
- function LegacyObjectAPIInterceptor(openmct, ROOTS, instantiate, topic, objectService) {
- const eventEmitter = openmct.objects.eventEmitter;
-
- this.getObjects = function (keys, abortSignal) {
- const results = {};
-
- const promises = keys.map(function (keyString) {
- const key = utils.parseKeyString(keyString);
-
- return openmct.objects.get(key, abortSignal)
- .then(function (object) {
- object = utils.toOldFormat(object);
- results[keyString] = instantiate(object, keyString);
- });
- });
-
- return Promise.all(promises)
- .then(function () {
- return results;
- });
- };
-
- openmct.objects.supersecretSetFallbackProvider(
- new ObjectServiceProvider(
- eventEmitter,
- objectService,
- instantiate,
- topic,
- openmct.$injector
- )
- );
-
- ROOTS.forEach(function (r) {
- openmct.objects.addRoot(utils.parseKeyString(r.id));
- });
-
- return this;
- }
-
- return LegacyObjectAPIInterceptor;
-});
diff --git a/src/adapter/services/LegacyPersistenceAdapter.js b/src/adapter/services/LegacyPersistenceAdapter.js
deleted file mode 100644
index 11d14c93e..000000000
--- a/src/adapter/services/LegacyPersistenceAdapter.js
+++ /dev/null
@@ -1,77 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-import utils from 'objectUtils';
-
-export default class LegacyPersistenceAdapter {
- constructor(openmct) {
- this.openmct = openmct;
- }
-
- listObjects() {
- return Promise.resolve([]);
- }
-
- listSpaces() {
- return Promise.resolve(Object.keys(this.openmct.objects.providers));
- }
-
- createObject(space, key, legacyDomainObject) {
- let object = utils.toNewFormat(legacyDomainObject, {
- namespace: space,
- key: key
- });
-
- return this.openmct.objects.save(object);
- }
-
- deleteObject(space, key) {
- const identifier = {
- namespace: space,
- key: key
- };
-
- return this.openmct.objects.delete(identifier);
- }
-
- updateObject(space, key, legacyDomainObject) {
- let object = utils.toNewFormat(legacyDomainObject, {
- namespace: space,
- key: key
- });
-
- return this.openmct.objects.save(object);
- }
-
- readObject(space, key) {
- const identifier = {
- namespace: space,
- key: key
- };
-
- return this.openmct.objects.get(identifier).then(domainObject => {
- let object = this.openmct.legacyObject(domainObject);
-
- return object.model;
- });
- }
-}
diff --git a/src/adapter/templates/adapted-view-template.html b/src/adapter/templates/adapted-view-template.html
deleted file mode 100644
index f1fcd8435..000000000
--- a/src/adapter/templates/adapted-view-template.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
- as represented by the Administrator of the National Aeronautics and Space
- Administration. All rights reserved.
-
- Open MCT is licensed under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0.
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Open MCT includes source code licensed under additional open source
- licenses. See the Open Source Licenses file (LICENSES.md) included with
- this source code distribution or the Licensing information page available
- at runtime from the About dialog for additional information.
--->
-<div ng-controller="AdaptedViewController" mct-view="view"></div>
diff --git a/src/adapter/views/LegacyViewProvider.js b/src/adapter/views/LegacyViewProvider.js
deleted file mode 100644
index 1a1714c76..000000000
--- a/src/adapter/views/LegacyViewProvider.js
+++ /dev/null
@@ -1,141 +0,0 @@
-define([
-
-], function (
-
-) {
- const DEFAULT_VIEW_PRIORITY = 100;
-
- const PRIORITY_LEVELS = {
- "fallback": Number.NEGATIVE_INFINITY,
- "default": -100,
- "none": 0,
- "optional": DEFAULT_VIEW_PRIORITY,
- "preferred": 1000,
- "mandatory": Number.POSITIVE_INFINITY
- };
-
- function LegacyViewProvider(legacyView, openmct, convertToLegacyObject) {
- return {
- key: legacyView.key,
- name: legacyView.name,
- cssClass: legacyView.cssClass,
- description: legacyView.description,
- canEdit: function () {
- return legacyView.editable === true;
- },
- canView: function (domainObject) {
- if (!domainObject || !domainObject.identifier) {
- return false;
- }
-
- if (legacyView.type) {
- return domainObject.type === legacyView.type;
- }
-
- let legacyObject = convertToLegacyObject(domainObject);
- if (legacyView.needs) {
- let meetsNeeds = legacyView.needs.every(k => legacyObject.hasCapability(k));
- if (!meetsNeeds) {
- return false;
- }
- }
-
- return openmct.$injector.get('policyService').allow(
- 'view', legacyView, legacyObject
- );
- },
- view: function (domainObject) {
- let $rootScope = openmct.$injector.get('$rootScope');
- let templateLinker = openmct.$injector.get('templateLinker');
- let scope = $rootScope.$new(true);
- let legacyObject = convertToLegacyObject(domainObject);
- let isDestroyed = false;
- let unlistenToStatus;
- let element;
- scope.domainObject = legacyObject;
- scope.model = legacyObject.getModel();
- let child;
- let parent;
-
- return {
- show: function (container) {
- parent = container;
- child = document.createElement('div');
- parent.appendChild(child);
- let statusCapability = legacyObject.getCapability('status');
- unlistenToStatus = statusCapability.listen((newStatus) => {
- child.classList.remove('s-status-timeconductor-unsynced');
-
- if (newStatus.includes('timeconductor-unsynced')) {
- child.classList.add('s-status-timeconductor-unsynced');
- }
- });
-
- // TODO: implement "gestures" support ?
- let uses = legacyView.uses || [];
- let promises = [];
- let results = uses.map(function (capabilityKey, i) {
- let result = legacyObject.useCapability(capabilityKey);
- if (result.then) {
- promises.push(result.then(function (r) {
- results[i] = r;
- }));
- }
-
- return result;
- });
-
- function link() {
- if (isDestroyed) {
- return;
- }
-
- uses.forEach(function (key, i) {
- scope[key] = results[i];
- });
- element = openmct.$angular.element(child);
- templateLinker.link(
- scope,
- element,
- legacyView
- );
- child.classList.add('u-contents');
- }
-
- if (promises.length) {
- Promise.all(promises)
- .then(function () {
- link();
- scope.$digest();
- });
- } else {
- link();
- }
- },
- onClearData() {
- scope.$broadcast('clearData');
- },
- destroy: function () {
- element.off();
- element.remove();
- scope.$destroy();
- element = null;
- scope = null;
- unlistenToStatus();
- }
- };
- },
- priority: function () {
- let priority = legacyView.priority || DEFAULT_VIEW_PRIORITY;
- if (typeof priority === 'string') {
- priority = PRIORITY_LEVELS[priority];
- }
-
- return priority;
- }
- };
- }
-
- return LegacyViewProvider;
-
-});
diff --git a/src/adapter/views/TypeInspectorViewProvider.js b/src/adapter/views/TypeInspectorViewProvider.js
deleted file mode 100644
index f7c3c6bd9..000000000
--- a/src/adapter/views/TypeInspectorViewProvider.js
+++ /dev/null
@@ -1,98 +0,0 @@
-define([
-
-], function (
-
-) {
- function TypeInspectorViewProvider(typeDefinition, openmct, convertToLegacyObject) {
- let representation = openmct.$injector.get('representations[]')
- .filter((r) => r.key === typeDefinition.inspector)[0];
-
- return {
- key: representation.key,
- name: representation.name,
- cssClass: representation.cssClass,
- description: representation.description,
- canView: function (selection) {
- if (selection.length !== 1 || selection[0].length === 0) {
- return false;
- }
-
- let selectionContext = selection[0][0].context;
-
- if (!selectionContext.item) {
- return false;
- }
-
- return selectionContext.item.type === typeDefinition.key;
- },
- view: function (selection) {
- let domainObject = selection[0][0].context.item;
- let $rootScope = openmct.$injector.get('$rootScope');
- let templateLinker = openmct.$injector.get('templateLinker');
- let scope = $rootScope.$new(true);
- let legacyObject = convertToLegacyObject(domainObject);
- let isDestroyed = false;
- let element;
- scope.domainObject = legacyObject;
- scope.model = legacyObject.getModel();
-
- return {
- show: function (container) {
- let child = document.createElement('div');
- container.appendChild(child);
- // TODO: implement "gestures" support ?
- let uses = representation.uses || [];
- let promises = [];
- let results = uses.map(function (capabilityKey, i) {
- let result = legacyObject.useCapability(capabilityKey);
- if (result.then) {
- promises.push(result.then(function (r) {
- results[i] = r;
- }));
- }
-
- return result;
- });
-
- function link() {
- if (isDestroyed) {
- return;
- }
-
- uses.forEach(function (key, i) {
- scope[key] = results[i];
- });
- element = openmct.$angular.element(child);
- templateLinker.link(
- scope,
- element,
- representation
- );
- container.style.height = '100%';
- }
-
- if (promises.length) {
- Promise.all(promises)
- .then(function () {
- link();
- scope.$digest();
- });
- } else {
- link();
- }
- },
- destroy: function () {
- element.off();
- element.remove();
- scope.$destroy();
- element = null;
- scope = null;
- }
- };
- }
- };
- }
-
- return TypeInspectorViewProvider;
-
-});
diff --git a/src/adapter/views/installLegacyViews.js b/src/adapter/views/installLegacyViews.js
deleted file mode 100644
index 86419a09c..000000000
--- a/src/adapter/views/installLegacyViews.js
+++ /dev/null
@@ -1,32 +0,0 @@
-define([
- './LegacyViewProvider',
- './TypeInspectorViewProvider',
- 'objectUtils'
-], function (
- LegacyViewProvider,
- TypeInspectorViewProvider,
- objectUtils
-) {
- function installLegacyViews(openmct, legacyViews, instantiate) {
-
- function convertToLegacyObject(domainObject) {
- let keyString = objectUtils.makeKeyString(domainObject.identifier);
- let oldModel = objectUtils.toOldFormat(domainObject);
-
- return instantiate(oldModel, keyString);
- }
-
- legacyViews.forEach(function (legacyView) {
- openmct.objectViews.addProvider(new LegacyViewProvider(legacyView, openmct, convertToLegacyObject));
- });
-
- let inspectorTypes = openmct.$injector.get('types[]')
- .filter((t) => Object.prototype.hasOwnProperty.call(t, 'inspector'));
-
- inspectorTypes.forEach(function (typeDefinition) {
- openmct.inspectorViews.addProvider(new TypeInspectorViewProvider(typeDefinition, openmct, convertToLegacyObject));
- });
- }
-
- return installLegacyViews;
-});
diff --git a/src/api/Branding.js b/src/api/Branding.js
index 502db096a..c44821741 100644
--- a/src/api/Branding.js
+++ b/src/api/Branding.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/api/Editor.js b/src/api/Editor.js
index 02c679a16..d919da383 100644
--- a/src/api/Editor.js
+++ b/src/api/Editor.js
@@ -1,6 +1,6 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/api/actions/ActionCollection.js b/src/api/actions/ActionCollection.js
index 6545dd4a0..6606616b0 100644
--- a/src/api/actions/ActionCollection.js
+++ b/src/api/actions/ActionCollection.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -85,8 +85,6 @@ class ActionCollection extends EventEmitter {
}
destroy() {
- super.removeAllListeners();
-
if (!this.skipEnvironmentObservers) {
this.objectUnsubscribes.forEach(unsubscribe => {
unsubscribe();
@@ -96,6 +94,7 @@ class ActionCollection extends EventEmitter {
}
this.emit('destroy', this.view);
+ this.removeAllListeners();
}
getVisibleActions() {
diff --git a/src/api/actions/ActionCollectionSpec.js b/src/api/actions/ActionCollectionSpec.js
index 99145b767..3dbb13329 100644
--- a/src/api/actions/ActionCollectionSpec.js
+++ b/src/api/actions/ActionCollectionSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/api/actions/ActionsAPI.js b/src/api/actions/ActionsAPI.js
index 8af7b3cbb..c6a88dbd6 100644
--- a/src/api/actions/ActionsAPI.js
+++ b/src/api/actions/ActionsAPI.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/api/actions/ActionsAPISpec.js b/src/api/actions/ActionsAPISpec.js
index e293d6ba8..58759acb9 100644
--- a/src/api/actions/ActionsAPISpec.js
+++ b/src/api/actions/ActionsAPISpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/api/annotation/AnnotationAPI.js b/src/api/annotation/AnnotationAPI.js
new file mode 100644
index 000000000..a19762038
--- /dev/null
+++ b/src/api/annotation/AnnotationAPI.js
@@ -0,0 +1,277 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+import { v4 as uuid } from 'uuid';
+import EventEmitter from 'EventEmitter';
+
+/**
+ * @readonly
+ * @enum {String} AnnotationType
+ * @property {String} NOTEBOOK The notebook annotation type
+ * @property {String} GEOSPATIAL The geospatial annotation type
+ * @property {String} PIXEL_SPATIAL The pixel-spatial annotation type
+ * @property {String} TEMPORAL The temporal annotation type
+ * @property {String} PLOT_SPATIAL The plot-spatial annotation type
+ */
+const ANNOTATION_TYPES = Object.freeze({
+ NOTEBOOK: 'NOTEBOOK',
+ GEOSPATIAL: 'GEOSPATIAL',
+ PIXEL_SPATIAL: 'PIXEL_SPATIAL',
+ TEMPORAL: 'TEMPORAL',
+ PLOT_SPATIAL: 'PLOT_SPATIAL'
+});
+
+/**
+ * @typedef {Object} Tag
+ * @property {String} key a unique identifier for the tag
+ * @property {String} backgroundColor eg. "#cc0000"
+ * @property {String} foregroundColor eg. "#ffffff"
+ */
+export default class AnnotationAPI extends EventEmitter {
+ constructor(openmct) {
+ super();
+ this.openmct = openmct;
+ this.availableTags = {};
+
+ this.ANNOTATION_TYPES = ANNOTATION_TYPES;
+
+ this.openmct.types.addType('annotation', {
+ name: 'Annotation',
+ description: 'A user created note or comment about time ranges, pixel space, and geospatial features.',
+ creatable: false,
+ cssClass: 'icon-notebook',
+ initialize: function (domainObject) {
+ domainObject.targets = domainObject.targets || {};
+ domainObject.originalContextPath = domainObject.originalContextPath || '';
+ domainObject.tags = domainObject.tags || [];
+ domainObject.contentText = domainObject.contentText || '';
+ domainObject.annotationType = domainObject.annotationType || 'plotspatial';
+ }
+ });
+ }
+
+ /**
+ * Create the a generic annotation
+ * @typedef {Object} CreateAnnotationOptions
+ * @property {String} name a name for the new parameter
+ * @property {import('../objects/ObjectAPI').DomainObject} domainObject the domain object to create
+ * @property {ANNOTATION_TYPES} annotationType the type of annotation to create
+ * @property {Tag[]} tags
+ * @property {String} contentText
+ * @property {import('../objects/ObjectAPI').Identifier[]} targets
+ */
+ /**
+ * @method create
+ * @param {CreateAnnotationOptions} options
+ * @returns {Promise<import('../objects/ObjectAPI').DomainObject>} a promise which will resolve when the domain object
+ * has been created, or be rejected if it cannot be saved
+ */
+ async create({name, domainObject, annotationType, tags, contentText, targets}) {
+ if (!Object.keys(this.ANNOTATION_TYPES).includes(annotationType)) {
+ throw new Error(`Unknown annotation type: ${annotationType}`);
+ }
+
+ if (!Object.keys(targets).length) {
+ throw new Error(`At least one target is required to create an annotation`);
+ }
+
+ const domainObjectKeyString = this.openmct.objects.makeKeyString(domainObject.identifier);
+ const originalPathObjects = await this.openmct.objects.getOriginalPath(domainObjectKeyString);
+ const originalContextPath = this.openmct.objects.getRelativePath(originalPathObjects);
+ const namespace = domainObject.identifier.namespace;
+ const type = 'annotation';
+ const typeDefinition = this.openmct.types.get(type);
+ const definition = typeDefinition.definition;
+
+ const createdObject = {
+ name,
+ type,
+ identifier: {
+ key: uuid(),
+ namespace
+ },
+ tags,
+ annotationType,
+ contentText,
+ originalContextPath
+ };
+
+ if (definition.initialize) {
+ definition.initialize(createdObject);
+ }
+
+ createdObject.targets = targets;
+ createdObject.originalContextPath = originalContextPath;
+
+ const success = await this.openmct.objects.save(createdObject);
+ if (success) {
+ this.emit('annotationCreated', createdObject);
+
+ return createdObject;
+ } else {
+ throw new Error('Failed to create object');
+ }
+ }
+
+ defineTag(tagKey, tagsDefinition) {
+ this.availableTags[tagKey] = tagsDefinition;
+ }
+
+ getAvailableTags() {
+ if (this.availableTags) {
+ const rearrangedToArray = Object.keys(this.availableTags).map(tagKey => {
+ return {
+ id: tagKey,
+ ...this.availableTags[tagKey]
+ };
+ });
+
+ return rearrangedToArray;
+ } else {
+ return [];
+ }
+ }
+
+ async getAnnotation(query, searchType) {
+ let foundAnnotation = null;
+
+ const searchResults = (await Promise.all(this.openmct.objects.search(query, null, searchType))).flat();
+ if (searchResults) {
+ foundAnnotation = searchResults[0];
+ }
+
+ return foundAnnotation;
+ }
+
+ async addAnnotationTag(existingAnnotation, targetDomainObject, targetSpecificDetails, annotationType, tag) {
+ if (!existingAnnotation) {
+ const targets = {};
+ const targetKeyString = this.openmct.objects.makeKeyString(targetDomainObject.identifier);
+ targets[targetKeyString] = targetSpecificDetails;
+ const contentText = `${annotationType} tag`;
+ const annotationCreationArguments = {
+ name: contentText,
+ domainObject: targetDomainObject,
+ annotationType,
+ tags: [tag],
+ contentText,
+ targets
+ };
+ const newAnnotation = await this.create(annotationCreationArguments);
+
+ return newAnnotation;
+ } else {
+ const tagArray = [tag, ...existingAnnotation.tags];
+ this.openmct.objects.mutate(existingAnnotation, 'tags', tagArray);
+
+ return existingAnnotation;
+ }
+ }
+
+ removeAnnotationTag(existingAnnotation, tagToRemove) {
+ if (existingAnnotation && existingAnnotation.tags.includes(tagToRemove)) {
+ const cleanedArray = existingAnnotation.tags.filter(extantTag => extantTag !== tagToRemove);
+ this.openmct.objects.mutate(existingAnnotation, 'tags', cleanedArray);
+ } else {
+ throw new Error(`Asked to remove tag (${tagToRemove}) that doesn't exist`, existingAnnotation);
+ }
+ }
+
+ removeAnnotationTags(existingAnnotation) {
+ // just removes tags on the annotation as we can't really delete objects
+ if (existingAnnotation && existingAnnotation.tags) {
+ this.openmct.objects.mutate(existingAnnotation, 'tags', []);
+ }
+ }
+
+ #getMatchingTags(query) {
+ if (!query) {
+ return [];
+ }
+
+ const matchingTags = Object.keys(this.availableTags).filter(tagKey => {
+ if (this.availableTags[tagKey] && this.availableTags[tagKey].label) {
+ return this.availableTags[tagKey].label.toLowerCase().includes(query.toLowerCase());
+ }
+
+ return false;
+ });
+
+ return matchingTags;
+ }
+
+ #addTagMetaInformationToResults(results, matchingTagKeys) {
+ const tagsAddedToResults = results.map(result => {
+ const fullTagModels = result.tags.map(tagKey => {
+ const tagModel = this.availableTags[tagKey];
+ tagModel.tagID = tagKey;
+
+ return tagModel;
+ });
+
+ return {
+ fullTagModels,
+ matchingTagKeys,
+ ...result
+ };
+ });
+
+ return tagsAddedToResults;
+ }
+
+ async #addTargetModelsToResults(results) {
+ const modelAddedToResults = await Promise.all(results.map(async result => {
+ const targetModels = await Promise.all(Object.keys(result.targets).map(async (targetID) => {
+ const targetModel = await this.openmct.objects.get(targetID);
+ const targetKeyString = this.openmct.objects.makeKeyString(targetModel.identifier);
+ const originalPathObjects = await this.openmct.objects.getOriginalPath(targetKeyString);
+
+ return {
+ originalPath: originalPathObjects,
+ ...targetModel
+ };
+ }));
+
+ return {
+ targetModels,
+ ...result
+ };
+ }));
+
+ return modelAddedToResults;
+ }
+
+ /**
+ * @method searchForTags
+ * @param {String} query A query to match against tags. E.g., "dr" will match the tags "drilling" and "driving"
+ * @param {Object} abortController An optional abort method to stop the query
+ * @returns {Promise} returns a model of matching tags with their target domain objects attached
+ */
+ async searchForTags(query, abortController) {
+ const matchingTagKeys = this.#getMatchingTags(query);
+ const searchResults = (await Promise.all(this.openmct.objects.search(matchingTagKeys, abortController, this.openmct.objects.SEARCH_TYPES.TAGS))).flat();
+ const appliedTagSearchResults = this.#addTagMetaInformationToResults(searchResults, matchingTagKeys);
+ const appliedTargetsModels = await this.#addTargetModelsToResults(appliedTagSearchResults);
+
+ return appliedTargetsModels;
+ }
+}
diff --git a/src/api/annotation/AnnotationAPISpec.js b/src/api/annotation/AnnotationAPISpec.js
new file mode 100644
index 000000000..731aead3c
--- /dev/null
+++ b/src/api/annotation/AnnotationAPISpec.js
@@ -0,0 +1,176 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+import { createOpenMct, resetApplicationState } from '../../utils/testing';
+import ExampleTagsPlugin from "../../../example/exampleTags/plugin";
+
+describe("The Annotation API", () => {
+ let openmct;
+ let mockObjectProvider;
+ let mockDomainObject;
+ let mockAnnotationObject;
+
+ beforeEach((done) => {
+ openmct = createOpenMct();
+ openmct.install(new ExampleTagsPlugin());
+ const availableTags = openmct.annotation.getAvailableTags();
+ mockDomainObject = {
+ type: 'notebook',
+ name: 'fooRabbitNotebook',
+ identifier: {
+ key: 'some-object',
+ namespace: 'fooNameSpace'
+ }
+ };
+ mockAnnotationObject = {
+ type: 'annotation',
+ name: 'Some Notebook Annotation',
+ annotationType: openmct.annotation.ANNOTATION_TYPES.NOTEBOOK,
+ tags: [availableTags[0].id, availableTags[1].id],
+ identifier: {
+ key: 'anAnnotationKey',
+ namespace: 'fooNameSpace'
+ },
+ targets: {
+ 'fooNameSpace:some-object': {
+ entryId: 'fooBarEntry'
+ }
+ }
+ };
+
+ mockObjectProvider = jasmine.createSpyObj("mock provider", [
+ "create",
+ "update",
+ "get"
+ ]);
+ // eslint-disable-next-line require-await
+ mockObjectProvider.get = async (identifier) => {
+ if (identifier.key === mockDomainObject.identifier.key) {
+ return mockDomainObject;
+ } else if (identifier.key === mockAnnotationObject.identifier.key) {
+ return mockAnnotationObject;
+ } else {
+ return null;
+ }
+ };
+
+ mockObjectProvider.create.and.returnValue(Promise.resolve(true));
+ mockObjectProvider.update.and.returnValue(Promise.resolve(true));
+
+ openmct.objects.addProvider('fooNameSpace', mockObjectProvider);
+ openmct.on('start', done);
+ openmct.startHeadless();
+ });
+ afterEach(async () => {
+ openmct.objects.providers = {};
+ await resetApplicationState(openmct);
+ });
+ it("is defined", () => {
+ expect(openmct.annotation).toBeDefined();
+ });
+
+ describe("Creation", () => {
+ it("can create annotations", async () => {
+ const annotationCreationArguments = {
+ name: 'Test Annotation',
+ domainObject: mockDomainObject,
+ annotationType: openmct.annotation.ANNOTATION_TYPES.NOTEBOOK,
+ tags: ['sometag'],
+ contentText: "fooContext",
+ targets: {'fooTarget': {}}
+ };
+ const annotationObject = await openmct.annotation.create(annotationCreationArguments);
+ expect(annotationObject).toBeDefined();
+ expect(annotationObject.type).toEqual('annotation');
+ });
+ it("fails if annotation is an unknown type", async () => {
+ try {
+ await openmct.annotation.create('Garbage Annotation', mockDomainObject, 'garbageAnnotation', ['sometag'], "fooContext", {'fooTarget': {}});
+ } catch (error) {
+ expect(error).toBeDefined();
+ }
+ });
+ });
+
+ describe("Tagging", () => {
+ it("can create a tag", async () => {
+ const annotationObject = await openmct.annotation.addAnnotationTag(null, mockDomainObject, {entryId: 'foo'}, openmct.annotation.ANNOTATION_TYPES.NOTEBOOK, 'aWonderfulTag');
+ expect(annotationObject).toBeDefined();
+ expect(annotationObject.type).toEqual('annotation');
+ expect(annotationObject.tags).toContain('aWonderfulTag');
+ });
+ it("can delete a tag", async () => {
+ const originalAnnotationObject = await openmct.annotation.addAnnotationTag(null, mockDomainObject, {entryId: 'foo'}, openmct.annotation.ANNOTATION_TYPES.NOTEBOOK, 'aWonderfulTag');
+ const annotationObject = await openmct.annotation.addAnnotationTag(originalAnnotationObject, mockDomainObject, {entryId: 'foo'}, openmct.annotation.ANNOTATION_TYPES.NOTEBOOK, 'anotherTagToRemove');
+ expect(annotationObject).toBeDefined();
+ openmct.annotation.removeAnnotationTag(annotationObject, 'anotherTagToRemove');
+ expect(annotationObject.tags).toEqual(['aWonderfulTag']);
+ openmct.annotation.removeAnnotationTag(annotationObject, 'aWonderfulTag');
+ expect(annotationObject.tags).toEqual([]);
+ });
+ it("throws an error if deleting non-existent tag", async () => {
+ const annotationObject = await openmct.annotation.addAnnotationTag(null, mockDomainObject, {entryId: 'foo'}, openmct.annotation.ANNOTATION_TYPES.NOTEBOOK, 'aWonderfulTag');
+ expect(annotationObject).toBeDefined();
+ expect(() => {
+ openmct.annotation.removeAnnotationTag(annotationObject, 'ThisTagShouldNotExist');
+ }).toThrow();
+ });
+ it("can remove all tags", async () => {
+ const annotationObject = await openmct.annotation.addAnnotationTag(null, mockDomainObject, {entryId: 'foo'}, openmct.annotation.ANNOTATION_TYPES.NOTEBOOK, 'aWonderfulTag');
+ expect(annotationObject).toBeDefined();
+ expect(() => {
+ openmct.annotation.removeAnnotationTags(annotationObject);
+ }).not.toThrow();
+ expect(annotationObject.tags).toEqual([]);
+ });
+ });
+
+ describe("Search", () => {
+ let sharedWorkerToRestore;
+ beforeEach(async () => {
+ // use local worker
+ sharedWorkerToRestore = openmct.objects.inMemorySearchProvider.worker;
+ openmct.objects.inMemorySearchProvider.worker = null;
+ await openmct.objects.inMemorySearchProvider.index(mockDomainObject);
+ await openmct.objects.inMemorySearchProvider.index(mockAnnotationObject);
+ });
+ afterEach(() => {
+ openmct.objects.inMemorySearchProvider.worker = sharedWorkerToRestore;
+ });
+ it("can search for tags", async () => {
+ const results = await openmct.annotation.searchForTags('S');
+ expect(results).toBeDefined();
+ expect(results.length).toEqual(1);
+ });
+ it("can get notebook annotations", async () => {
+ const targetKeyString = openmct.objects.makeKeyString(mockDomainObject.identifier);
+ const query = {
+ targetKeyString,
+ entryId: 'fooBarEntry'
+ };
+
+ const results = await openmct.annotation.getAnnotation(query, openmct.objects.SEARCH_TYPES.NOTEBOOK_ANNOTATIONS);
+ expect(results).toBeDefined();
+ expect(results.tags.length).toEqual(2);
+ });
+ });
+});
diff --git a/src/api/api.js b/src/api/api.js
index b6e4af91f..505213476 100644
--- a/src/api/api.js
+++ b/src/api/api.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -24,6 +24,7 @@ define([
'./actions/ActionsAPI',
'./composition/CompositionAPI',
'./Editor',
+ './faultmanagement/FaultManagementAPI',
'./forms/FormsAPI',
'./indicators/IndicatorAPI',
'./menu/MenuAPI',
@@ -33,11 +34,14 @@ define([
'./status/StatusAPI',
'./telemetry/TelemetryAPI',
'./time/TimeAPI',
- './types/TypeRegistry'
+ './types/TypeRegistry',
+ './user/UserAPI',
+ './annotation/AnnotationAPI'
], function (
ActionsAPI,
CompositionAPI,
EditorAPI,
+ FaultManagementAPI,
FormsAPI,
IndicatorAPI,
MenuAPI,
@@ -47,14 +51,17 @@ define([
StatusAPI,
TelemetryAPI,
TimeAPI,
- TypeRegistry
+ TypeRegistry,
+ UserAPI,
+ AnnotationAPI
) {
return {
ActionsAPI: ActionsAPI.default,
CompositionAPI: CompositionAPI,
EditorAPI: EditorAPI,
+ FaultManagementAPI: FaultManagementAPI,
FormsAPI: FormsAPI,
- IndicatorAPI: IndicatorAPI,
+ IndicatorAPI: IndicatorAPI.default,
MenuAPI: MenuAPI.default,
NotificationAPI: NotificationAPI.default,
ObjectAPI: ObjectAPI,
@@ -62,6 +69,8 @@ define([
StatusAPI: StatusAPI.default,
TelemetryAPI: TelemetryAPI,
TimeAPI: TimeAPI.default,
- TypeRegistry: TypeRegistry
+ TypeRegistry: TypeRegistry,
+ UserAPI: UserAPI.default,
+ AnnotationAPI: AnnotationAPI.default
};
});
diff --git a/src/api/composition/CompositionAPI.js b/src/api/composition/CompositionAPI.js
index 8e23faa81..02dfb0315 100644
--- a/src/api/composition/CompositionAPI.js
+++ b/src/api/composition/CompositionAPI.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/api/composition/CompositionCollection.js b/src/api/composition/CompositionCollection.js
index 5a013fbe3..e04bbe888 100644
--- a/src/api/composition/CompositionCollection.js
+++ b/src/api/composition/CompositionCollection.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/api/composition/DefaultCompositionProvider.js b/src/api/composition/DefaultCompositionProvider.js
index 99a60cf51..3a46b127f 100644
--- a/src/api/composition/DefaultCompositionProvider.js
+++ b/src/api/composition/DefaultCompositionProvider.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/api/faultmanagement/FaultManagementAPI.js b/src/api/faultmanagement/FaultManagementAPI.js
new file mode 100644
index 000000000..b22a85c09
--- /dev/null
+++ b/src/api/faultmanagement/FaultManagementAPI.js
@@ -0,0 +1,106 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+export default class FaultManagementAPI {
+ constructor(openmct) {
+ this.openmct = openmct;
+ }
+
+ addProvider(provider) {
+ this.provider = provider;
+ }
+
+ supportsActions() {
+ return this.provider?.acknowledgeFault !== undefined && this.provider?.shelveFault !== undefined;
+ }
+
+ request(domainObject) {
+ if (!this.provider?.supportsRequest(domainObject)) {
+ return Promise.reject();
+ }
+
+ return this.provider.request(domainObject);
+ }
+
+ subscribe(domainObject, callback) {
+ if (!this.provider?.supportsSubscribe(domainObject)) {
+ return Promise.reject();
+ }
+
+ return this.provider.subscribe(domainObject, callback);
+ }
+
+ acknowledgeFault(fault, ackData) {
+ return this.provider.acknowledgeFault(fault, ackData);
+ }
+
+ shelveFault(fault, shelveData) {
+ return this.provider.shelveFault(fault, shelveData);
+ }
+}
+
+/** @typedef {object} Fault
+ * @property {string} type
+ * @property {object} fault
+ * @property {boolean} fault.acknowledged
+ * @property {object} fault.currentValueInfo
+ * @property {number} fault.currentValueInfo.value
+ * @property {string} fault.currentValueInfo.rangeCondition
+ * @property {string} fault.currentValueInfo.monitoringResult
+ * @property {string} fault.id
+ * @property {string} fault.name
+ * @property {string} fault.namespace
+ * @property {number} fault.seqNum
+ * @property {string} fault.severity
+ * @property {boolean} fault.shelved
+ * @property {string} fault.shortDescription
+ * @property {string} fault.triggerTime
+ * @property {object} fault.triggerValueInfo
+ * @property {number} fault.triggerValueInfo.value
+ * @property {string} fault.triggerValueInfo.rangeCondition
+ * @property {string} fault.triggerValueInfo.monitoringResult
+ * @example
+ * {
+ * "type": "",
+ * "fault": {
+ * "acknowledged": true,
+ * "currentValueInfo": {
+ * "value": 0,
+ * "rangeCondition": "",
+ * "monitoringResult": ""
+ * },
+ * "id": "",
+ * "name": "",
+ * "namespace": "",
+ * "seqNum": 0,
+ * "severity": "",
+ * "shelved": true,
+ * "shortDescription": "",
+ * "triggerTime": "",
+ * "triggerValueInfo": {
+ * "value": 0,
+ * "rangeCondition": "",
+ * "monitoringResult": ""
+ * }
+ * }
+ * }
+ */
diff --git a/src/api/faultmanagement/FaultManagementAPISpec.js b/src/api/faultmanagement/FaultManagementAPISpec.js
new file mode 100644
index 000000000..61c87402e
--- /dev/null
+++ b/src/api/faultmanagement/FaultManagementAPISpec.js
@@ -0,0 +1,144 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * License); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+import {
+ createOpenMct,
+ resetApplicationState
+} from '../../utils/testing';
+
+const faultName = 'super duper fault';
+const aFault = {
+ type: '',
+ fault: {
+ acknowledged: true,
+ currentValueInfo: {
+ value: 0,
+ rangeCondition: '',
+ monitoringResult: ''
+ },
+ id: '',
+ name: faultName,
+ namespace: '',
+ seqNum: 0,
+ severity: '',
+ shelved: true,
+ shortDescription: '',
+ triggerTime: '',
+ triggerValueInfo: {
+ value: 0,
+ rangeCondition: '',
+ monitoringResult: ''
+ }
+ }
+};
+const faultDomainObject = {
+ name: 'it is not your fault',
+ type: 'faultManagement',
+ identifier: {
+ key: 'nobodies',
+ namespace: 'fault'
+ }
+};
+const aComment = 'THIS is my fault.';
+const faultManagementProvider = {
+ request() {
+ return Promise.resolve([aFault]);
+ },
+ subscribe(domainObject, callback) {
+ return () => {};
+ },
+ supportsRequest(domainObject) {
+ return domainObject.type === 'faultManagement';
+ },
+ supportsSubscribe(domainObject) {
+ return domainObject.type === 'faultManagement';
+ },
+ acknowledgeFault(fault, { comment = '' }) {
+ return Promise.resolve({
+ success: true
+ });
+ },
+ shelveFault(fault, shelveData) {
+ return Promise.resolve({
+ success: true
+ });
+ }
+};
+
+describe('The Fault Management API', () => {
+ let openmct;
+
+ beforeEach(() => {
+ openmct = createOpenMct();
+ openmct.install(openmct.plugins.FaultManagement());
+ // openmct.install(openmct.plugins.example.ExampleFaultSource());
+ openmct.faults.addProvider(faultManagementProvider);
+ });
+
+ afterEach(() => {
+ return resetApplicationState(openmct);
+ });
+
+ it('allows you to request a fault', async () => {
+ spyOn(faultManagementProvider, 'supportsRequest').and.callThrough();
+
+ let faultResponse = await openmct.faults.request(faultDomainObject);
+
+ expect(faultManagementProvider.supportsRequest).toHaveBeenCalledWith(faultDomainObject);
+ expect(faultResponse[0].fault.name).toEqual(faultName);
+ });
+
+ it('allows you to subscribe to a fault', () => {
+ spyOn(faultManagementProvider, 'subscribe').and.callThrough();
+ spyOn(faultManagementProvider, 'supportsSubscribe').and.callThrough();
+
+ let unsubscribe = openmct.faults.subscribe(faultDomainObject, () => {});
+
+ expect(unsubscribe).toEqual(jasmine.any(Function));
+ expect(faultManagementProvider.supportsSubscribe).toHaveBeenCalledWith(faultDomainObject);
+ expect(faultManagementProvider.subscribe).toHaveBeenCalledOnceWith(faultDomainObject, jasmine.any(Function));
+
+ });
+
+ it('will tell you if the fault management provider supports actions', () => {
+ expect(openmct.faults.supportsActions()).toBeTrue();
+ });
+
+ it('will allow you to acknowledge a fault', async () => {
+ spyOn(faultManagementProvider, 'acknowledgeFault').and.callThrough();
+
+ let ackResponse = await openmct.faults.acknowledgeFault(aFault, aComment);
+
+ expect(faultManagementProvider.acknowledgeFault).toHaveBeenCalledWith(aFault, aComment);
+ expect(ackResponse.success).toBeTrue();
+ });
+
+ it('will allow you to shelve a fault', async () => {
+ spyOn(faultManagementProvider, 'shelveFault').and.callThrough();
+
+ let shelveResponse = await openmct.faults.shelveFault(aFault, aComment);
+
+ expect(faultManagementProvider.shelveFault).toHaveBeenCalledWith(aFault, aComment);
+ expect(shelveResponse.success).toBeTrue();
+ });
+
+});
diff --git a/src/api/forms/FormController.js b/src/api/forms/FormController.js
index 93f87b9dc..68e195f4e 100644
--- a/src/api/forms/FormController.js
+++ b/src/api/forms/FormController.js
@@ -8,6 +8,7 @@ import NumberField from './components/controls/NumberField.vue';
import SelectField from './components/controls/SelectField.vue';
import TextAreaField from './components/controls/TextAreaField.vue';
import TextField from './components/controls/TextField.vue';
+import ToggleSwitchField from './components/controls/ToggleSwitchField.vue';
import Vue from 'vue';
@@ -21,7 +22,8 @@ export const DEFAULT_CONTROLS_MAP = {
'numberfield': NumberField,
'select': SelectField,
'textarea': TextAreaField,
- 'textfield': TextField
+ 'textfield': TextField,
+ 'toggleSwitch': ToggleSwitchField
};
export default class FormControl {
@@ -67,10 +69,11 @@ export default class FormControl {
*/
_getControlViewProvider(control) {
const self = this;
+ let rowComponent;
return {
show(element, model, onChange) {
- const rowComponent = new Vue({
+ rowComponent = new Vue({
el: element,
components: {
FormControlComponent: DEFAULT_CONTROLS_MAP[control]
@@ -88,8 +91,10 @@ export default class FormControl {
});
return rowComponent;
+ },
+ destroy() {
+ rowComponent.$destroy();
}
};
}
}
-
diff --git a/src/api/forms/FormsAPI.js b/src/api/forms/FormsAPI.js
index e6cf74633..439979325 100644
--- a/src/api/forms/FormsAPI.js
+++ b/src/api/forms/FormsAPI.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -23,10 +23,13 @@
import FormController from './FormController';
import FormProperties from './components/FormProperties.vue';
+import EventEmitter from 'EventEmitter';
import Vue from 'vue';
-export default class FormsAPI {
+export default class FormsAPI extends EventEmitter {
constructor(openmct) {
+ super();
+
this.openmct = openmct;
this.formController = new FormController(openmct);
}
@@ -107,6 +110,8 @@ export default class FormsAPI {
let onDismiss;
let onSave;
+ const self = this;
+
const promise = new Promise((resolve, reject) => {
onSave = onFormSave(resolve);
onDismiss = onFormDismiss(reject);
@@ -115,7 +120,7 @@ export default class FormsAPI {
const vm = new Vue({
components: { FormProperties },
provide: {
- openmct: this.openmct
+ openmct: self.openmct
},
data() {
return {
@@ -132,7 +137,7 @@ export default class FormsAPI {
if (element) {
element.append(formElement);
} else {
- overlay = this.openmct.overlays.overlay({
+ overlay = self.openmct.overlays.overlay({
element: vm.$el,
size: 'small',
onDestroy: () => vm.$destroy()
@@ -140,6 +145,7 @@ export default class FormsAPI {
}
function onFormPropertyChange(data) {
+ self.emit('onFormPropertyChange', data);
if (onChange) {
onChange(data);
}
diff --git a/src/api/forms/FormsAPISpec.js b/src/api/forms/FormsAPISpec.js
new file mode 100644
index 000000000..ac7f0fc9f
--- /dev/null
+++ b/src/api/forms/FormsAPISpec.js
@@ -0,0 +1,157 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+import { createOpenMct, resetApplicationState } from '../../utils/testing';
+
+describe('The Forms API', () => {
+ let openmct;
+ let element;
+
+ beforeEach((done) => {
+ element = document.createElement('div');
+ element.style.display = 'block';
+ element.style.width = '1920px';
+ element.style.height = '1080px';
+
+ openmct = createOpenMct();
+ openmct.on('start', done);
+
+ openmct.startHeadless(element);
+ });
+
+ afterEach(() => {
+ return resetApplicationState(openmct);
+ });
+
+ it('openmct supports form API', () => {
+ expect(openmct.forms).not.toBe(null);
+ });
+
+ describe('check default form controls exists', () => {
+ it('autocomplete', () => {
+ const control = openmct.forms.getFormControl('autocomplete');
+ expect(control).not.toBe(null);
+ });
+
+ it('clock', () => {
+ const control = openmct.forms.getFormControl('composite');
+ expect(control).not.toBe(null);
+ });
+
+ it('datetime', () => {
+ const control = openmct.forms.getFormControl('datetime');
+ expect(control).not.toBe(null);
+ });
+
+ it('file-input', () => {
+ const control = openmct.forms.getFormControl('file-input');
+ expect(control).not.toBe(null);
+ });
+
+ it('locator', () => {
+ const control = openmct.forms.getFormControl('locator');
+ expect(control).not.toBe(null);
+ });
+
+ it('numberfield', () => {
+ const control = openmct.forms.getFormControl('numberfield');
+ expect(control).not.toBe(null);
+ });
+
+ it('select', () => {
+ const control = openmct.forms.getFormControl('select');
+ expect(control).not.toBe(null);
+ });
+
+ it('textarea', () => {
+ const control = openmct.forms.getFormControl('textarea');
+ expect(control).not.toBe(null);
+ });
+
+ it('textfield', () => {
+ const control = openmct.forms.getFormControl('textfield');
+ expect(control).not.toBe(null);
+ });
+ });
+
+ it('supports user defined form controls', () => {
+ const newFormControl = {
+ show: () => {
+ console.log('show new control');
+ },
+ destroy: () => {
+ console.log('destroy');
+ }
+ };
+ openmct.forms.addNewFormControl('newFormControl', newFormControl);
+ const control = openmct.forms.getFormControl('newFormControl');
+ expect(control).not.toBe(null);
+ expect(control.show).not.toBe(null);
+ expect(control.destroy).not.toBe(null);
+ });
+
+ describe('show form on UI', () => {
+ let formStructure;
+
+ beforeEach(() => {
+ formStructure = {
+ title: 'Test Show Form',
+ sections: [
+ {
+ rows: [
+ {
+ key: 'name',
+ control: 'textfield',
+ name: 'Title',
+ pattern: '\\S+',
+ required: false,
+ cssClass: 'l-input-lg',
+ value: 'Test Name'
+ }
+ ]
+ }
+ ]
+ };
+ });
+
+ it('when container element is provided', (done) => {
+ openmct.forms.showForm(formStructure, { element }).catch(() => {
+ done();
+ });
+ const titleElement = element.querySelector('.c-overlay__dialog-title');
+ expect(titleElement.textContent).toBe(formStructure.title);
+
+ element.querySelector('.js-cancel-button').click();
+ });
+
+ it('when container element is not provided', (done) => {
+ openmct.forms.showForm(formStructure).catch(() => {
+ done();
+ });
+
+ const titleElement = document.querySelector('.c-overlay__dialog-title');
+ const title = titleElement.textContent;
+
+ expect(title).toBe(formStructure.title);
+ document.querySelector('.js-cancel-button').click();
+ });
+ });
+});
diff --git a/src/api/forms/components/FormProperties.vue b/src/api/forms/components/FormProperties.vue
index 090a2c8ca..5cf322c18 100644
--- a/src/api/forms/components/FormProperties.vue
+++ b/src/api/forms/components/FormProperties.vue
@@ -21,9 +21,9 @@
*****************************************************************************/
<template>
-<div class="c-form">
+<div class="c-form js-form">
<div class="c-overlay__top-bar c-form__top-bar">
- <div class="c-overlay__dialog-title">{{ model.title }}</div>
+ <div class="c-overlay__dialog-title js-form-title">{{ model.title }}</div>
<div
v-if="hasRequiredFields"
class="c-overlay__dialog-hint hint"
@@ -47,18 +47,14 @@
>
{{ section.name }}
</h2>
- <div
+ <FormRow
v-for="(row, index) in section.rows"
:key="row.id"
- class="u-contents"
- >
- <FormRow
- :css-class="section.cssClass"
- :first="index < 1"
- :row="row"
- @onChange="onChange"
- />
- </div>
+ :css-class="row.cssClass"
+ :first="index < 1"
+ :row="row"
+ @onChange="onChange"
+ />
</div>
</form>
@@ -67,6 +63,7 @@
tabindex="0"
:disabled="isInvalid"
class="c-button c-button--major"
+ aria-label="Save"
@click="onSave"
>
{{ submitLabel }}
@@ -74,7 +71,8 @@
<button
v-if="!hideCancel"
tabindex="0"
- class="c-button"
+ class="c-button js-cancel-button"
+ aria-label="Cancel"
@click="onDismiss"
>
{{ cancelLabel }}
@@ -85,7 +83,7 @@
<script>
import FormRow from "@/api/forms/components/FormRow.vue";
-import uuid from 'uuid';
+import { v4 as uuid } from 'uuid';
export default {
components: {
diff --git a/src/api/forms/components/FormRow.vue b/src/api/forms/components/FormRow.vue
index 8f395e503..aeac6e833 100644
--- a/src/api/forms/components/FormRow.vue
+++ b/src/api/forms/components/FormRow.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
-* Open MCT, Copyright (c) 2014-2021, United States Government
+* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -21,21 +21,28 @@
*****************************************************************************/
<template>
-<div class="form-row c-form__row"
- :class="[{ 'first': first }]"
- @onChange="onChange"
+<div
+ class="form-row c-form__row"
+ :class="[
+ { 'first': first },
+ cssClass
+ ]"
+ @onChange="onChange"
>
- <div class="c-form-row__label"
- :title="row.description"
+ <div
+ class="c-form-row__label"
+ :title="row.description"
>
{{ row.name }}
</div>
- <div class="c-form-row__state-indicator"
- :class="rowClass"
+ <div
+ class="c-form-row__state-indicator"
+ :class="reqClass"
>
</div>
- <div v-if="row.control"
- class="c-form-row__controls"
+ <div
+ v-if="row.control"
+ class="c-form-row__controls"
>
<div ref="rowElement"></div>
</div>
@@ -72,8 +79,8 @@ export default {
};
},
computed: {
- rowClass() {
- let cssClass = this.cssClass;
+ reqClass() {
+ let reqClass = 'req';
if (!this.row.required) {
return;
@@ -83,13 +90,13 @@ export default {
if (this.visited && this.valid !== undefined) {
if (this.valid === true) {
- cssClass = `${cssClass} valid`;
+ reqClass = 'valid';
} else {
- cssClass = `${cssClass} invalid`;
+ reqClass = 'invalid';
}
}
- return cssClass;
+ return reqClass;
}
},
mounted() {
diff --git a/src/api/forms/components/controls/AutoCompleteField.vue b/src/api/forms/components/controls/AutoCompleteField.vue
index 9f495fb9f..bc3910526 100644
--- a/src/api/forms/components/controls/AutoCompleteField.vue
+++ b/src/api/forms/components/controls/AutoCompleteField.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
-* Open MCT, Copyright (c) 2014-2021, United States Government
+* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -19,29 +19,47 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
-
<template>
-<div class="form-control autocomplete">
- <input v-model="field"
- class="autocompleteInput"
- type="text"
- @click="inputClicked()"
- @keydown="keyDown($event)"
+<div
+ ref="autoCompleteForm"
+ class="form-control c-input--autocomplete js-autocomplete"
+>
+ <div
+ class="c-input--autocomplete__wrapper"
>
- <span class="icon-arrow-down"
- @click="arrowClicked()"
- ></span>
- <div class="autocompleteOptions"
- @blur="hideOptions = true"
+ <input
+ ref="autoCompleteInput"
+ v-model="field"
+ class="c-input--autocomplete__input js-autocomplete__input"
+ type="text"
+ :placeholder="placeHolderText"
+ @click="inputClicked()"
+ @keydown="keyDown($event)"
+ >
+ <div
+ class="icon-arrow-down c-icon-button c-input--autocomplete__afford-arrow js-autocomplete__afford-arrow"
+ @click="arrowClicked()"
+ ></div>
+ </div>
+ <div
+ v-if="!hideOptions"
+ class="c-menu c-input--autocomplete__options"
+ aria-label="Autocomplete Options"
+ @blur="hideOptions = true"
>
- <ul v-if="!hideOptions">
- <li v-for="opt in filteredOptions"
+ <ul>
+ <li
+ v-for="opt in filteredOptions"
:key="opt.optionId"
- :class="{'optionPreSelected': optionIndex === opt.optionId}"
+ :class="[
+ {'optionPreSelected': optionIndex === opt.optionId},
+ itemCssClass
+ ]"
+ :style="itemStyle(opt)"
@click="fillInputWithString(opt.name)"
@mouseover="optionMouseover(opt.optionId)"
>
- <span class="optionText">{{ opt.name }}</span>
+ {{ opt.name }}
</li>
</ul>
</div>
@@ -59,35 +77,69 @@ export default {
props: {
model: {
type: Object,
- required: true
+ required: true,
+ default() {
+ return {};
+ }
+ },
+ placeHolderText: {
+ type: String,
+ default() {
+ return "";
+ }
+ },
+ itemCssClass: {
+ type: String,
+ required: false,
+ default() {
+ return "";
+ }
}
},
data() {
return {
hideOptions: true,
+ showFilteredOptions: false,
optionIndex: 0,
field: this.model.value
};
},
computed: {
filteredOptions() {
- const options = this.optionNames || [];
+ const fullOptions = this.options || [];
+ if (this.showFilteredOptions) {
+ const optionsFiltered = fullOptions
+ .filter(option => {
+ if (option.name && this.field) {
+ return option.name.toLowerCase().indexOf(this.field.toLowerCase()) >= 0;
+ }
+
+ return false;
+ }).map((option, index) => {
+ return {
+ optionId: index,
+ name: option.name,
+ color: option.color
+ };
+ });
+
+ return optionsFiltered;
+ }
+
+ const optionsFiltered = fullOptions.map((option, index) => {
+ return {
+ optionId: index,
+ name: option.name,
+ color: option.color
+ };
+ });
- return options
- .filter(option => {
- return option.toLowerCase().indexOf(this.field.toLowerCase()) >= 0;
- }).map((option, index) => {
- return {
- optionId: index,
- name: option
- };
- });
+ return optionsFiltered;
}
},
watch: {
field(newValue, oldValue) {
if (newValue !== oldValue) {
-
const data = {
model: this.model,
value: newValue
@@ -95,21 +147,35 @@ export default {
this.$emit('onChange', data);
}
+ },
+ hideOptions(newValue) {
+ if (!newValue) {
+ // adding a event listener when the hideOpntions is false (dropdown is visible)
+ // handleoutsideclick can collapse the dropdown when clicked outside autocomplete
+ document.body.addEventListener('click', this.handleOutsideClick);
+ } else {
+ //removing event listener when hideOptions become true (dropdown is collapsed)
+ document.body.removeEventListener('click', this.handleOutsideClick);
+ }
}
},
mounted() {
- this.options = this.model.options;
- this.autocompleteInputElement = this.$el.getElementsByClassName('autocompleteInput')[0];
- if (this.options[0].name) {
- // If "options" include name, value pair
- this.optionNames = this.options.map((opt) => {
- return opt.name;
+ this.autocompleteInputAndArrow = this.$refs.autoCompleteForm;
+ this.autocompleteInputElement = this.$refs.autoCompleteInput;
+ if (this.model.options && this.model.options.length && !this.model.options[0].name) {
+ // If options is only an array of string.
+ this.options = this.model.options.map((option) => {
+ return {
+ name: option
+ };
});
} else {
- // If options is only an array of string.
- this.optionNames = this.options;
+ this.options = this.model.options;
}
},
+ destroyed() {
+ document.body.removeEventListener('click', this.handleOutsideClick);
+ },
methods: {
decrementOptionIndex() {
if (this.optionIndex === 0) {
@@ -131,11 +197,12 @@ export default {
this.hideOptions = true;
this.field = string;
},
- showOptions(string) {
+ showOptions() {
this.hideOptions = false;
this.optionIndex = 0;
},
keyDown($event) {
+ this.showFilteredOptions = true;
if (this.filteredOptions) {
let keyCode = $event.keyCode;
switch (keyCode) {
@@ -155,11 +222,28 @@ export default {
},
inputClicked() {
this.autocompleteInputElement.select();
- this.showOptions(this.autocompleteInputElement.value);
+ this.showOptions();
},
arrowClicked() {
+ // if the user clicked the arrow, we want
+ // to show them all the options
+ this.showFilteredOptions = false;
this.autocompleteInputElement.select();
- this.showOptions('');
+
+ if (this.hideOptions) {
+ this.showOptions();
+ } else {
+ this.hideOptions = true;
+ }
+
+ },
+ handleOutsideClick(event) {
+ // if click event is detected outside autocomplete (both input & arrow) while the
+ // dropdown is visible, this will collapse the dropdown.
+ const clickedInsideAutocomplete = this.autocompleteInputAndArrow.contains(event.target);
+ if (!clickedInsideAutocomplete && !this.hideOptions) {
+ this.hideOptions = true;
+ }
},
optionMouseover(optionId) {
this.optionIndex = optionId;
@@ -175,6 +259,12 @@ export default {
});
}
});
+ },
+ itemStyle(option) {
+ if (option.color) {
+
+ return { '--optionIconColor': option.color };
+ }
}
}
};
diff --git a/src/api/forms/components/controls/CheckBoxField.vue b/src/api/forms/components/controls/CheckBoxField.vue
index 6bbd21553..bfff992bd 100644
--- a/src/api/forms/components/controls/CheckBoxField.vue
+++ b/src/api/forms/components/controls/CheckBoxField.vue
@@ -36,7 +36,10 @@
</template>
<script>
+import toggleMixin from '../../toggle-check-box-mixin';
+
export default {
+ mixins: [toggleMixin],
props: {
model: {
type: Object,
@@ -47,16 +50,6 @@ export default {
return {
isChecked: this.model.value
};
- },
- methods: {
- toggleCheckBox() {
- this.isChecked = !this.isChecked;
- const data = {
- model: this.model,
- value: this.isChecked
- };
- this.$emit('onChange', data);
- }
}
};
</script>
diff --git a/src/api/forms/components/controls/ClockDisplayFormatField.vue b/src/api/forms/components/controls/ClockDisplayFormatField.vue
index 9f83e7a66..14785afd6 100644
--- a/src/api/forms/components/controls/ClockDisplayFormatField.vue
+++ b/src/api/forms/components/controls/ClockDisplayFormatField.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
-* Open MCT, Copyright (c) 2014-2021, United States Government
+* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -22,10 +22,11 @@
<template>
<div class="c-form-control--clock-display-format-fields">
- <SelectField v-for="item in items"
- :key="item.key"
- :model="item"
- @onChange="onChange"
+ <SelectField
+ v-for="item in items"
+ :key="item.key"
+ :model="item"
+ @onChange="onChange"
/>
</div>
</template>
diff --git a/src/api/forms/components/controls/Composite.vue b/src/api/forms/components/controls/Composite.vue
index 4e72ed34b..65c98ac9d 100644
--- a/src/api/forms/components/controls/Composite.vue
+++ b/src/api/forms/components/controls/Composite.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
-* Open MCT, Copyright (c) 2014-2021, United States Government
+* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -22,12 +22,13 @@
<template>
<span>
- <CompositeItem v-for="(item, index) in model.items"
- :key="item.name"
- :first="index < 1"
- :value="JSON.stringify(model.value[index])"
- :item="item"
- @onChange="onChange"
+ <CompositeItem
+ v-for="(item, index) in model.items"
+ :key="item.name"
+ :first="index < 1"
+ :value="JSON.stringify(model.value[index])"
+ :item="item"
+ @onChange="onChange"
/>
</span>
</template>
diff --git a/src/api/forms/components/controls/CompositeItem.vue b/src/api/forms/components/controls/CompositeItem.vue
index cb107f9cb..3386c6367 100644
--- a/src/api/forms/components/controls/CompositeItem.vue
+++ b/src/api/forms/components/controls/CompositeItem.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
-* Open MCT, Copyright (c) 2014-2021, United States Government
+* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -22,10 +22,11 @@
<template>
<div :class="compositeCssClass">
- <FormRow :css-class="item.cssClass"
- :first="first"
- :row="row"
- @onChange="onChange"
+ <FormRow
+ :css-class="item.cssClass"
+ :first="first"
+ :row="row"
+ @onChange="onChange"
/>
<span class="composite-control-label">
{{ item.name }}
diff --git a/src/api/forms/components/controls/Datetime.vue b/src/api/forms/components/controls/Datetime.vue
index ed4a0c1d0..5c6bee75d 100644
--- a/src/api/forms/components/controls/Datetime.vue
+++ b/src/api/forms/components/controls/Datetime.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
-* Open MCT, Copyright (c) 2014-2021, United States Government
+* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -27,50 +27,55 @@
<div class="hint time sm">Min</div>
<div class="hint time sm">Sec</div>
<div class="hint timezone">Timezone</div>
- <form ref="dateTimeForm"
- prevent
- class="u-contents"
+ <form
+ ref="dateTimeForm"
+ prevent
+ class="u-contents"
>
<div class="field control date">
- <input v-model="date"
- :pattern="/\d{4}-\d{2}-\d{2}/"
- :placeholder="format"
- type="date"
- name="date"
- @change="onChange"
+ <input
+ v-model="date"
+ :pattern="/\d{4}-\d{2}-\d{2}/"
+ :placeholder="format"
+ type="date"
+ name="date"
+ @change="onChange"
>
</div>
<div class="field control hour sm">
- <input v-model="hour"
- :pattern="/\d+/"
- type="number"
- name="hour"
- maxlength="10"
- min="0"
- max="23"
- @change="onChange"
+ <input
+ v-model="hour"
+ :pattern="/\d+/"
+ type="number"
+ name="hour"
+ maxlength="10"
+ min="0"
+ max="23"
+ @change="onChange"
>
</div>
<div class="field control min sm">
- <input v-model="min"
- :pattern="/\d+/"
- type="number"
- name="min"
- maxlength="2"
- min="0"
- max="59"
- @change="onChange"
+ <input
+ v-model="min"
+ :pattern="/\d+/"
+ type="number"
+ name="min"
+ maxlength="2"
+ min="0"
+ max="59"
+ @change="onChange"
>
</div>
<div class="field control sec sm">
- <input v-model="sec"
- :pattern="/\d+/"
- type="number"
- name="sec"
- maxlength="2"
- min="0"
- max="59"
- @change="onChange"
+ <input
+ v-model="sec"
+ :pattern="/\d+/"
+ type="number"
+ name="sec"
+ maxlength="2"
+ min="0"
+ max="59"
+ @change="onChange"
>
</div>
<div class="field control timezone">
diff --git a/src/api/forms/components/controls/FileInput.vue b/src/api/forms/components/controls/FileInput.vue
index 664720f53..ef9a6a3b8 100644
--- a/src/api/forms/components/controls/FileInput.vue
+++ b/src/api/forms/components/controls/FileInput.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
-* Open MCT, Copyright (c) 2014-2021, United States Government
+* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -22,21 +22,30 @@
<template>
<span class="form-control shell">
- <span class="field control"
- :class="model.cssClass"
+ <span
+ class="field control"
+ :class="model.cssClass"
>
- <input id="fileElem"
- ref="fileInput"
- type="file"
- accept=".json"
- style="display:none"
+ <input
+ id="fileElem"
+ ref="fileInput"
+ type="file"
+ accept=".json"
+ style="display:none"
>
- <button id="fileSelect"
- class="c-button"
- @click="selectFile"
+ <button
+ id="fileSelect"
+ class="c-button"
+ @click="selectFile"
>
{{ name }}
</button>
+ <button
+ v-if="removable"
+ class="c-button icon-trash"
+ title="Remove file"
+ @click="removeFile"
+ ></button>
</span>
</span>
</template>
@@ -60,6 +69,9 @@ export default {
const fileInfo = this.fileInfo || this.model.value;
return fileInfo && fileInfo.name || this.model.text;
+ },
+ removable() {
+ return (this.fileInfo || this.model.value) && this.model.removable;
}
},
mounted() {
@@ -94,6 +106,15 @@ export default {
},
selectFile() {
this.$refs.fileInput.click();
+ },
+ removeFile() {
+ this.model.value = undefined;
+ this.fileInfo = undefined;
+ const data = {
+ model: this.model,
+ value: undefined
+ };
+ this.$emit('onChange', data);
}
}
};
diff --git a/src/api/forms/components/controls/Locator.vue b/src/api/forms/components/controls/Locator.vue
index d02788551..339912cec 100644
--- a/src/api/forms/components/controls/Locator.vue
+++ b/src/api/forms/components/controls/Locator.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
-* Open MCT, Copyright (c) 2014-2021, United States Government
+* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -21,19 +21,19 @@
*****************************************************************************/
<template>
-<SelectorDialogTree :ignore-type-check="true"
- :css-class="`form-locator c-form-control--locator`"
- :parent="model.parent"
- @treeItemSelected="handleItemSelection"
+<mct-tree
+ :is-selector-tree="true"
+ :initial-selection="model.parent"
+ @tree-item-selection="handleItemSelection"
/>
</template>
<script>
-import SelectorDialogTree from '@/ui/components/SelectorDialogTree.vue';
+import MctTree from '@/ui/layout/mct-tree.vue';
export default {
components: {
- SelectorDialogTree
+ MctTree
},
inject: ['openmct'],
props: {
@@ -43,10 +43,10 @@ export default {
}
},
methods: {
- handleItemSelection({ parentObjectPath }) {
+ handleItemSelection(item) {
const data = {
model: this.model,
- value: parentObjectPath
+ value: item.objectPath
};
this.$emit('onChange', data);
diff --git a/src/api/forms/components/controls/NumberField.vue b/src/api/forms/components/controls/NumberField.vue
index 0f2d982d4..ef85cc81a 100644
--- a/src/api/forms/components/controls/NumberField.vue
+++ b/src/api/forms/components/controls/NumberField.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
-* Open MCT, Copyright (c) 2014-2021, United States Government
+* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -22,21 +22,26 @@
<template>
<span class="form-control shell">
- <span class="field control"
- :class="model.cssClass"
+ <span
+ class="field control"
+ :class="model.cssClass"
>
- <input v-model="field"
- type="number"
- :min="model.min"
- :max="model.max"
- :step="model.step"
- @blur="blur()"
+ <input
+ v-model="field"
+ :aria-label="model.name"
+ type="number"
+ :min="model.min"
+ :max="model.max"
+ :step="model.step"
+ @input="updateText()"
>
</span>
</span>
</template>
<script>
+import { throttle } from 'lodash';
+
export default {
props: {
model: {
@@ -49,8 +54,11 @@ export default {
field: this.model.value
};
},
+ mounted() {
+ this.updateText = throttle(this.updateText.bind(this), 200);
+ },
methods: {
- blur() {
+ updateText() {
const data = {
model: this.model,
value: this.field
diff --git a/src/api/forms/components/controls/SelectField.vue b/src/api/forms/components/controls/SelectField.vue
index 8ffbcfca5..aecf0c286 100644
--- a/src/api/forms/components/controls/SelectField.vue
+++ b/src/api/forms/components/controls/SelectField.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
-* Open MCT, Copyright (c) 2014-2021, United States Government
+* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -22,14 +22,16 @@
<template>
<div class="form-control select-field">
- <select v-model="selected"
- required="model.required"
- name="mctControl"
- @change="onChange($event)"
+ <select
+ v-model="selected"
+ required="model.required"
+ name="mctControl"
+ @change="onChange($event)"
>
- <option v-for="option in model.options"
- :key="option.name"
- :value="option.value"
+ <option
+ v-for="option in model.options"
+ :key="option.name"
+ :value="option.value"
>
{{ option.name }}
</option>
diff --git a/src/api/forms/components/controls/TextAreaField.vue b/src/api/forms/components/controls/TextAreaField.vue
index 83e24745a..834940b34 100644
--- a/src/api/forms/components/controls/TextAreaField.vue
+++ b/src/api/forms/components/controls/TextAreaField.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
-* Open MCT, Copyright (c) 2014-2021, United States Government
+* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -22,13 +22,15 @@
<template>
<span class="form-control shell">
- <span class="field control"
- :class="model.cssClass"
+ <span
+ class="field control"
+ :class="model.cssClass"
>
- <textarea v-model="field"
- type="text"
- :size="model.size"
- @blur="blur()"
+ <textarea
+ v-model="field"
+ type="text"
+ :size="model.size"
+ @input="updateText()"
>
</textarea>
</span>
@@ -36,6 +38,8 @@
</template>
<script>
+import { throttle } from 'lodash';
+
export default {
props: {
model: {
@@ -48,8 +52,11 @@ export default {
field: this.model.value
};
},
+ mounted() {
+ this.updateText = throttle(this.updateText.bind(this), 500);
+ },
methods: {
- blur() {
+ updateText() {
const data = {
model: this.model,
value: this.field
diff --git a/src/api/forms/components/controls/TextField.vue b/src/api/forms/components/controls/TextField.vue
index 60eaa3c93..60b6e1752 100644
--- a/src/api/forms/components/controls/TextField.vue
+++ b/src/api/forms/components/controls/TextField.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -22,19 +22,23 @@
<template>
<span class="form-control shell">
- <span class="field control"
- :class="model.cssClass"
+ <span
+ class="field control"
+ :class="model.cssClass"
>
- <input v-model="field"
- type="text"
- :size="model.size"
- @blur="blur()"
+ <input
+ v-model="field"
+ type="text"
+ :size="model.size"
+ @input="updateText()"
>
</span>
</span>
</template>
<script>
+import { throttle } from 'lodash';
+
export default {
props: {
model: {
@@ -47,8 +51,11 @@ export default {
field: this.model.value
};
},
+ mounted() {
+ this.updateText = throttle(this.updateText.bind(this), 500);
+ },
methods: {
- blur() {
+ updateText() {
const data = {
model: this.model,
value: this.field
diff --git a/src/api/forms/components/controls/ToggleSwitchField.vue b/src/api/forms/components/controls/ToggleSwitchField.vue
new file mode 100644
index 000000000..bea9224e3
--- /dev/null
+++ b/src/api/forms/components/controls/ToggleSwitchField.vue
@@ -0,0 +1,62 @@
+/*****************************************************************************
+* Open MCT, Copyright (c) 2014-2022, United States Government
+* as represented by the Administrator of the National Aeronautics and Space
+* Administration. All rights reserved.
+*
+* Open MCT is licensed under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+* http://www.apache.org/licenses/LICENSE-2.0.
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+* License for the specific language governing permissions and limitations
+* under the License.
+*
+* Open MCT includes source code licensed under additional open source
+* licenses. See the Open Source Licenses file (LICENSES.md) included with
+* this source code distribution or the Licensing information page available
+* at runtime from the About dialog for additional information.
+*****************************************************************************/
+
+<template>
+<span class="form-control shell">
+ <span
+ class="field control"
+ :class="model.cssClass"
+ >
+ <ToggleSwitch
+ id="switchId"
+ :checked="isChecked"
+ @change="toggleCheckBox"
+ />
+ </span>
+</span>
+</template>
+
+<script>
+import toggleMixin from '../../toggle-check-box-mixin';
+import ToggleSwitch from '@/ui/components/ToggleSwitch.vue';
+
+import { v4 as uuid } from 'uuid';
+
+export default {
+ components: {
+ ToggleSwitch
+ },
+ mixins: [toggleMixin],
+ props: {
+ model: {
+ type: Object,
+ required: true
+ }
+ },
+ data() {
+ return {
+ switchId: `toggleSwitch-${uuid}`,
+ isChecked: this.model.value
+ };
+ }
+};
+</script>
diff --git a/src/api/forms/toggle-check-box-mixin.js b/src/api/forms/toggle-check-box-mixin.js
new file mode 100644
index 000000000..f15c34403
--- /dev/null
+++ b/src/api/forms/toggle-check-box-mixin.js
@@ -0,0 +1,19 @@
+export default {
+ data() {
+ return {
+ isChecked: false
+ };
+ },
+ methods: {
+ toggleCheckBox(event) {
+ this.isChecked = !this.isChecked;
+
+ const data = {
+ model: this.model,
+ value: this.isChecked
+ };
+
+ this.$emit('onChange', data);
+ }
+ }
+};
diff --git a/src/api/indicators/IndicatorAPI.js b/src/api/indicators/IndicatorAPI.js
index 67779dd2b..98d78112c 100644
--- a/src/api/indicators/IndicatorAPI.js
+++ b/src/api/indicators/IndicatorAPI.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -19,27 +19,27 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
-define([
- './SimpleIndicator',
- 'lodash'
-], function (
- SimpleIndicator,
- _
-) {
- function IndicatorAPI(openmct) {
+
+import EventEmitter from "EventEmitter";
+import SimpleIndicator from "./SimpleIndicator";
+
+class IndicatorAPI extends EventEmitter {
+ constructor(openmct) {
+ super();
+
this.openmct = openmct;
this.indicatorObjects = [];
}
- IndicatorAPI.prototype.getIndicatorObjectsByPriority = function () {
+ getIndicatorObjectsByPriority() {
const sortedIndicators = this.indicatorObjects.sort((a, b) => b.priority - a.priority);
return sortedIndicators;
- };
+ }
- IndicatorAPI.prototype.simpleIndicator = function () {
+ simpleIndicator() {
return new SimpleIndicator(this.openmct);
- };
+ }
/**
* Accepts an indicator object, which is a simple object
@@ -62,14 +62,16 @@ define([
* myIndicator.iconClass("icon-info");
*
*/
- IndicatorAPI.prototype.add = function (indicator) {
+ add(indicator) {
if (!indicator.priority) {
indicator.priority = this.openmct.priority.DEFAULT;
}
this.indicatorObjects.push(indicator);
- };
- return IndicatorAPI;
+ this.emit('addIndicator', indicator);
+ }
+
+}
-});
+export default IndicatorAPI;
diff --git a/src/api/indicators/IndicatorAPISpec.js b/src/api/indicators/IndicatorAPISpec.js
index 42a8a49e3..4a08dbbb4 100644
--- a/src/api/indicators/IndicatorAPISpec.js
+++ b/src/api/indicators/IndicatorAPISpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/api/indicators/SimpleIndicator.js b/src/api/indicators/SimpleIndicator.js
index b610e7d09..31ce745a5 100644
--- a/src/api/indicators/SimpleIndicator.js
+++ b/src/api/indicators/SimpleIndicator.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -20,82 +20,101 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
-define(['zepto', './res/indicator-template.html'],
- function ($, indicatorTemplate) {
- const DEFAULT_ICON_CLASS = 'icon-info';
+import EventEmitter from 'EventEmitter';
+import indicatorTemplate from './res/indicator-template.html';
+import { convertTemplateToHTML } from '@/utils/template/templateHelpers';
- function SimpleIndicator(openmct) {
- this.openmct = openmct;
- this.element = $(indicatorTemplate)[0];
- this.priority = openmct.priority.DEFAULT;
+const DEFAULT_ICON_CLASS = 'icon-info';
- this.textElement = this.element.querySelector('.js-indicator-text');
+class SimpleIndicator extends EventEmitter {
+ constructor(openmct) {
+ super();
- //Set defaults
- this.text('New Indicator');
- this.description('');
- this.iconClass(DEFAULT_ICON_CLASS);
- this.statusClass('');
- }
+ this.openmct = openmct;
+ this.element = convertTemplateToHTML(indicatorTemplate)[0];
+ this.priority = openmct.priority.DEFAULT;
- SimpleIndicator.prototype.text = function (text) {
- if (text !== undefined && text !== this.textValue) {
- this.textValue = text;
- this.textElement.innerText = text;
+ this.textElement = this.element.querySelector('.js-indicator-text');
- if (!text) {
- this.element.classList.add('hidden');
- } else {
- this.element.classList.remove('hidden');
- }
- }
+ //Set defaults
+ this.text('New Indicator');
+ this.description('');
+ this.iconClass(DEFAULT_ICON_CLASS);
+
+ this.click = this.click.bind(this);
+
+ this.element.addEventListener('click', this.click);
+ openmct.once('destroy', () => {
+ this.removeAllListeners();
+ this.element.removeEventListener('click', this.click);
+ });
+ }
- return this.textValue;
- };
+ text(text) {
+ if (text !== undefined && text !== this.textValue) {
+ this.textValue = text;
+ this.textElement.innerText = text;
- SimpleIndicator.prototype.description = function (description) {
- if (description !== undefined && description !== this.descriptionValue) {
- this.descriptionValue = description;
- this.element.title = description;
+ if (!text) {
+ this.element.classList.add('hidden');
+ } else {
+ this.element.classList.remove('hidden');
}
+ }
- return this.descriptionValue;
- };
+ return this.textValue;
+ }
- SimpleIndicator.prototype.iconClass = function (iconClass) {
- if (iconClass !== undefined && iconClass !== this.iconClassValue) {
- // element.classList is precious and throws errors if you try and add
- // or remove empty strings
- if (this.iconClassValue) {
- this.element.classList.remove(this.iconClassValue);
- }
+ description(description) {
+ if (description !== undefined && description !== this.descriptionValue) {
+ this.descriptionValue = description;
+ this.element.title = description;
+ }
+
+ return this.descriptionValue;
+ }
- if (iconClass) {
- this.element.classList.add(iconClass);
- }
+ iconClass(iconClass) {
+ if (iconClass !== undefined && iconClass !== this.iconClassValue) {
+ // element.classList is precious and throws errors if you try and add
+ // or remove empty strings
+ if (this.iconClassValue) {
+ this.element.classList.remove(this.iconClassValue);
+ }
- this.iconClassValue = iconClass;
+ if (iconClass) {
+ this.element.classList.add(iconClass);
}
- return this.iconClassValue;
- };
+ this.iconClassValue = iconClass;
+ }
- SimpleIndicator.prototype.statusClass = function (statusClass) {
- if (statusClass !== undefined && statusClass !== this.statusClassValue) {
- if (this.statusClassValue) {
- this.element.classList.remove(this.statusClassValue);
- }
+ return this.iconClassValue;
+ }
- if (statusClass) {
- this.element.classList.add(statusClass);
- }
+ statusClass(statusClass) {
+ if (arguments.length === 1 && statusClass !== this.statusClassValue) {
+ if (this.statusClassValue) {
+ this.element.classList.remove(this.statusClassValue);
+ }
- this.statusClassValue = statusClass;
+ if (statusClass !== undefined) {
+ this.element.classList.add(statusClass);
}
- return this.statusClassValue;
- };
+ this.statusClassValue = statusClass;
+ }
+
+ return this.statusClassValue;
+ }
- return SimpleIndicator;
+ click(event) {
+ this.emit('click', event);
}
-);
+
+ getElement() {
+ return this.element;
+ }
+}
+
+export default SimpleIndicator;
diff --git a/src/api/menu/MenuAPI.js b/src/api/menu/MenuAPI.js
index 8a53c39d8..5937226d3 100644
--- a/src/api/menu/MenuAPI.js
+++ b/src/api/menu/MenuAPI.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/api/menu/MenuAPISpec.js b/src/api/menu/MenuAPISpec.js
index 354254b8b..00a55f873 100644
--- a/src/api/menu/MenuAPISpec.js
+++ b/src/api/menu/MenuAPISpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -26,29 +26,31 @@ import { createOpenMct, createMouseEvent, resetApplicationState } from '../../ut
describe ('The Menu API', () => {
let openmct;
- let element;
+ let appHolder;
let menuAPI;
let actionsArray;
- let x;
- let y;
let result;
- let onDestroy;
+ let menuElement;
+
+ const x = 8;
+ const y = 16;
+
+ const menuOptions = {
+ onDestroy: () => {
+ console.log('default onDestroy');
+ }
+ };
beforeEach((done) => {
- const appHolder = document.createElement('div');
+ appHolder = document.createElement('div');
appHolder.style.display = 'block';
appHolder.style.width = '1920px';
appHolder.style.height = '1080px';
openmct = createOpenMct();
- element = document.createElement('div');
- element.style.display = 'block';
- element.style.width = '1920px';
- element.style.height = '1080px';
-
openmct.on('start', done);
- openmct.startHeadless(appHolder);
+ openmct.startHeadless();
menuAPI = new MenuAPI(openmct);
actionsArray = [
@@ -56,7 +58,7 @@ describe ('The Menu API', () => {
key: 'test-css-class-1',
name: 'Test Action 1',
cssClass: 'icon-clock',
- description: 'This is a test action',
+ description: 'This is a test action 1',
onItemClicked: () => {
result = 'Test Action 1 Invoked';
}
@@ -65,149 +67,165 @@ describe ('The Menu API', () => {
key: 'test-css-class-2',
name: 'Test Action 2',
cssClass: 'icon-clock',
- description: 'This is a test action',
+ description: 'This is a test action 2',
onItemClicked: () => {
result = 'Test Action 2 Invoked';
}
}
];
- x = 8;
- y = 16;
});
afterEach(() => {
return resetApplicationState(openmct);
});
- describe("showMenu method", () => {
- it("creates an instance of Menu when invoked", () => {
- menuAPI.showMenu(x, y, actionsArray);
-
- expect(menuAPI.menuComponent).toBeInstanceOf(Menu);
+ describe('showMenu method', () => {
+ beforeAll(() => {
+ spyOn(menuOptions, 'onDestroy').and.callThrough();
});
- describe("creates a menu component", () => {
- let menuComponent;
- let vueComponent;
+ it('creates an instance of Menu when invoked', (done) => {
+ menuOptions.onDestroy = done;
- beforeEach(() => {
- onDestroy = jasmine.createSpy('onDestroy');
+ menuAPI.showMenu(x, y, actionsArray, menuOptions);
- const menuOptions = {
- onDestroy
- };
+ expect(menuAPI.menuComponent).toBeInstanceOf(Menu);
+ document.body.click();
+ });
- menuAPI.showMenu(x, y, actionsArray, menuOptions);
- vueComponent = menuAPI.menuComponent.component;
- menuComponent = document.querySelector(".c-menu");
+ describe('creates a menu component', () => {
+ it('with all the actions passed in', (done) => {
+ menuOptions.onDestroy = done;
- spyOn(vueComponent, '$destroy');
- });
+ menuAPI.showMenu(x, y, actionsArray, menuOptions);
+ menuElement = document.querySelector('.c-menu');
+ expect(menuElement).toBeDefined();
- it("renders a menu component in the expected x and y coordinates", () => {
- let boundingClientRect = menuComponent.getBoundingClientRect();
- let left = boundingClientRect.left;
- let top = boundingClientRect.top;
+ const listItems = menuElement.children[0].children;
- expect(left).toEqual(x);
- expect(top).toEqual(y);
+ expect(listItems.length).toEqual(actionsArray.length);
+ document.body.click();
});
- it("with all the actions passed in", () => {
- expect(menuComponent).toBeDefined();
+ it('with click-able menu items, that will invoke the correct callBack', (done) => {
+ menuOptions.onDestroy = done;
- let listItems = menuComponent.children[0].children;
-
- expect(listItems.length).toEqual(actionsArray.length);
- });
+ menuAPI.showMenu(x, y, actionsArray, menuOptions);
- it("with click-able menu items, that will invoke the correct callBacks", () => {
- let listItem1 = menuComponent.children[0].children[0];
+ menuElement = document.querySelector('.c-menu');
+ const listItem1 = menuElement.children[0].children[0];
listItem1.click();
- expect(result).toEqual("Test Action 1 Invoked");
+ expect(result).toEqual('Test Action 1 Invoked');
});
- it("dismisses the menu when action is clicked on", () => {
- let listItem1 = menuComponent.children[0].children[0];
+ it('dismisses the menu when action is clicked on', (done) => {
+ menuOptions.onDestroy = done;
+ menuAPI.showMenu(x, y, actionsArray, menuOptions);
+
+ menuElement = document.querySelector('.c-menu');
+ const listItem1 = menuElement.children[0].children[0];
listItem1.click();
- let menu = document.querySelector('.c-menu');
+ menuElement = document.querySelector('.c-menu');
- expect(menu).toBeNull();
+ expect(menuElement).toBeNull();
});
- it("invokes the destroy method when menu is dismissed", () => {
+ it('invokes the destroy method when menu is dismissed', (done) => {
+ menuOptions.onDestroy = done;
+
+ menuAPI.showMenu(x, y, actionsArray, menuOptions);
+
+ const vueComponent = menuAPI.menuComponent.component;
+ spyOn(vueComponent, '$destroy');
+
document.body.click();
expect(vueComponent.$destroy).toHaveBeenCalled();
});
- it("invokes the onDestroy callback if passed in", () => {
- document.body.click();
+ it('invokes the onDestroy callback if passed in', (done) => {
+ let count = 0;
+ menuOptions.onDestroy = () => {
+ count++;
+ expect(count).toEqual(1);
+ done();
+ };
- expect(onDestroy).toHaveBeenCalled();
+ menuAPI.showMenu(x, y, actionsArray, menuOptions);
+
+ document.body.click();
});
});
});
- describe("superMenu method", () => {
- it("creates a superMenu", () => {
- menuAPI.showSuperMenu(x, y, actionsArray);
+ describe('superMenu method', () => {
+ it('creates a superMenu', (done) => {
+ menuOptions.onDestroy = done;
- const superMenu = document.querySelector('.c-super-menu__menu');
+ menuAPI.showSuperMenu(x, y, actionsArray, menuOptions);
+ menuElement = document.querySelector('.c-super-menu__menu');
- expect(superMenu).not.toBeNull();
+ expect(menuElement).not.toBeNull();
+ document.body.click();
});
- it("Mouse over a superMenu shows correct description", (done) => {
- menuAPI.showSuperMenu(x, y, actionsArray);
+ it('Mouse over a superMenu shows correct description', (done) => {
+ menuOptions.onDestroy = done;
+
+ menuAPI.showSuperMenu(x, y, actionsArray, menuOptions);
+ menuElement = document.querySelector('.c-super-menu__menu');
- const superMenu = document.querySelector('.c-super-menu__menu');
- const superMenuItem = superMenu.querySelector('li');
+ const superMenuItem = menuElement.querySelector('li');
const mouseOverEvent = createMouseEvent('mouseover');
superMenuItem.dispatchEvent(mouseOverEvent);
const itemDescription = document.querySelector('.l-item-description__description');
- setTimeout(() => {
+ menuAPI.menuComponent.component.$nextTick(() => {
+ expect(menuElement).not.toBeNull();
expect(itemDescription.innerText).toEqual(actionsArray[0].description);
- expect(superMenu).not.toBeNull();
- done();
- }, 300);
+
+ document.body.click();
+ });
});
});
- describe("Menu Placements", () => {
- it("default menu position BOTTOM_RIGHT", () => {
- menuAPI.showMenu(x, y, actionsArray);
+ describe('Menu Placements', () => {
+ it('default menu position BOTTOM_RIGHT', (done) => {
+ menuOptions.onDestroy = done;
- const menu = document.querySelector('.c-menu');
+ menuAPI.showMenu(x, y, actionsArray, menuOptions);
+ menuElement = document.querySelector('.c-menu');
- const boundingClientRect = menu.getBoundingClientRect();
+ const boundingClientRect = menuElement.getBoundingClientRect();
const left = boundingClientRect.left;
const top = boundingClientRect.top;
expect(left).toEqual(x);
expect(top).toEqual(y);
+
+ document.body.click();
});
- it("menu position BOTTOM_RIGHT", () => {
- const menuOptions = {
- placement: openmct.menus.menuPlacement.BOTTOM_RIGHT
- };
+ it('menu position BOTTOM_RIGHT', (done) => {
+ menuOptions.onDestroy = done;
+ menuOptions.placement = openmct.menus.menuPlacement.BOTTOM_RIGHT;
menuAPI.showMenu(x, y, actionsArray, menuOptions);
+ menuElement = document.querySelector('.c-menu');
- const menu = document.querySelector('.c-menu');
- const boundingClientRect = menu.getBoundingClientRect();
+ const boundingClientRect = menuElement.getBoundingClientRect();
const left = boundingClientRect.left;
const top = boundingClientRect.top;
expect(left).toEqual(x);
expect(top).toEqual(y);
+
+ document.body.click();
});
});
});
diff --git a/src/api/menu/components/Menu.vue b/src/api/menu/components/Menu.vue
index 50021641d..23a11f19b 100644
--- a/src/api/menu/components/Menu.vue
+++ b/src/api/menu/components/Menu.vue
@@ -1,6 +1,7 @@
<template>
-<div class="c-menu"
- :class="options.menuClass"
+<div
+ class="c-menu"
+ :class="options.menuClass"
>
<ul v-if="options.actions.length && options.actions[0].length">
<template
@@ -11,6 +12,7 @@
:key="action.name"
:class="[action.cssClass, action.isDisabled ? 'disabled' : '']"
:title="action.description"
+ :data-testid="action.testId || false"
@click="action.onItemClicked"
>
{{ action.name }}
@@ -34,8 +36,9 @@
<li
v-for="action in options.actions"
:key="action.name"
- :class="action.cssClass"
+ :class="[action.cssClass, action.isDisabled ? 'disabled' : '']"
:title="action.description"
+ :data-testid="action.testId || false"
@click="action.onItemClicked"
>
{{ action.name }}
diff --git a/src/api/menu/components/SuperMenu.vue b/src/api/menu/components/SuperMenu.vue
index 25f9b51f2..7b66c68b6 100644
--- a/src/api/menu/components/SuperMenu.vue
+++ b/src/api/menu/components/SuperMenu.vue
@@ -1,8 +1,10 @@
<template>
-<div class="c-menu"
- :class="[options.menuClass, 'c-super-menu']"
+<div
+ class="c-menu"
+ :class="[options.menuClass, 'c-super-menu']"
>
- <ul v-if="options.actions.length && options.actions[0].length"
+ <ul
+ v-if="options.actions.length && options.actions[0].length"
class="c-super-menu__menu"
>
<template
@@ -13,6 +15,7 @@
:key="action.name"
:class="[action.cssClass, action.isDisabled ? 'disabled' : '']"
:title="action.description"
+ :data-testid="action.testId || false"
@click="action.onItemClicked"
@mouseover="toggleItemDescription(action)"
@mouseleave="toggleItemDescription()"
@@ -34,7 +37,8 @@
</template>
</ul>
- <ul v-else
+ <ul
+ v-else
class="c-super-menu__menu"
>
<li
@@ -42,6 +46,7 @@
:key="action.name"
:class="action.cssClass"
:title="action.description"
+ :data-testid="action.testId || false"
@click="action.onItemClicked"
@mouseover="toggleItemDescription(action)"
@mouseleave="toggleItemDescription()"
diff --git a/src/api/menu/menu.js b/src/api/menu/menu.js
index 44c6fe6db..123398191 100644
--- a/src/api/menu/menu.js
+++ b/src/api/menu/menu.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/api/notifications/NotificationAPI.js b/src/api/notifications/NotificationAPI.js
index f4496461a..5319e2cb9 100644
--- a/src/api/notifications/NotificationAPI.js
+++ b/src/api/notifications/NotificationAPI.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/api/notifications/NotificationAPISpec.js b/src/api/notifications/NotificationAPISpec.js
index 93c03f204..eb44b613b 100644
--- a/src/api/notifications/NotificationAPISpec.js
+++ b/src/api/notifications/NotificationAPISpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/api/objects/InMemorySearchProvider.js b/src/api/objects/InMemorySearchProvider.js
index df67c03ae..840b31618 100644
--- a/src/api/objects/InMemorySearchProvider.js
+++ b/src/api/objects/InMemorySearchProvider.js
@@ -20,7 +20,7 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
-import uuid from 'uuid';
+import { v4 as uuid } from 'uuid';
class InMemorySearchProvider {
/**
@@ -39,11 +39,10 @@ class InMemorySearchProvider {
* If max results is not specified in query, use this as default.
*/
this.DEFAULT_MAX_RESULTS = 100;
-
this.openmct = openmct;
-
this.indexedIds = {};
this.indexedCompositions = {};
+ this.indexedTags = {};
this.idsToIndex = [];
this.pendingIndex = {};
this.pendingRequests = 0;
@@ -52,11 +51,18 @@ class InMemorySearchProvider {
/**
* If we don't have SharedWorkers available (e.g., iOS)
*/
- this.localIndexedItems = {};
+ this.localIndexedDomainObjects = {};
+ this.localIndexedAnnotationsByDomainObject = {};
+ this.localIndexedAnnotationsByTag = {};
this.pendingQueries = {};
this.onWorkerMessage = this.onWorkerMessage.bind(this);
this.onWorkerMessageError = this.onWorkerMessageError.bind(this);
+ this.localSearchForObjects = this.localSearchForObjects.bind(this);
+ this.localSearchForAnnotations = this.localSearchForAnnotations.bind(this);
+ this.localSearchForTags = this.localSearchForTags.bind(this);
+ this.localSearchForNotebookAnnotations = this.localSearchForNotebookAnnotations.bind(this);
+ this.onAnnotationCreation = this.onAnnotationCreation.bind(this);
this.onerror = this.onWorkerError.bind(this);
this.startIndexing = this.startIndexing.bind(this);
@@ -76,13 +82,39 @@ class InMemorySearchProvider {
startIndexing() {
const rootObject = this.openmct.objects.rootProvider.rootObject;
+
+ this.searchTypes = this.openmct.objects.SEARCH_TYPES;
+
+ this.supportedSearchTypes = [this.searchTypes.OBJECTS, this.searchTypes.ANNOTATIONS, this.searchTypes.NOTEBOOK_ANNOTATIONS, this.searchTypes.TAGS];
+
this.scheduleForIndexing(rootObject.identifier);
+ this.indexAnnotations();
+
if (typeof SharedWorker !== 'undefined') {
this.worker = this.startSharedWorker();
} else {
// we must be on iOS
}
+
+ this.openmct.annotation.on('annotationCreated', this.onAnnotationCreation);
+
+ }
+
+ indexAnnotations() {
+ const theInMemorySearchProvider = this;
+ Object.values(this.openmct.objects.providers).forEach(objectProvider => {
+ if (objectProvider.getAllObjects) {
+ const allObjects = objectProvider.getAllObjects();
+ if (allObjects) {
+ Object.values(allObjects).forEach(domainObject => {
+ if (domainObject.type === 'annotation') {
+ theInMemorySearchProvider.scheduleForIndexing(domainObject.identifier);
+ }
+ });
+ }
+ }
+ });
}
/**
@@ -98,51 +130,60 @@ class InMemorySearchProvider {
return intermediateResponse;
}
- /**
- * Query the search provider for results.
- *
- * @param {String} input the string to search by.
- * @param {Number} maxResults max number of results to return.
- * @returns {Promise} a promise for a modelResults object.
- */
- query(input, maxResults) {
- if (!maxResults) {
- maxResults = this.DEFAULT_MAX_RESULTS;
- }
-
+ search(query, searchType) {
const queryId = uuid();
const pendingQuery = this.getIntermediateResponse();
this.pendingQueries[queryId] = pendingQuery;
+ const searchOptions = {
+ queryId,
+ searchType,
+ query,
+ maxResults: this.DEFAULT_MAX_RESULTS
+ };
if (this.worker) {
- this.dispatchSearch(queryId, input, maxResults);
+ this.#dispatchSearchToWorker(searchOptions);
} else {
- this.localSearch(queryId, input, maxResults);
+ this.#localQueryFallBack(searchOptions);
}
return pendingQuery.promise;
}
+ #localQueryFallBack({queryId, searchType, query, maxResults}) {
+ if (searchType === this.searchTypes.OBJECTS) {
+ return this.localSearchForObjects(queryId, query, maxResults);
+ } else if (searchType === this.searchTypes.ANNOTATIONS) {
+ return this.localSearchForAnnotations(queryId, query, maxResults);
+ } else if (searchType === this.searchTypes.NOTEBOOK_ANNOTATIONS) {
+ return this.localSearchForNotebookAnnotations(queryId, query, maxResults);
+ } else if (searchType === this.searchTypes.TAGS) {
+ return this.localSearchForTags(queryId, query, maxResults);
+ } else {
+ throw new Error(`🤷‍♂️ Unknown search type passed: ${searchType}`);
+ }
+ }
+
+ supportsSearchType(searchType) {
+ return this.supportedSearchTypes.includes(searchType);
+ }
+
/**
- * Handle messages from the worker. Only really knows how to handle search
- * results, which are parsed, transformed into a modelResult object, which
- * is used to resolve the corresponding promise.
+ * Handle messages from the worker.
* @private
*/
async onWorkerMessage(event) {
- if (event.data.request !== 'search') {
- return;
- }
-
const pendingQuery = this.pendingQueries[event.data.queryId];
const modelResults = {
total: event.data.total
};
modelResults.hits = await Promise.all(event.data.results.map(async (hit) => {
- const identifier = this.openmct.objects.parseKeyString(hit.keyString);
- const domainObject = await this.openmct.objects.get(identifier);
+ if (hit && hit.keyString) {
+ const identifier = this.openmct.objects.parseKeyString(hit.keyString);
+ const domainObject = await this.openmct.objects.get(identifier);
- return domainObject;
+ return domainObject;
+ }
}));
pendingQuery.resolve(modelResults);
@@ -183,7 +224,8 @@ class InMemorySearchProvider {
/**
* Schedule an id to be indexed at a later date. If there are less
- * pending requests then allowed, will kick off an indexing request.
+ * pending requests than the maximum allowed, this will kick off an indexing request.
+ * This is done only when indexing first begins and we need to index a lot of objects.
*
* @private
* @param {identifier} id to be indexed.
@@ -216,6 +258,15 @@ class InMemorySearchProvider {
}
}
+ onAnnotationCreation(annotationObject) {
+
+ const objectProvider = this.openmct.objects.getProvider(annotationObject.identifier);
+ if (objectProvider === undefined || objectProvider.search === undefined) {
+ const provider = this;
+ provider.index(annotationObject);
+ }
+ }
+
onNameMutation(domainObject, name) {
const provider = this;
@@ -223,6 +274,13 @@ class InMemorySearchProvider {
provider.index(domainObject);
}
+ onTagMutation(domainObject, newTags) {
+ domainObject.tags = newTags;
+ const provider = this;
+
+ provider.index(domainObject);
+ }
+
onCompositionMutation(domainObject, composition) {
const provider = this;
const indexedComposition = domainObject.composition;
@@ -259,6 +317,13 @@ class InMemorySearchProvider {
'composition',
this.onCompositionMutation.bind(this, domainObject)
);
+ if (domainObject.type === 'annotation') {
+ this.indexedTags[keyString] = this.openmct.objects.observe(
+ domainObject,
+ 'tags',
+ this.onTagMutation.bind(this, domainObject)
+ );
+ }
}
if ((keyString !== 'ROOT')) {
@@ -317,26 +382,83 @@ class InMemorySearchProvider {
* @private
* @returns {String} a unique query Id for the query.
*/
- dispatchSearch(queryId, searchInput, maxResults) {
+ #dispatchSearchToWorker({queryId, searchType, query, maxResults}) {
const message = {
- request: 'search',
- input: searchInput,
+ request: searchType.toString(),
+ input: query,
maxResults,
queryId
};
this.worker.port.postMessage(message);
}
+ localIndexTags(keyString, objectToIndex, model) {
+ // add new tags
+ model.tags.forEach(tagID => {
+ if (!this.localIndexedAnnotationsByTag[tagID]) {
+ this.localIndexedAnnotationsByTag[tagID] = [];
+ }
+
+ const existsInIndex = this.localIndexedAnnotationsByTag[tagID].some(indexedObject => {
+ return indexedObject.keyString === objectToIndex.keyString;
+ });
+
+ if (!existsInIndex) {
+ this.localIndexedAnnotationsByTag[tagID].push(objectToIndex);
+ }
+
+ });
+ const tagsToRemoveFromIndex = Object.keys(this.localIndexedAnnotationsByTag).filter(indexedTag => {
+ return !(model.tags.includes(indexedTag));
+ });
+ tagsToRemoveFromIndex.forEach(tagToRemoveFromIndex => {
+ this.localIndexedAnnotationsByTag[tagToRemoveFromIndex] = this.localIndexedAnnotationsByTag[tagToRemoveFromIndex].filter(indexedAnnotation => {
+ const shouldKeep = indexedAnnotation.keyString !== keyString;
+
+ return shouldKeep;
+ });
+ });
+ }
+
+ localIndexAnnotation(objectToIndex, model) {
+ Object.keys(model.targets).forEach(targetID => {
+ if (!this.localIndexedAnnotationsByDomainObject[targetID]) {
+ this.localIndexedAnnotationsByDomainObject[targetID] = [];
+ }
+
+ objectToIndex.targets = model.targets;
+ objectToIndex.tags = model.tags;
+ const existsInIndex = this.localIndexedAnnotationsByDomainObject[targetID].some(indexedObject => {
+ return indexedObject.keyString === objectToIndex.keyString;
+ });
+
+ if (!existsInIndex) {
+ this.localIndexedAnnotationsByDomainObject[targetID].push(objectToIndex);
+ }
+ });
+ }
+
/**
* A local version of the same SharedWorker function
* if we don't have SharedWorkers available (e.g., iOS)
*/
localIndexItem(keyString, model) {
- this.localIndexedItems[keyString] = {
+ const objectToIndex = {
type: model.type,
name: model.name,
keyString
};
+ if (model && (model.type === 'annotation')) {
+ if (model.targets) {
+ this.localIndexAnnotation(objectToIndex, model);
+ }
+
+ if (model.tags) {
+ this.localIndexTags(keyString, objectToIndex, model);
+ }
+ } else {
+ this.localIndexedDomainObjects[keyString] = objectToIndex;
+ }
}
/**
@@ -346,21 +468,122 @@ class InMemorySearchProvider {
* Gets search results from the indexedItems based on provided search
* input. Returns matching results from indexedItems
*/
- localSearch(queryId, searchInput, maxResults) {
+ localSearchForObjects(queryId, searchInput, maxResults) {
// This results dictionary will have domain object ID keys which
// point to the value the domain object's score.
- let results;
+ let results = [];
const input = searchInput.trim().toLowerCase();
const message = {
- request: 'search',
- results: {},
+ request: 'searchForObjects',
+ results: [],
total: 0,
queryId
};
- results = Object.values(this.localIndexedItems).filter((indexedItem) => {
+ results = Object.values(this.localIndexedDomainObjects).filter((indexedItem) => {
return indexedItem.name.toLowerCase().includes(input);
- });
+ }) || [];
+
+ message.total = results.length;
+ message.results = results
+ .slice(0, maxResults);
+ const eventToReturn = {
+ data: message
+ };
+ this.onWorkerMessage(eventToReturn);
+ }
+
+ /**
+ * A local version of the same SharedWorker function
+ * if we don't have SharedWorkers available (e.g., iOS)
+ */
+ localSearchForAnnotations(queryId, searchInput, maxResults) {
+ // This results dictionary will have domain object ID keys which
+ // point to the value the domain object's score.
+ let results = [];
+ const message = {
+ request: 'searchForAnnotations',
+ results: [],
+ total: 0,
+ queryId
+ };
+
+ results = this.localIndexedAnnotationsByDomainObject[searchInput] || [];
+
+ message.total = results.length;
+ message.results = results
+ .slice(0, maxResults);
+ const eventToReturn = {
+ data: message
+ };
+ this.onWorkerMessage(eventToReturn);
+ }
+
+ /**
+ * A local version of the same SharedWorker function
+ * if we don't have SharedWorkers available (e.g., iOS)
+ */
+ localSearchForTags(queryId, matchingTagKeys, maxResults) {
+ let results = [];
+ const message = {
+ request: 'searchForTags',
+ results: [],
+ total: 0,
+ queryId
+ };
+
+ if (matchingTagKeys) {
+ matchingTagKeys.forEach(matchingTag => {
+ const matchingAnnotations = this.localIndexedAnnotationsByTag[matchingTag];
+ if (matchingAnnotations) {
+ matchingAnnotations.forEach(matchingAnnotation => {
+ const existsInResults = results.some(indexedObject => {
+ return matchingAnnotation.keyString === indexedObject.keyString;
+ });
+ if (!existsInResults) {
+ results.push(matchingAnnotation);
+ }
+ });
+ }
+ });
+ }
+
+ message.total = results.length;
+ message.results = results
+ .slice(0, maxResults);
+ const eventToReturn = {
+ data: message
+ };
+ this.onWorkerMessage(eventToReturn);
+ }
+
+ /**
+ * A local version of the same SharedWorker function
+ * if we don't have SharedWorkers available (e.g., iOS)
+ */
+ localSearchForNotebookAnnotations(queryId, {entryId, targetKeyString}, maxResults) {
+ // This results dictionary will have domain object ID keys which
+ // point to the value the domain object's score.
+ let results = [];
+ const message = {
+ request: 'searchForNotebookAnnotations',
+ results: [],
+ total: 0,
+ queryId
+ };
+
+ const matchingAnnotations = this.localIndexedAnnotationsByDomainObject[targetKeyString];
+ if (matchingAnnotations) {
+ results = matchingAnnotations.filter(matchingAnnotation => {
+ if (!matchingAnnotation.targets) {
+ return false;
+ }
+
+ const target = matchingAnnotation.targets[targetKeyString];
+
+ return (target && target.entryId && (target.entryId === entryId));
+ });
+ }
message.total = results.length;
message.results = results
diff --git a/src/api/objects/InMemorySearchWorker.js b/src/api/objects/InMemorySearchWorker.js
index 3974f2da1..a2bb53a02 100644
--- a/src/api/objects/InMemorySearchWorker.js
+++ b/src/api/objects/InMemorySearchWorker.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -26,18 +26,27 @@
(function () {
// An object composed of domain object IDs and models
// {id: domainObject's ID, name: domainObject's name}
- const indexedItems = {};
+ const indexedDomainObjects = {};
+ const indexedAnnotationsByDomainObject = {};
+ const indexedAnnotationsByTag = {};
self.onconnect = function (e) {
const port = e.ports[0];
port.onmessage = function (event) {
- if (event.data.request === 'index') {
- console.log('onmessage index: ', event.data);
+ const requestType = event.data.request;
+ if (requestType === 'index') {
indexItem(event.data.keyString, event.data.model);
- } else if (event.data.request === 'search') {
- console.log('onmessage search: ', event.data);
- port.postMessage(search(event.data));
+ } else if (requestType === 'OBJECTS') {
+ port.postMessage(searchForObjects(event.data));
+ } else if (requestType === 'ANNOTATIONS') {
+ port.postMessage(searchForAnnotations(event.data));
+ } else if (requestType === 'TAGS') {
+ port.postMessage(searchForTags(event.data));
+ } else if (requestType === 'NOTEBOOK_ANNOTATIONS') {
+ port.postMessage(searchForNotebookAnnotations(event.data));
+ } else {
+ throw new Error(`Unknown request ${event.data.request}`);
}
};
@@ -50,12 +59,70 @@
console.error('Error on feed', error);
};
+ function indexAnnotation(objectToIndex, model) {
+ Object.keys(model.targets).forEach(targetID => {
+ if (!indexedAnnotationsByDomainObject[targetID]) {
+ indexedAnnotationsByDomainObject[targetID] = [];
+ }
+
+ objectToIndex.targets = model.targets;
+ objectToIndex.tags = model.tags;
+ const existsInIndex = indexedAnnotationsByDomainObject[targetID].some(indexedObject => {
+ return indexedObject.keyString === objectToIndex.keyString;
+ });
+
+ if (!existsInIndex) {
+ indexedAnnotationsByDomainObject[targetID].push(objectToIndex);
+ }
+ });
+ }
+
+ function indexTags(keyString, objectToIndex, model) {
+ // add new tags
+ model.tags.forEach(tagID => {
+ if (!indexedAnnotationsByTag[tagID]) {
+ indexedAnnotationsByTag[tagID] = [];
+ }
+
+ const existsInIndex = indexedAnnotationsByTag[tagID].some(indexedObject => {
+ return indexedObject.keyString === objectToIndex.keyString;
+ });
+
+ if (!existsInIndex) {
+ indexedAnnotationsByTag[tagID].push(objectToIndex);
+ }
+
+ });
+ // remove old tags
+ const tagsToRemoveFromIndex = Object.keys(indexedAnnotationsByTag).filter(indexedTag => {
+ return !(model.tags.includes(indexedTag));
+ });
+ tagsToRemoveFromIndex.forEach(tagToRemoveFromIndex => {
+ indexedAnnotationsByTag[tagToRemoveFromIndex] = indexedAnnotationsByTag[tagToRemoveFromIndex].filter(indexedAnnotation => {
+ const shouldKeep = indexedAnnotation.keyString !== keyString;
+
+ return shouldKeep;
+ });
+ });
+ }
+
function indexItem(keyString, model) {
- indexedItems[keyString] = {
+ const objectToIndex = {
type: model.type,
name: model.name,
keyString
};
+ if (model && (model.type === 'annotation')) {
+ if (model.targets) {
+ indexAnnotation(objectToIndex, model);
+ }
+
+ if (model.tags) {
+ indexTags(keyString, objectToIndex, model);
+ }
+ } else {
+ indexedDomainObjects[keyString] = objectToIndex;
+ }
}
/**
@@ -67,23 +134,98 @@
* * maxResults: The maximum number of search results desired
* * queryId: an id identifying this query, will be returned.
*/
- function search(data) {
- // This results dictionary will have domain object ID keys which
- // point to the value the domain object's score.
- let results;
+ function searchForObjects(data) {
+ let results = [];
const input = data.input.trim().toLowerCase();
const message = {
- request: 'search',
+ request: 'searchForObjects',
+ results: [],
+ total: 0,
+ queryId: data.queryId
+ };
+
+ results = Object.values(indexedDomainObjects).filter((indexedItem) => {
+ return indexedItem.name.toLowerCase().includes(input);
+ }) || [];
+
+ message.total = results.length;
+ message.results = results
+ .slice(0, data.maxResults);
+
+ return message;
+ }
+
+ function searchForAnnotations(data) {
+ let results = [];
+ const message = {
+ request: 'searchForAnnotations',
+ results: [],
+ total: 0,
+ queryId: data.queryId
+ };
+
+ results = indexedAnnotationsByDomainObject[data.input] || [];
+
+ message.total = results.length;
+ message.results = results
+ .slice(0, data.maxResults);
+
+ return message;
+ }
+
+ function searchForTags(data) {
+ let results = [];
+ const message = {
+ request: 'searchForTags',
+ results: [],
+ total: 0,
+ queryId: data.queryId
+ };
+
+ if (data.input) {
+ data.input.forEach(matchingTag => {
+ const matchingAnnotations = indexedAnnotationsByTag[matchingTag];
+ if (matchingAnnotations) {
+ matchingAnnotations.forEach(matchingAnnotation => {
+ const existsInResults = results.some(indexedObject => {
+ return matchingAnnotation.keyString === indexedObject.keyString;
+ });
+ if (!existsInResults) {
+ results.push(matchingAnnotation);
+ }
+ });
+ }
+ });
+ }
+
+ message.total = results.length;
+ message.results = results
+ .slice(0, data.maxResults);
+
+ return message;
+ }
+
+ function searchForNotebookAnnotations(data) {
+ let results = [];
+ const message = {
+ request: 'searchForNotebookAnnotations',
results: {},
total: 0,
queryId: data.queryId
};
- console.log('indexed on search: ', indexedItems);
+ const matchingAnnotations = indexedAnnotationsByDomainObject[data.input.targetKeyString];
+ if (matchingAnnotations) {
+ results = matchingAnnotations.filter(matchingAnnotation => {
+ if (!matchingAnnotation.targets) {
+ return false;
+ }
- results = Object.values(indexedItems).filter((indexedItem) => {
- return indexedItem.name.toLowerCase().includes(input);
- });
+ const target = matchingAnnotation.targets[data.input.targetKeyString];
+
+ return (target && target.entryId && (target.entryId === data.input.entryId));
+ });
+ }
message.total = results.length;
message.results = results
diff --git a/src/api/objects/InterceptorRegistry.js b/src/api/objects/InterceptorRegistry.js
index 448082fd2..80f83cc71 100644
--- a/src/api/objects/InterceptorRegistry.js
+++ b/src/api/objects/InterceptorRegistry.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/api/objects/MutableDomainObject.js b/src/api/objects/MutableDomainObject.js
index 5ed866b7d..046f8b069 100644
--- a/src/api/objects/MutableDomainObject.js
+++ b/src/api/objects/MutableDomainObject.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/api/objects/ObjectAPI.js b/src/api/objects/ObjectAPI.js
index 56fcde50e..a0aae34ce 100644
--- a/src/api/objects/ObjectAPI.js
+++ b/src/api/objects/ObjectAPI.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -31,626 +31,618 @@ import ConflictError from './ConflictError';
import InMemorySearchProvider from './InMemorySearchProvider';
/**
- * Utilities for loading, saving, and manipulating domain objects.
- * @interface ObjectAPI
- * @memberof module:openmct
- */
-
-function ObjectAPI(typeRegistry, openmct) {
- this.openmct = openmct;
- this.typeRegistry = typeRegistry;
- this.eventEmitter = new EventEmitter();
- this.providers = {};
- this.rootRegistry = new RootRegistry();
- this.inMemorySearchProvider = new InMemorySearchProvider(openmct);
-
- this.rootProvider = new RootObjectProvider(this.rootRegistry);
- this.cache = {};
- this.interceptorRegistry = new InterceptorRegistry();
-
- this.SYNCHRONIZED_OBJECT_TYPES = ['notebook', 'plan'];
-
- this.errors = {
- Conflict: ConflictError
- };
-}
-
-/**
- * Set fallback provider, this is an internal API for legacy reasons.
- * @private
- */
-ObjectAPI.prototype.supersecretSetFallbackProvider = function (p) {
- this.fallbackProvider = p;
-};
-
-/**
- * Retrieve the provider for a given identifier.
- * @private
- */
-ObjectAPI.prototype.getProvider = function (identifier) {
-
- if (identifier.key === 'ROOT') {
- return this.rootProvider;
- }
-
- return this.providers[identifier.namespace] || this.fallbackProvider;
-};
-
-/**
- * Get an active transaction instance
- * @returns {Transaction} a transaction object
- */
-ObjectAPI.prototype.getActiveTransaction = function () {
- return this.transaction;
-};
-
-/**
- * Get the root-level object.
- * @returns {Promise.<DomainObject>} a promise for the root object
- */
-ObjectAPI.prototype.getRoot = function () {
- return this.rootProvider.get();
-};
-
-/**
- * Register a new object provider for a particular namespace.
+ * Uniquely identifies a domain object.
*
- * @param {string} namespace the namespace for which to provide objects
- * @param {module:openmct.ObjectProvider} provider the provider which
- * will handle loading domain objects from this namespace
- * @memberof {module:openmct.ObjectAPI#}
- * @name addProvider
+ * @typedef Identifier
+ * @memberof module:openmct.ObjectAPI~
+ * @property {string} namespace the namespace to/from which this domain
+ * object should be loaded/stored.
+ * @property {string} key a unique identifier for the domain object
+ * within that namespace
*/
-ObjectAPI.prototype.addProvider = function (namespace, provider) {
- this.providers[namespace] = provider;
-};
/**
- * Provides the ability to read, write, and delete domain objects.
+ * A domain object is an entity of relevance to a user's workflow, that
+ * should appear as a distinct and meaningful object within the user
+ * interface. Examples of domain objects are folders, telemetry sensors,
+ * and so forth.
*
- * When registering a new object provider, all methods on this interface
- * are optional.
+ * A few common properties are defined for domain objects. Beyond these,
+ * individual types of domain objects may add more as they see fit.
*
- * @interface ObjectProvider
+ * @typedef DomainObject
+ * @property {module:openmct.ObjectAPI~Identifier} identifier a key/namespace pair which
+ * uniquely identifies this domain object
+ * @property {string} type the type of domain object
+ * @property {string} name the human-readable name for this domain object
+ * @property {string} [creator] the user name of the creator of this domain
+ * object
+ * @property {number} [modified] the time, in milliseconds since the UNIX
+ * epoch, at which this domain object was last modified
+ * @property {module:openmct.ObjectAPI~Identifier[]} [composition] if
+ * present, this will be used by the default composition provider
+ * to load domain objects
* @memberof module:openmct
*/
-
/**
- * Create the given domain object in the corresponding persistence store
- *
- * @method create
- * @memberof module:openmct.ObjectProvider#
- * @param {module:openmct.DomainObject} domainObject the domain object to
- * create
- * @returns {Promise} a promise which will resolve when the domain object
- * has been created, or be rejected if it cannot be saved
+ * Utilities for loading, saving, and manipulating domain objects.
+ * @interface ObjectAPI
+ * @memberof module:openmct
*/
+export default class ObjectAPI {
+ constructor(typeRegistry, openmct) {
+ this.openmct = openmct;
+ this.typeRegistry = typeRegistry;
+ this.SEARCH_TYPES = Object.freeze({
+ OBJECTS: 'OBJECTS',
+ ANNOTATIONS: 'ANNOTATIONS',
+ NOTEBOOK_ANNOTATIONS: 'NOTEBOOK_ANNOTATIONS',
+ TAGS: 'TAGS'
+ });
+ this.eventEmitter = new EventEmitter();
+ this.providers = {};
+ this.rootRegistry = new RootRegistry(openmct);
+ this.inMemorySearchProvider = new InMemorySearchProvider(openmct);
-/**
- * Update this domain object in its persistence store
- *
- * @method update
- * @memberof module:openmct.ObjectProvider#
- * @param {module:openmct.DomainObject} domainObject the domain object to
- * update
- * @returns {Promise} a promise which will resolve when the domain object
- * has been updated, or be rejected if it cannot be saved
- */
+ this.rootProvider = new RootObjectProvider(this.rootRegistry);
+ this.cache = {};
+ this.interceptorRegistry = new InterceptorRegistry();
-/**
- * Delete this domain object.
- *
- * @method delete
- * @memberof module:openmct.ObjectProvider#
- * @param {module:openmct.DomainObject} domainObject the domain object to
- * delete
- * @returns {Promise} a promise which will resolve when the domain object
- * has been deleted, or be rejected if it cannot be deleted
- */
+ this.SYNCHRONIZED_OBJECT_TYPES = ['notebook', 'plan'];
-/**
- * Get a domain object.
- *
- * @method get
- * @memberof module:openmct.ObjectProvider#
- * @param {string} key the key for the domain object to load
- * @param {AbortController.signal} abortSignal (optional) signal to abort fetch requests
- * @returns {Promise} a promise which will resolve when the domain object
- * has been saved, or be rejected if it cannot be saved
- */
+ this.errors = {
+ Conflict: ConflictError
+ };
+ }
-ObjectAPI.prototype.get = function (identifier, abortSignal) {
- let keystring = this.makeKeyString(identifier);
+ /**
+ * Retrieve the provider for a given identifier.
+ */
+ getProvider(identifier) {
+ if (identifier.key === 'ROOT') {
+ return this.rootProvider;
+ }
- if (this.cache[keystring] !== undefined) {
- return this.cache[keystring];
+ return this.providers[identifier.namespace] || this.fallbackProvider;
}
- identifier = utils.parseKeyString(identifier);
- let dirtyObject;
- if (this.isTransactionActive()) {
- dirtyObject = this.transaction.getDirtyObject(identifier);
+ /**
+ * Get an active transaction instance
+ * @returns {Transaction} a transaction object
+ */
+ getActiveTransaction() {
+ return this.transaction;
}
- if (dirtyObject) {
- return Promise.resolve(dirtyObject);
+ /**
+ * Get the root-level object.
+ * @returns {Promise.<DomainObject>} a promise for the root object
+ */
+ getRoot() {
+ return this.rootProvider.get();
}
- const provider = this.getProvider(identifier);
-
- if (!provider) {
- throw new Error('No Provider Matched');
+ /**
+ * Register a new object provider for a particular namespace.
+ *
+ * @param {string} namespace the namespace for which to provide objects
+ * @param {module:openmct.ObjectProvider} provider the provider which
+ * will handle loading domain objects from this namespace
+ * @memberof {module:openmct.ObjectAPI#}
+ * @name addProvider
+ */
+ addProvider(namespace, provider) {
+ this.providers[namespace] = provider;
}
- if (!provider.get) {
- throw new Error('Provider does not support get!');
- }
+ /**
+ * Provides the ability to read, write, and delete domain objects.
+ *
+ * When registering a new object provider, all methods on this interface
+ * are optional.
+ *
+ * @interface ObjectProvider
+ * @memberof module:openmct
+ */
+
+ /**
+ * Create the given domain object in the corresponding persistence store
+ *
+ * @method create
+ * @memberof module:openmct.ObjectProvider#
+ * @param {module:openmct.DomainObject} domainObject the domain object to
+ * create
+ * @returns {Promise} a promise which will resolve when the domain object
+ * has been created, or be rejected if it cannot be saved
+ */
+
+ /**
+ * Update this domain object in its persistence store
+ *
+ * @method update
+ * @memberof module:openmct.ObjectProvider#
+ * @param {module:openmct.DomainObject} domainObject the domain object to
+ * update
+ * @returns {Promise} a promise which will resolve when the domain object
+ * has been updated, or be rejected if it cannot be saved
+ */
+
+ /**
+ * Delete this domain object.
+ *
+ * @method delete
+ * @memberof module:openmct.ObjectProvider#
+ * @param {module:openmct.DomainObject} domainObject the domain object to
+ * delete
+ * @returns {Promise} a promise which will resolve when the domain object
+ * has been deleted, or be rejected if it cannot be deleted
+ */
+
+ /**
+ * Get a domain object.
+ *
+ * @method get
+ * @memberof module:openmct.ObjectProvider#
+ * @param {string} key the key for the domain object to load
+ * @param {AbortController.signal} abortSignal (optional) signal to abort fetch requests
+ * @returns {Promise} a promise which will resolve when the domain object
+ * has been saved, or be rejected if it cannot be saved
+ */
+
+ get(identifier, abortSignal) {
+ let keystring = this.makeKeyString(identifier);
+
+ if (this.cache[keystring] !== undefined) {
+ return this.cache[keystring];
+ }
- let objectPromise = provider.get(identifier, abortSignal).then(result => {
- delete this.cache[keystring];
+ identifier = utils.parseKeyString(identifier);
+ let dirtyObject;
+ if (this.isTransactionActive()) {
+ dirtyObject = this.transaction.getDirtyObject(identifier);
+ }
- result = this.applyGetInterceptors(identifier, result);
- if (result.isMutable) {
- result.$refresh(result);
- } else {
- let mutableDomainObject = this._toMutable(result);
- mutableDomainObject.$refresh(result);
+ if (dirtyObject) {
+ return Promise.resolve(dirtyObject);
}
- return result;
- }).catch((result) => {
- console.warn(`Failed to retrieve ${keystring}:`, result);
+ const provider = this.getProvider(identifier);
- delete this.cache[keystring];
+ if (!provider) {
+ throw new Error('No Provider Matched');
+ }
- result = this.applyGetInterceptors(identifier);
+ if (!provider.get) {
+ throw new Error('Provider does not support get!');
+ }
- return result;
- });
+ let objectPromise = provider.get(identifier, abortSignal).then(result => {
+ delete this.cache[keystring];
- this.cache[keystring] = objectPromise;
+ result = this.applyGetInterceptors(identifier, result);
+ if (result.isMutable) {
+ result.$refresh(result);
+ } else {
+ let mutableDomainObject = this._toMutable(result);
+ mutableDomainObject.$refresh(result);
+ }
- return objectPromise;
-};
+ return result;
+ }).catch((result) => {
+ console.warn(`Failed to retrieve ${keystring}:`, result);
-/**
- * Search for domain objects.
- *
- * Object providersSearches and combines results of each object provider search.
- * Objects without search provided will have been indexed
- * and will be searched using the fallback in-memory search.
- * Search results are asynchronous and resolve in parallel.
- *
- * @method search
- * @memberof module:openmct.ObjectAPI#
- * @param {string} query the term to search for
- * @param {AbortController.signal} abortSignal (optional) signal to cancel downstream fetch requests
- * @returns {Array.<Promise.<module:openmct.DomainObject>>}
- * an array of promises returned from each object provider's search function
- * each resolving to domain objects matching provided search query and options.
- */
-ObjectAPI.prototype.search = function (query, abortSignal) {
- const searchPromises = Object.values(this.providers)
- .filter(provider => provider.search !== undefined)
- .map(provider => provider.search(query, abortSignal));
- // abortSignal doesn't seem to be used in generic search?
- searchPromises.push(this.inMemorySearchProvider.query(query, null)
- .then(results => results.hits
- .map(hit => {
- return hit;
- })));
-
- return searchPromises;
-};
+ delete this.cache[keystring];
-/**
- * Will fetch object for the given identifier, returning a version of the object that will automatically keep
- * itself updated as it is mutated. Before using this function, you should ask yourself whether you really need it.
- * The platform will provide mutable objects to views automatically if the underlying object can be mutated. The
- * platform will manage the lifecycle of any mutable objects that it provides. If you use `getMutable` you are
- * committing to managing that lifecycle yourself. `.destroy` should be called when the object is no longer needed.
- *
- * @memberof {module:openmct.ObjectAPI#}
- * @returns {Promise.<MutableDomainObject>} a promise that will resolve with a MutableDomainObject if
- * the object can be mutated.
- */
-ObjectAPI.prototype.getMutable = function (identifier) {
- if (!this.supportsMutation(identifier)) {
- throw new Error(`Object "${this.makeKeyString(identifier)}" does not support mutation.`);
- }
+ if (!result) {
+ //no result means resource either doesn't exist or is missing
+ //otherwise it's an error, and we shouldn't apply interceptors
+ result = this.applyGetInterceptors(identifier);
+ }
- return this.get(identifier).then((object) => {
- return this._toMutable(object);
- });
-};
+ return result;
+ });
-/**
- * This function is for cleaning up a mutable domain object when you're done with it.
- * You only need to use this if you retrieved the object using `getMutable()`. If the object was provided by the
- * platform (eg. passed into a `view()` function) then the platform is responsible for its lifecycle.
- * @param {MutableDomainObject} domainObject
- */
-ObjectAPI.prototype.destroyMutable = function (domainObject) {
- if (domainObject.isMutable) {
- return domainObject.$destroy();
- } else {
- throw new Error("Attempted to destroy non-mutable domain object");
+ this.cache[keystring] = objectPromise;
+
+ return objectPromise;
}
-};
-ObjectAPI.prototype.delete = function () {
- throw new Error('Delete not implemented');
-};
+ /**
+ * Search for domain objects.
+ *
+ * Object providersSearches and combines results of each object provider search.
+ * Objects without search provided will have been indexed
+ * and will be searched using the fallback in-memory search.
+ * Search results are asynchronous and resolve in parallel.
+ *
+ * @method search
+ * @memberof module:openmct.ObjectAPI#
+ * @param {string} query the term to search for
+ * @param {AbortController.signal} abortSignal (optional) signal to cancel downstream fetch requests
+ * @param {string} searchType the type of search as defined by SEARCH_TYPES
+ * @returns {Array.<Promise.<module:openmct.DomainObject>>}
+ * an array of promises returned from each object provider's search function
+ * each resolving to domain objects matching provided search query and options.
+ */
+ search(query, abortSignal, searchType = this.SEARCH_TYPES.OBJECTS) {
+ if (!Object.keys(this.SEARCH_TYPES).includes(searchType.toUpperCase())) {
+ throw new Error(`Unknown search type: ${searchType}`);
+ }
-ObjectAPI.prototype.isPersistable = function (idOrKeyString) {
- let identifier = utils.parseKeyString(idOrKeyString);
- let provider = this.getProvider(identifier);
+ const searchPromises = Object.values(this.providers)
+ .filter(provider => {
+ return ((provider.supportsSearchType !== undefined) && provider.supportsSearchType(searchType));
+ })
+ .map(provider => provider.search(query, abortSignal, searchType));
+ if (!this.inMemorySearchProvider.supportsSearchType(searchType)) {
+ throw new Error(`${searchType} not implemented in inMemorySearchProvider`);
+ }
- return provider !== undefined
- && provider.create !== undefined
- && provider.update !== undefined;
-};
+ searchPromises.push(this.inMemorySearchProvider.search(query, searchType)
+ .then(results => results.hits
+ .map(hit => {
+ return hit;
+ })));
-ObjectAPI.prototype.isMissing = function (domainObject) {
- let identifier = utils.makeKeyString(domainObject.identifier);
- let missingName = 'Missing: ' + identifier;
+ return searchPromises;
+ }
- return domainObject.name === missingName;
-};
+ /**
+ * Will fetch object for the given identifier, returning a version of the object that will automatically keep
+ * itself updated as it is mutated. Before using this function, you should ask yourself whether you really need it.
+ * The platform will provide mutable objects to views automatically if the underlying object can be mutated. The
+ * platform will manage the lifecycle of any mutable objects that it provides. If you use `getMutable` you are
+ * committing to managing that lifecycle yourself. `.destroy` should be called when the object is no longer needed.
+ *
+ * @memberof {module:openmct.ObjectAPI#}
+ * @returns {Promise.<MutableDomainObject>} a promise that will resolve with a MutableDomainObject if
+ * the object can be mutated.
+ */
+ getMutable(identifier) {
+ if (!this.supportsMutation(identifier)) {
+ throw new Error(`Object "${this.makeKeyString(identifier)}" does not support mutation.`);
+ }
-/**
- * Save this domain object in its current state. EXPERIMENTAL
- *
- * @private
- * @memberof module:openmct.ObjectAPI#
- * @param {module:openmct.DomainObject} domainObject the domain object to
- * save
- * @returns {Promise} a promise which will resolve when the domain object
- * has been saved, or be rejected if it cannot be saved
- */
-ObjectAPI.prototype.save = function (domainObject) {
- let provider = this.getProvider(domainObject.identifier);
- let savedResolve;
- let savedReject;
- let result;
-
- if (!this.isPersistable(domainObject.identifier)) {
- result = Promise.reject('Object provider does not support saving');
- } else if (hasAlreadyBeenPersisted(domainObject)) {
- result = Promise.resolve(true);
- } else {
- const persistedTime = Date.now();
- if (domainObject.persisted === undefined) {
- result = new Promise((resolve, reject) => {
- savedResolve = resolve;
- savedReject = reject;
- });
- domainObject.persisted = persistedTime;
- const newObjectPromise = provider.create(domainObject);
- if (newObjectPromise) {
- newObjectPromise.then(response => {
- this.mutate(domainObject, 'persisted', persistedTime);
- savedResolve(response);
- }).catch((error) => {
- savedReject(error);
- });
- } else {
- result = Promise.reject(`[ObjectAPI][save] Object provider returned ${newObjectPromise} when creating new object.`);
- }
+ return this.get(identifier).then((object) => {
+ return this._toMutable(object);
+ });
+ }
+
+ /**
+ * This function is for cleaning up a mutable domain object when you're done with it.
+ * You only need to use this if you retrieved the object using `getMutable()`. If the object was provided by the
+ * platform (eg. passed into a `view()` function) then the platform is responsible for its lifecycle.
+ * @param {MutableDomainObject} domainObject
+ */
+ destroyMutable(domainObject) {
+ if (domainObject.isMutable) {
+ return domainObject.$destroy();
} else {
- domainObject.persisted = persistedTime;
- this.mutate(domainObject, 'persisted', persistedTime);
- result = provider.update(domainObject);
+ throw new Error("Attempted to destroy non-mutable domain object");
}
}
- return result;
-};
-
-/**
- * After entering into edit mode, creates a new instance of Transaction to keep track of changes in Objects
- */
-ObjectAPI.prototype.startTransaction = function () {
- if (this.isTransactionActive()) {
- throw new Error("Unable to start new Transaction: Previous Transaction is active");
+ delete() {
+ throw new Error('Delete not implemented');
}
- this.transaction = new Transaction(this);
-};
-
-/**
- * Clear instance of Transaction
- */
-ObjectAPI.prototype.endTransaction = function () {
- this.transaction = null;
-};
+ isPersistable(idOrKeyString) {
+ let identifier = utils.parseKeyString(idOrKeyString);
+ let provider = this.getProvider(identifier);
-/**
- * Add a root-level object.
- * @param {module:openmct.ObjectAPI~Identifier|function} an array of
- * identifiers for root level objects, or a function that returns a
- * promise for an identifier or an array of root level objects.
- * @method addRoot
- * @memberof module:openmct.ObjectAPI#
- */
-ObjectAPI.prototype.addRoot = function (key) {
- this.rootRegistry.addRoot(key);
-};
+ return provider !== undefined
+ && provider.create !== undefined
+ && provider.update !== undefined;
+ }
-/**
- * Register an object interceptor that transforms a domain object requested via module:openmct.ObjectAPI.get
- * The domain object will be transformed after it is retrieved from the persistence store
- * The domain object will be transformed only if the interceptor is applicable to that domain object as defined by the InterceptorDef
- *
- * @param {module:openmct.InterceptorDef} interceptorDef the interceptor definition to add
- * @method addGetInterceptor
- * @memberof module:openmct.InterceptorRegistry#
- */
-ObjectAPI.prototype.addGetInterceptor = function (interceptorDef) {
- this.interceptorRegistry.addInterceptor(interceptorDef);
-};
+ isMissing(domainObject) {
+ let identifier = utils.makeKeyString(domainObject.identifier);
+ let missingName = 'Missing: ' + identifier;
-/**
- * Retrieve the interceptors for a given domain object.
- * @private
- */
-ObjectAPI.prototype.listGetInterceptors = function (identifier, object) {
- return this.interceptorRegistry.getInterceptors(identifier, object);
-};
+ return domainObject.name === missingName;
+ }
-/**
- * Inovke interceptors if applicable for a given domain object.
- * @private
- */
-ObjectAPI.prototype.applyGetInterceptors = function (identifier, domainObject) {
- const interceptors = this.listGetInterceptors(identifier, domainObject);
- interceptors.forEach(interceptor => {
- domainObject = interceptor.invoke(identifier, domainObject);
- });
+ /**
+ * Save this domain object in its current state.
+ *
+ * @memberof module:openmct.ObjectAPI#
+ * @param {module:openmct.DomainObject} domainObject the domain object to
+ * save
+ * @returns {Promise} a promise which will resolve when the domain object
+ * has been saved, or be rejected if it cannot be saved
+ */
+ save(domainObject) {
+ let provider = this.getProvider(domainObject.identifier);
+ let savedResolve;
+ let savedReject;
+ let result;
+
+ if (!this.isPersistable(domainObject.identifier)) {
+ result = Promise.reject('Object provider does not support saving');
+ } else if (this.#hasAlreadyBeenPersisted(domainObject)) {
+ result = Promise.resolve(true);
+ } else {
+ const persistedTime = Date.now();
+ if (domainObject.persisted === undefined) {
+ result = new Promise((resolve, reject) => {
+ savedResolve = resolve;
+ savedReject = reject;
+ });
+ domainObject.persisted = persistedTime;
+ const newObjectPromise = provider.create(domainObject);
+ if (newObjectPromise) {
+ newObjectPromise.then(response => {
+ this.mutate(domainObject, 'persisted', persistedTime);
+ savedResolve(response);
+ }).catch((error) => {
+ savedReject(error);
+ });
+ } else {
+ result = Promise.reject(`[ObjectAPI][save] Object provider returned ${newObjectPromise} when creating new object.`);
+ }
+ } else {
+ domainObject.persisted = persistedTime;
+ this.mutate(domainObject, 'persisted', persistedTime);
+ result = provider.update(domainObject);
+ }
+ }
- return domainObject;
-};
+ return result;
+ }
-/**
- * Return relative url path from a given object path
- * eg: #/browse/mine/cb56f6bf-c900-43b7-b923-2e3b64b412db/6e89e858-77ce-46e4-a1ad-749240286497/....
- * @param {Array} objectPath
- * @returns {string} relative url for object
- */
-ObjectAPI.prototype.getRelativePath = function (objectPath) {
- return objectPath
- .map(p => this.makeKeyString(p.identifier))
- .reverse()
- .join('/')
- ;
-};
+ /**
+ * After entering into edit mode, creates a new instance of Transaction to keep track of changes in Objects
+ */
+ startTransaction() {
+ if (this.isTransactionActive()) {
+ throw new Error("Unable to start new Transaction: Previous Transaction is active");
+ }
-/**
- * Modify a domain object.
- * @param {module:openmct.DomainObject} object the object to mutate
- * @param {string} path the property to modify
- * @param {*} value the new value for this property
- * @method mutate
- * @memberof module:openmct.ObjectAPI#
- */
-ObjectAPI.prototype.mutate = function (domainObject, path, value) {
- if (!this.supportsMutation(domainObject.identifier)) {
- throw `Error: Attempted to mutate immutable object ${domainObject.name}`;
+ this.transaction = new Transaction(this);
}
- if (domainObject.isMutable) {
- domainObject.$set(path, value);
- } else {
- //Creating a temporary mutable domain object allows other mutable instances of the
- //object to be kept in sync.
- let mutableDomainObject = this._toMutable(domainObject);
-
- //Mutate original object
- MutableDomainObject.mutateObject(domainObject, path, value);
+ /**
+ * Clear instance of Transaction
+ */
+ endTransaction() {
+ this.transaction = null;
+ }
- //Mutate temporary mutable object, in the process informing any other mutable instances
- mutableDomainObject.$set(path, value);
+ /**
+ * Add a root-level object.
+ * @param {module:openmct.ObjectAPI~Identifier|array|function} identifier an identifier or
+ * an array of identifiers for root level objects, or a function that returns a
+ * promise for an identifier or an array of root level objects.
+ * @param {module:openmct.PriorityAPI~priority|Number} priority a number representing
+ * this item(s) position in the root object's composition (example: order in object tree).
+ * For arrays, they are treated as blocks.
+ * @method addRoot
+ * @memberof module:openmct.ObjectAPI#
+ */
+ addRoot(identifier, priority) {
+ this.rootRegistry.addRoot(identifier, priority);
+ }
- //Destroy temporary mutable object
- this.destroyMutable(mutableDomainObject);
+ /**
+ * Register an object interceptor that transforms a domain object requested via module:openmct.ObjectAPI.get
+ * The domain object will be transformed after it is retrieved from the persistence store
+ * The domain object will be transformed only if the interceptor is applicable to that domain object as defined by the InterceptorDef
+ *
+ * @param {module:openmct.InterceptorDef} interceptorDef the interceptor definition to add
+ * @method addGetInterceptor
+ * @memberof module:openmct.InterceptorRegistry#
+ */
+ addGetInterceptor(interceptorDef) {
+ this.interceptorRegistry.addInterceptor(interceptorDef);
}
- if (this.isTransactionActive()) {
- this.transaction.add(domainObject);
- } else {
- this.save(domainObject);
+ /**
+ * Retrieve the interceptors for a given domain object.
+ * @private
+ */
+ #listGetInterceptors(identifier, object) {
+ return this.interceptorRegistry.getInterceptors(identifier, object);
}
-};
-/**
- * Updates a domain object based on its latest persisted state. Note that this will mutate the provided object.
- * @param {module:openmct.DomainObject} domainObject an object to refresh from its persistence store
- * @returns {Promise} the provided object, updated to reflect the latest persisted state of the object.
- */
-ObjectAPI.prototype.refresh = async function (domainObject) {
- const refreshedObject = await this.get(domainObject.identifier);
+ /**
+ * Inovke interceptors if applicable for a given domain object.
+ * @private
+ */
+ applyGetInterceptors(identifier, domainObject) {
+ const interceptors = this.#listGetInterceptors(identifier, domainObject);
+ interceptors.forEach(interceptor => {
+ domainObject = interceptor.invoke(identifier, domainObject);
+ });
- if (domainObject.isMutable) {
- domainObject.$refresh(refreshedObject);
- } else {
- utils.refresh(domainObject, refreshedObject);
+ return domainObject;
}
- return domainObject;
-};
+ /**
+ * Return relative url path from a given object path
+ * eg: #/browse/mine/cb56f6bf-c900-43b7-b923-2e3b64b412db/6e89e858-77ce-46e4-a1ad-749240286497/....
+ * @param {Array} objectPath
+ * @returns {string} relative url for object
+ */
+ getRelativePath(objectPath) {
+ return objectPath
+ .map(p => this.makeKeyString(p.identifier))
+ .reverse()
+ .join('/');
+ }
-/**
- * @private
- */
-ObjectAPI.prototype._toMutable = function (object) {
- let mutableObject;
+ /**
+ * Modify a domain object.
+ * @param {module:openmct.DomainObject} object the object to mutate
+ * @param {string} path the property to modify
+ * @param {*} value the new value for this property
+ * @method mutate
+ * @memberof module:openmct.ObjectAPI#
+ */
+ mutate(domainObject, path, value) {
+ if (!this.supportsMutation(domainObject.identifier)) {
+ throw `Error: Attempted to mutate immutable object ${domainObject.name}`;
+ }
- if (object.isMutable) {
- mutableObject = object;
- } else {
- mutableObject = MutableDomainObject.createMutable(object, this.eventEmitter);
+ if (domainObject.isMutable) {
+ domainObject.$set(path, value);
+ } else {
+ //Creating a temporary mutable domain object allows other mutable instances of the
+ //object to be kept in sync.
+ let mutableDomainObject = this._toMutable(domainObject);
- // Check if provider supports realtime updates
- let identifier = utils.parseKeyString(mutableObject.identifier);
- let provider = this.getProvider(identifier);
+ //Mutate original object
+ MutableDomainObject.mutateObject(domainObject, path, value);
- if (provider !== undefined
- && provider.observe !== undefined
- && this.SYNCHRONIZED_OBJECT_TYPES.includes(object.type)) {
- let unobserve = provider.observe(identifier, (updatedModel) => {
- if (updatedModel.persisted > mutableObject.modified) {
- //Don't replace with a stale model. This can happen on slow connections when multiple mutations happen
- //in rapid succession and intermediate persistence states are returned by the observe function.
- updatedModel = this.applyGetInterceptors(identifier, updatedModel);
- mutableObject.$refresh(updatedModel);
- }
- });
- mutableObject.$on('$_destroy', () => {
- unobserve();
- });
+ //Mutate temporary mutable object, in the process informing any other mutable instances
+ mutableDomainObject.$set(path, value);
+
+ //Destroy temporary mutable object
+ this.destroyMutable(mutableDomainObject);
+ }
+
+ if (this.isTransactionActive()) {
+ this.transaction.add(domainObject);
+ } else {
+ this.save(domainObject);
}
}
- return mutableObject;
-};
+ /**
+ * @private
+ */
+ _toMutable(object) {
+ let mutableObject;
-/**
- * Updates a domain object based on its latest persisted state. Note that this will mutate the provided object.
- * @param {module:openmct.DomainObject} domainObject an object to refresh from its persistence store
- * @returns {Promise} the provided object, updated to reflect the latest persisted state of the object.
- */
-ObjectAPI.prototype.refresh = async function (domainObject) {
- const refreshedObject = await this.get(domainObject.identifier);
+ if (object.isMutable) {
+ mutableObject = object;
+ } else {
+ mutableObject = MutableDomainObject.createMutable(object, this.eventEmitter);
+
+ // Check if provider supports realtime updates
+ let identifier = utils.parseKeyString(mutableObject.identifier);
+ let provider = this.getProvider(identifier);
+
+ if (provider !== undefined
+ && provider.observe !== undefined
+ && this.SYNCHRONIZED_OBJECT_TYPES.includes(object.type)) {
+ let unobserve = provider.observe(identifier, (updatedModel) => {
+ if (updatedModel.persisted > mutableObject.modified) {
+ //Don't replace with a stale model. This can happen on slow connections when multiple mutations happen
+ //in rapid succession and intermediate persistence states are returned by the observe function.
+ updatedModel = this.applyGetInterceptors(identifier, updatedModel);
+ mutableObject.$refresh(updatedModel);
+ }
+ });
+ mutableObject.$on('$_destroy', () => {
+ unobserve();
+ });
+ }
+ }
- if (domainObject.isMutable) {
- domainObject.$refresh(refreshedObject);
- } else {
- utils.refresh(domainObject, refreshedObject);
+ return mutableObject;
}
- return domainObject;
-};
+ /**
+ * Updates a domain object based on its latest persisted state. Note that this will mutate the provided object.
+ * @param {module:openmct.DomainObject} domainObject an object to refresh from its persistence store
+ * @returns {Promise} the provided object, updated to reflect the latest persisted state of the object.
+ */
+ async refresh(domainObject) {
+ const refreshedObject = await this.get(domainObject.identifier);
-/**
- * @param module:openmct.ObjectAPI~Identifier identifier An object identifier
- * @returns {boolean} true if the object can be mutated, otherwise returns false
- */
-ObjectAPI.prototype.supportsMutation = function (identifier) {
- return this.isPersistable(identifier);
-};
+ if (domainObject.isMutable) {
+ domainObject.$refresh(refreshedObject);
+ } else {
+ utils.refresh(domainObject, refreshedObject);
+ }
-/**
- * Observe changes to a domain object.
- * @param {module:openmct.DomainObject} object the object to observe
- * @param {string} path the property to observe
- * @param {Function} callback a callback to invoke when new values for
- * this property are observed
- * @method observe
- * @memberof module:openmct.ObjectAPI#
- */
-ObjectAPI.prototype.observe = function (domainObject, path, callback) {
- if (domainObject.isMutable) {
- return domainObject.$observe(path, callback);
- } else {
- let mutable = this._toMutable(domainObject);
- mutable.$observe(path, callback);
+ return domainObject;
+ }
- return () => mutable.$destroy();
+ /**
+ * @param module:openmct.ObjectAPI~Identifier identifier An object identifier
+ * @returns {boolean} true if the object can be mutated, otherwise returns false
+ */
+ supportsMutation(identifier) {
+ return this.isPersistable(identifier);
}
-};
-/**
- * @param {module:openmct.ObjectAPI~Identifier} identifier
- * @returns {string} A string representation of the given identifier, including namespace and key
- */
-ObjectAPI.prototype.makeKeyString = function (identifier) {
- return utils.makeKeyString(identifier);
-};
+ /**
+ * Observe changes to a domain object.
+ * @param {module:openmct.DomainObject} object the object to observe
+ * @param {string} path the property to observe
+ * @param {Function} callback a callback to invoke when new values for
+ * this property are observed
+ * @method observe
+ * @memberof module:openmct.ObjectAPI#
+ */
+ observe(domainObject, path, callback) {
+ if (domainObject.isMutable) {
+ return domainObject.$observe(path, callback);
+ } else {
+ let mutable = this._toMutable(domainObject);
+ mutable.$observe(path, callback);
-/**
- * @param {string} keyString A string representation of the given identifier, that is, a namespace and key separated by a colon.
- * @returns {module:openmct.ObjectAPI~Identifier} An identifier object
- */
-ObjectAPI.prototype.parseKeyString = function (keyString) {
- return utils.parseKeyString(keyString);
-};
+ return () => mutable.$destroy();
+ }
+ }
-/**
- * Given any number of identifiers, will return true if they are all equal, otherwise false.
- * @param {module:openmct.ObjectAPI~Identifier[]} identifiers
- */
-ObjectAPI.prototype.areIdsEqual = function (...identifiers) {
- return identifiers.map(utils.parseKeyString)
- .every(identifier => {
- return identifier === identifiers[0]
- || (identifier.namespace === identifiers[0].namespace
- && identifier.key === identifiers[0].key);
- });
-};
+ /**
+ * @param {module:openmct.ObjectAPI~Identifier} identifier
+ * @returns {string} A string representation of the given identifier, including namespace and key
+ */
+ makeKeyString(identifier) {
+ return utils.makeKeyString(identifier);
+ }
-ObjectAPI.prototype.getOriginalPath = function (identifier, path = []) {
- return this.get(identifier).then((domainObject) => {
- path.push(domainObject);
- let location = domainObject.location;
+ /**
+ * @param {string} keyString A string representation of the given identifier, that is, a namespace and key separated by a colon.
+ * @returns {module:openmct.ObjectAPI~Identifier} An identifier object
+ */
+ parseKeyString(keyString) {
+ return utils.parseKeyString(keyString);
+ }
- if (location) {
- return this.getOriginalPath(utils.parseKeyString(location), path);
- } else {
- return path;
- }
- });
-};
+ /**
+ * Given any number of identifiers, will return true if they are all equal, otherwise false.
+ * @param {module:openmct.ObjectAPI~Identifier[]} identifiers
+ */
+ areIdsEqual(...identifiers) {
+ return identifiers.map(utils.parseKeyString)
+ .every(identifier => {
+ return identifier === identifiers[0]
+ || (identifier.namespace === identifiers[0].namespace
+ && identifier.key === identifiers[0].key);
+ });
+ }
-ObjectAPI.prototype.isObjectPathToALink = function (domainObject, objectPath) {
- return objectPath !== undefined
- && objectPath.length > 1
- && domainObject.location !== this.makeKeyString(objectPath[1].identifier);
-};
+ getOriginalPath(identifier, path = []) {
+ return this.get(identifier).then((domainObject) => {
+ path.push(domainObject);
+ let location = domainObject.location;
-ObjectAPI.prototype.isTransactionActive = function () {
- return Boolean(this.transaction && this.openmct.editor.isEditing());
-};
+ if (location) {
+ return this.getOriginalPath(utils.parseKeyString(location), path);
+ } else {
+ return path;
+ }
+ });
+ }
-/**
- * Uniquely identifies a domain object.
- *
- * @typedef Identifier
- * @memberof module:openmct.ObjectAPI~
- * @property {string} namespace the namespace to/from which this domain
- * object should be loaded/stored.
- * @property {string} key a unique identifier for the domain object
- * within that namespace
- */
+ isObjectPathToALink(domainObject, objectPath) {
+ return objectPath !== undefined
+ && objectPath.length > 1
+ && domainObject.location !== this.makeKeyString(objectPath[1].identifier);
+ }
-/**
- * A domain object is an entity of relevance to a user's workflow, that
- * should appear as a distinct and meaningful object within the user
- * interface. Examples of domain objects are folders, telemetry sensors,
- * and so forth.
- *
- * A few common properties are defined for domain objects. Beyond these,
- * individual types of domain objects may add more as they see fit.
- *
- * @property {module:openmct.ObjectAPI~Identifier} identifier a key/namespace pair which
- * uniquely identifies this domain object
- * @property {string} type the type of domain object
- * @property {string} name the human-readable name for this domain object
- * @property {string} [creator] the user name of the creator of this domain
- * object
- * @property {number} [modified] the time, in milliseconds since the UNIX
- * epoch, at which this domain object was last modified
- * @property {module:openmct.ObjectAPI~Identifier[]} [composition] if
- * present, this will be used by the default composition provider
- * to load domain objects
- * @typedef DomainObject
- * @memberof module:openmct
- */
+ isTransactionActive() {
+ return Boolean(this.transaction && this.openmct.editor.isEditing());
+ }
-function hasAlreadyBeenPersisted(domainObject) {
- const result = domainObject.persisted !== undefined
- && domainObject.persisted >= domainObject.modified;
+ #hasAlreadyBeenPersisted(domainObject) {
+ const result = domainObject.persisted !== undefined
+ && domainObject.persisted >= domainObject.modified;
- return result;
+ return result;
+ }
}
-
-export default ObjectAPI;
diff --git a/src/api/objects/ObjectAPISearchSpec.js b/src/api/objects/ObjectAPISearchSpec.js
index 47886026b..1b25c63ae 100644
--- a/src/api/objects/ObjectAPISearchSpec.js
+++ b/src/api/objects/ObjectAPISearchSpec.js
@@ -17,13 +17,16 @@ describe("The Object API Search Function", () => {
openmct = createOpenMct();
mockObjectProvider = jasmine.createSpyObj("mock object provider", [
- "search"
+ "search", "supportsSearchType"
]);
anotherMockObjectProvider = jasmine.createSpyObj("another mock object provider", [
- "search"
+ "search", "supportsSearchType"
]);
openmct.objects.addProvider('objects', mockObjectProvider);
openmct.objects.addProvider('other-objects', anotherMockObjectProvider);
+ mockObjectProvider.supportsSearchType.and.callFake(() => {
+ return true;
+ });
mockObjectProvider.search.and.callFake(() => {
return new Promise(resolve => {
const mockProviderSearch = {
@@ -38,6 +41,9 @@ describe("The Object API Search Function", () => {
}, MOCK_PROVIDER_SEARCH_DELAY);
});
});
+ anotherMockObjectProvider.supportsSearchType.and.callFake(() => {
+ return true;
+ });
anotherMockObjectProvider.search.and.callFake(() => {
return new Promise(resolve => {
const anotherMockProviderSearch = {
@@ -105,13 +111,18 @@ describe("The Object API Search Function", () => {
beforeEach((done) => {
openmct = createOpenMct();
- spyOn(openmct.objects.inMemorySearchProvider, "query").and.callThrough();
- spyOn(openmct.objects.inMemorySearchProvider, "localSearch").and.callThrough();
+ const defaultObjectProvider = openmct.objects.getProvider({
+ key: '',
+ namespace: ''
+ });
+ openmct.objects.addProvider('foo', defaultObjectProvider);
+ spyOn(openmct.objects.inMemorySearchProvider, "search").and.callThrough();
+ spyOn(openmct.objects.inMemorySearchProvider, "localSearchForObjects").and.callThrough();
openmct.on('start', async () => {
mockIdentifier1 = {
key: 'some-object',
- namespace: 'some-namespace'
+ namespace: 'foo'
};
mockDomainObject1 = {
type: 'clock',
@@ -120,7 +131,7 @@ describe("The Object API Search Function", () => {
};
mockIdentifier2 = {
key: 'some-other-object',
- namespace: 'some-namespace'
+ namespace: 'foo'
};
mockDomainObject2 = {
type: 'clock',
@@ -129,16 +140,16 @@ describe("The Object API Search Function", () => {
};
mockIdentifier3 = {
key: 'yet-another-object',
- namespace: 'some-namespace'
+ namespace: 'foo'
};
mockDomainObject3 = {
type: 'clock',
name: 'redBear',
identifier: mockIdentifier3
};
- await openmct.objects.inMemorySearchProvider.index(mockIdentifier1, mockDomainObject1);
- await openmct.objects.inMemorySearchProvider.index(mockIdentifier2, mockDomainObject2);
- await openmct.objects.inMemorySearchProvider.index(mockIdentifier3, mockDomainObject3);
+ await openmct.objects.inMemorySearchProvider.index(mockDomainObject1);
+ await openmct.objects.inMemorySearchProvider.index(mockDomainObject2);
+ await openmct.objects.inMemorySearchProvider.index(mockDomainObject3);
done();
});
openmct.startHeadless();
@@ -150,7 +161,7 @@ describe("The Object API Search Function", () => {
it("can provide indexing without a provider", () => {
openmct.objects.search('foo');
- expect(openmct.objects.inMemorySearchProvider.query).toHaveBeenCalled();
+ expect(openmct.objects.inMemorySearchProvider.search).toHaveBeenCalled();
});
it("can do partial search", async () => {
@@ -172,16 +183,22 @@ describe("The Object API Search Function", () => {
});
describe("Without Shared Workers", () => {
+ let sharedWorkerToRestore;
beforeEach(async () => {
+ // use local worker
+ sharedWorkerToRestore = openmct.objects.inMemorySearchProvider.worker;
openmct.objects.inMemorySearchProvider.worker = null;
// reindex locally
- await openmct.objects.inMemorySearchProvider.index(mockIdentifier1, mockDomainObject1);
- await openmct.objects.inMemorySearchProvider.index(mockIdentifier2, mockDomainObject2);
- await openmct.objects.inMemorySearchProvider.index(mockIdentifier3, mockDomainObject3);
+ await openmct.objects.inMemorySearchProvider.index(mockDomainObject1);
+ await openmct.objects.inMemorySearchProvider.index(mockDomainObject2);
+ await openmct.objects.inMemorySearchProvider.index(mockDomainObject3);
+ });
+ afterEach(() => {
+ openmct.objects.inMemorySearchProvider.worker = sharedWorkerToRestore;
});
it("calls local search", () => {
openmct.objects.search('foo');
- expect(openmct.objects.inMemorySearchProvider.localSearch).toHaveBeenCalled();
+ expect(openmct.objects.inMemorySearchProvider.localSearchForObjects).toHaveBeenCalled();
});
it("can do partial search", async () => {
diff --git a/src/api/objects/RootObjectProvider.js b/src/api/objects/RootObjectProvider.js
index 89024a3a4..0a9e15f06 100644
--- a/src/api/objects/RootObjectProvider.js
+++ b/src/api/objects/RootObjectProvider.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/api/objects/RootRegistry.js b/src/api/objects/RootRegistry.js
index a2406c14f..06e18a1d2 100644
--- a/src/api/objects/RootRegistry.js
+++ b/src/api/objects/RootRegistry.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -20,39 +20,43 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
-define([
- 'lodash'
-], function (
- _
-) {
+import utils from './object-utils';
- function RootRegistry() {
- this.providers = [];
+export default class RootRegistry {
+
+ constructor(openmct) {
+ this._rootItems = [];
+ this._openmct = openmct;
}
- RootRegistry.prototype.getRoots = function () {
- const promises = this.providers.map(function (provider) {
- return provider();
- });
+ getRoots() {
+ const sortedItems = this._rootItems.sort((a, b) => b.priority - a.priority);
+ const promises = sortedItems.map((rootItem) => rootItem.provider());
- return Promise.all(promises)
- .then(_.flatten);
- };
+ return Promise.all(promises).then(rootItems => rootItems.flat());
+ }
- function isKey(key) {
- return _.isObject(key) && _.has(key, 'key') && _.has(key, 'namespace');
+ addRoot(rootItem, priority) {
+
+ if (!this._isValid(rootItem)) {
+ return;
+ }
+
+ this._rootItems.push({
+ priority: priority || this._openmct.priority.DEFAULT,
+ provider: typeof rootItem === 'function' ? rootItem : () => rootItem
+ });
}
- RootRegistry.prototype.addRoot = function (key) {
- if (isKey(key) || (Array.isArray(key) && key.every(isKey))) {
- this.providers.push(function () {
- return key;
- });
- } else if (typeof key === "function") {
- this.providers.push(key);
+ _isValid(rootItem) {
+ if (utils.isIdentifier(rootItem) || typeof rootItem === 'function') {
+ return true;
}
- };
- return RootRegistry;
+ if (Array.isArray(rootItem)) {
+ return rootItem.every(utils.isIdentifier);
+ }
-});
+ return false;
+ }
+}
diff --git a/src/api/objects/Transaction.js b/src/api/objects/Transaction.js
index 0e5b1636c..d1c200f7d 100644
--- a/src/api/objects/Transaction.js
+++ b/src/api/objects/Transaction.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -22,12 +22,14 @@
export default class Transaction {
constructor(objectAPI) {
- this.dirtyObjects = new Set();
+ this.dirtyObjects = {};
this.objectAPI = objectAPI;
}
add(object) {
- this.dirtyObjects.add(object);
+ const key = this.objectAPI.makeKeyString(object.identifier);
+
+ this.dirtyObjects[key] = object;
}
cancel() {
@@ -37,7 +39,8 @@ export default class Transaction {
commit() {
const promiseArray = [];
const save = this.objectAPI.save.bind(this.objectAPI);
- this.dirtyObjects.forEach(object => {
+
+ Object.values(this.dirtyObjects).forEach(object => {
promiseArray.push(this.createDirtyObjectPromise(object, save));
});
@@ -48,7 +51,9 @@ export default class Transaction {
return new Promise((resolve, reject) => {
action(object)
.then((success) => {
- this.dirtyObjects.delete(object);
+ const key = this.objectAPI.makeKeyString(object.identifier);
+
+ delete this.dirtyObjects[key];
resolve(success);
})
.catch(reject);
@@ -57,7 +62,8 @@ export default class Transaction {
getDirtyObject(identifier) {
let dirtyObject;
- this.dirtyObjects.forEach(object => {
+
+ Object.values(this.dirtyObjects).forEach(object => {
const areIdsEqual = this.objectAPI.areIdsEqual(object.identifier, identifier);
if (areIdsEqual) {
dirtyObject = object;
@@ -67,14 +73,11 @@ export default class Transaction {
return dirtyObject;
}
- start() {
- this.dirtyObjects = new Set();
- }
-
_clear() {
const promiseArray = [];
const refresh = this.objectAPI.refresh.bind(this.objectAPI);
- this.dirtyObjects.forEach(object => {
+
+ Object.values(this.dirtyObjects).forEach(object => {
promiseArray.push(this.createDirtyObjectPromise(object, refresh));
});
diff --git a/src/api/objects/TransactionSpec.js b/src/api/objects/TransactionSpec.js
index 286401ad1..8d195f0fe 100644
--- a/src/api/objects/TransactionSpec.js
+++ b/src/api/objects/TransactionSpec.js
@@ -34,24 +34,24 @@ describe("Transaction Class", () => {
});
it('has no dirty objects', () => {
- expect(transaction.dirtyObjects.size).toEqual(0);
+ expect(Object.keys(transaction.dirtyObjects).length).toEqual(0);
});
it('add(), adds object to dirtyObjects', () => {
const mockDomainObjects = createMockDomainObjects();
transaction.add(mockDomainObjects[0]);
- expect(transaction.dirtyObjects.size).toEqual(1);
+ expect(Object.keys(transaction.dirtyObjects).length).toEqual(1);
});
it('cancel(), clears all dirtyObjects', (done) => {
const mockDomainObjects = createMockDomainObjects(3);
mockDomainObjects.forEach(transaction.add.bind(transaction));
- expect(transaction.dirtyObjects.size).toEqual(3);
+ expect(Object.keys(transaction.dirtyObjects).length).toEqual(3);
transaction.cancel()
.then(success => {
- expect(transaction.dirtyObjects.size).toEqual(0);
+ expect(Object.keys(transaction.dirtyObjects).length).toEqual(0);
}).finally(done);
});
@@ -59,12 +59,12 @@ describe("Transaction Class", () => {
const mockDomainObjects = createMockDomainObjects(3);
mockDomainObjects.forEach(transaction.add.bind(transaction));
- expect(transaction.dirtyObjects.size).toEqual(3);
+ expect(Object.keys(transaction.dirtyObjects).length).toEqual(3);
spyOn(objectAPI, 'save').and.callThrough();
transaction.commit()
.then(success => {
- expect(transaction.dirtyObjects.size).toEqual(0);
+ expect(Object.keys(transaction.dirtyObjects).length).toEqual(0);
expect(objectAPI.save.calls.count()).toEqual(3);
}).finally(done);
});
@@ -73,7 +73,7 @@ describe("Transaction Class", () => {
const mockDomainObjects = createMockDomainObjects();
transaction.add(mockDomainObjects[0]);
- expect(transaction.dirtyObjects.size).toEqual(1);
+ expect(Object.keys(transaction.dirtyObjects).length).toEqual(1);
const dirtyObject = transaction.getDirtyObject(mockDomainObjects[0].identifier);
expect(dirtyObject).toEqual(mockDomainObjects[0]);
@@ -82,7 +82,7 @@ describe("Transaction Class", () => {
it('getDirtyObject(), returns empty dirtyObject for no active transaction', () => {
const mockDomainObjects = createMockDomainObjects();
- expect(transaction.dirtyObjects.size).toEqual(0);
+ expect(Object.keys(transaction.dirtyObjects).length).toEqual(0);
const dirtyObject = transaction.getDirtyObject(mockDomainObjects[0].identifier);
expect(dirtyObject).toEqual(undefined);
diff --git a/src/api/objects/object-utils.js b/src/api/objects/object-utils.js
index 7a39f44ca..9ee7c5d83 100644
--- a/src/api/objects/object-utils.js
+++ b/src/api/objects/object-utils.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -177,6 +177,7 @@ define([
}
return {
+ isIdentifier: isIdentifier,
toOldFormat: toOldFormat,
toNewFormat: toNewFormat,
makeKeyString: makeKeyString,
diff --git a/src/api/objects/test/RootObjectProviderSpec.js b/src/api/objects/test/RootObjectProviderSpec.js
index c5909093c..a5a4417af 100644
--- a/src/api/objects/test/RootObjectProviderSpec.js
+++ b/src/api/objects/test/RootObjectProviderSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/api/objects/test/RootRegistrySpec.js b/src/api/objects/test/RootRegistrySpec.js
index b10e955e9..9835bf024 100644
--- a/src/api/objects/test/RootRegistrySpec.js
+++ b/src/api/objects/test/RootRegistrySpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -19,83 +19,113 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
-define([
- '../RootRegistry'
-], function (
- RootRegistry
-) {
- describe('RootRegistry', function () {
- let idA;
- let idB;
- let idC;
- let registry;
-
- beforeEach(function () {
- idA = {
- key: 'keyA',
- namespace: 'something'
- };
- idB = {
- key: 'keyB',
- namespace: 'something'
- };
- idC = {
- key: 'keyC',
- namespace: 'something'
- };
- registry = new RootRegistry();
- });
-
- it('can register a root by key', function () {
- registry.addRoot(idA);
-
- return registry.getRoots()
- .then(function (roots) {
- expect(roots).toEqual([idA]);
- });
- });
-
- it('can register multiple roots by key', function () {
- registry.addRoot([idA, idB]);
-
- return registry.getRoots()
- .then(function (roots) {
- expect(roots).toEqual([idA, idB]);
- });
- });
-
- it('can register an asynchronous root ', function () {
- registry.addRoot(function () {
- return Promise.resolve(idA);
+
+import { createOpenMct, resetApplicationState } from '../../../utils/testing';
+
+describe('RootRegistry', () => {
+ let openmct;
+ let idA;
+ let idB;
+ let idC;
+ let idD;
+
+ beforeEach((done) => {
+ openmct = createOpenMct();
+ idA = {
+ key: 'keyA',
+ namespace: 'something'
+ };
+ idB = {
+ key: 'keyB',
+ namespace: 'something'
+ };
+ idC = {
+ key: 'keyC',
+ namespace: 'something'
+ };
+ idD = {
+ key: 'keyD',
+ namespace: 'something'
+ };
+
+ openmct.on('start', done);
+ openmct.startHeadless();
+ });
+
+ afterEach(async () => {
+ await resetApplicationState(openmct);
+ });
+
+ it('can register a root by identifier', () => {
+ openmct.objects.addRoot(idA);
+
+ return openmct.objects.getRoot()
+ .then((rootObject) => {
+ expect(rootObject.composition).toEqual([idA]);
+ });
+ });
+
+ it('can register multiple roots by identifier', () => {
+ openmct.objects.addRoot([idA, idB]);
+
+ return openmct.objects.getRoot()
+ .then((rootObject) => {
+ expect(rootObject.composition).toEqual([idA, idB]);
});
+ });
- return registry.getRoots()
- .then(function (roots) {
- expect(roots).toEqual([idA]);
- });
- });
+ it('can register an asynchronous root ', () => {
+ openmct.objects.addRoot(() => Promise.resolve(idA));
- it('can register multiple asynchronous roots', function () {
- registry.addRoot(function () {
- return Promise.resolve([idA, idB]);
+ return openmct.objects.getRoot()
+ .then((rootObject) => {
+ expect(rootObject.composition).toEqual([idA]);
});
+ });
- return registry.getRoots()
- .then(function (roots) {
- expect(roots).toEqual([idA, idB]);
- });
- });
+ it('can register multiple asynchronous roots', () => {
+ openmct.objects.addRoot(() => Promise.resolve([idA, idB]));
- it('can combine different types of registration', function () {
- registry.addRoot([idA, idB]);
- registry.addRoot(function () {
- return Promise.resolve([idC]);
+ return openmct.objects.getRoot()
+ .then((rootObject) => {
+ expect(rootObject.composition).toEqual([idA, idB]);
});
+ });
+
+ it('can combine different types of registration', () => {
+ openmct.objects.addRoot([idA, idB]);
+ openmct.objects.addRoot(() => Promise.resolve([idC]));
+
+ return openmct.objects.getRoot()
+ .then((rootObject) => {
+ expect(rootObject.composition).toEqual([idA, idB, idC]);
+ });
+ });
- return registry.getRoots()
- .then(function (roots) {
- expect(roots).toEqual([idA, idB, idC]);
- });
- });
+ it('supports priority ordering for identifiers', () => {
+ openmct.objects.addRoot(idA, openmct.priority.LOW);
+ openmct.objects.addRoot(idB, openmct.priority.HIGH);
+ openmct.objects.addRoot(idC); // DEFAULT
+
+ return openmct.objects.getRoot()
+ .then((rootObject) => {
+ expect(rootObject.composition[0]).toEqual(idB);
+ expect(rootObject.composition[1]).toEqual(idC);
+ expect(rootObject.composition[2]).toEqual(idA);
+ });
+ });
+
+ it('supports priority ordering for different types of registration', () => {
+ openmct.objects.addRoot(() => Promise.resolve([idC]), openmct.priority.LOW);
+ openmct.objects.addRoot(idB, openmct.priority.HIGH);
+ openmct.objects.addRoot([idA, idD]); // default
+
+ return openmct.objects.getRoot()
+ .then((rootObject) => {
+ expect(rootObject.composition[0]).toEqual(idB);
+ expect(rootObject.composition[1]).toEqual(idA);
+ expect(rootObject.composition[2]).toEqual(idD);
+ expect(rootObject.composition[3]).toEqual(idC);
+ });
});
});
diff --git a/src/api/overlays/components/OverlayComponent.vue b/src/api/overlays/components/OverlayComponent.vue
index f8a66f759..9742fd736 100644
--- a/src/api/overlays/components/OverlayComponent.vue
+++ b/src/api/overlays/components/OverlayComponent.vue
@@ -7,6 +7,7 @@
<div class="c-overlay__outer">
<button
v-if="dismissable"
+ aria-label="Close"
class="c-click-icon c-overlay__close-button icon-x"
@click="destroy"
></button>
diff --git a/src/api/priority/PriorityAPI.js b/src/api/priority/PriorityAPI.js
index d0401a11a..acba58437 100644
--- a/src/api/priority/PriorityAPI.js
+++ b/src/api/priority/PriorityAPI.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/api/status/StatusAPI.js b/src/api/status/StatusAPI.js
index 0251f4338..5848c48d6 100644
--- a/src/api/status/StatusAPI.js
+++ b/src/api/status/StatusAPI.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/api/telemetry/DefaultMetadataProvider.js b/src/api/telemetry/DefaultMetadataProvider.js
index 3d95c317c..7a51369fb 100644
--- a/src/api/telemetry/DefaultMetadataProvider.js
+++ b/src/api/telemetry/DefaultMetadataProvider.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open openmct, Copyright (c) 2014-2021, United States Government
+ * Open openmct, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/api/telemetry/TelemetryAPI.js b/src/api/telemetry/TelemetryAPI.js
index 45fff447a..15cef5b19 100644
--- a/src/api/telemetry/TelemetryAPI.js
+++ b/src/api/telemetry/TelemetryAPI.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -20,122 +20,18 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
-const { TelemetryCollection } = require("./TelemetryCollection");
-
-define([
- '../../plugins/displayLayout/CustomStringFormatter',
- './TelemetryMetadataManager',
- './TelemetryValueFormatter',
- './DefaultMetadataProvider',
- 'objectUtils',
- 'lodash'
-], function (
- CustomStringFormatter,
- TelemetryMetadataManager,
- TelemetryValueFormatter,
- DefaultMetadataProvider,
- objectUtils,
- _
-) {
- /**
- * A LimitEvaluator may be used to detect when telemetry values
- * have exceeded nominal conditions.
- *
- * @interface LimitEvaluator
- * @memberof module:openmct.TelemetryAPI~
- */
-
- /**
- * Check for any limit violations associated with a telemetry datum.
- * @method evaluate
- * @param {*} datum the telemetry datum to evaluate
- * @param {TelemetryProperty} the property to check for limit violations
- * @memberof module:openmct.TelemetryAPI~LimitEvaluator
- * @returns {module:openmct.TelemetryAPI~LimitViolation} metadata about
- * the limit violation, or undefined if a value is within limits
- */
-
- /**
- * A violation of limits defined for a telemetry property.
- * @typedef LimitViolation
- * @memberof {module:openmct.TelemetryAPI~}
- * @property {string} cssClass the class (or space-separated classes) to
- * apply to display elements for values which violate this limit
- * @property {string} name the human-readable name for the limit violation
- */
-
- /**
- * A TelemetryFormatter converts telemetry values for purposes of
- * display as text.
- *
- * @interface TelemetryFormatter
- * @memberof module:openmct.TelemetryAPI~
- */
-
- /**
- * Retrieve the 'key' from the datum and format it accordingly to
- * telemetry metadata in domain object.
- *
- * @method format
- * @memberof module:openmct.TelemetryAPI~TelemetryFormatter#
- */
-
- /**
- * Describes a property which would be found in a datum of telemetry
- * associated with a particular domain object.
- *
- * @typedef TelemetryProperty
- * @memberof module:openmct.TelemetryAPI~
- * @property {string} key the name of the property in the datum which
- * contains this telemetry value
- * @property {string} name the human-readable name for this property
- * @property {string} [units] the units associated with this property
- * @property {boolean} [temporal] true if this property is a timestamp, or
- * may be otherwise used to order telemetry in a time-like
- * fashion; default is false
- * @property {boolean} [numeric] true if the values for this property
- * can be interpreted plainly as numbers; default is true
- * @property {boolean} [enumerated] true if this property may have only
- * certain specific values; default is false
- * @property {string} [values] for enumerated states, an ordered list
- * of possible values
- */
-
- /**
- * Describes and bounds requests for telemetry data.
- *
- * @typedef TelemetryRequest
- * @memberof module:openmct.TelemetryAPI~
- * @property {string} sort the key of the property to sort by. This may
- * be prefixed with a "+" or a "-" sign to sort in ascending
- * or descending order respectively. If no prefix is present,
- * ascending order will be used.
- * @property {*} start the lower bound for values of the sorting property
- * @property {*} end the upper bound for values of the sorting property
- * @property {string[]} strategies symbolic identifiers for strategies
- * (such as `minmax`) which may be recognized by providers;
- * these will be tried in order until an appropriate provider
- * is found
- */
+import TelemetryCollection from './TelemetryCollection';
+import TelemetryRequestInterceptorRegistry from './TelemetryRequestInterceptor';
+import CustomStringFormatter from '../../plugins/displayLayout/CustomStringFormatter';
+import TelemetryMetadataManager from './TelemetryMetadataManager';
+import TelemetryValueFormatter from './TelemetryValueFormatter';
+import DefaultMetadataProvider from './DefaultMetadataProvider';
+import objectUtils from 'objectUtils';
+import _ from 'lodash';
- /**
- * Provides telemetry data. To connect to new data sources, new
- * TelemetryProvider implementations should be
- * [registered]{@link module:openmct.TelemetryAPI#addProvider}.
- *
- * @interface TelemetryProvider
- * @memberof module:openmct.TelemetryAPI~
- */
+export default class TelemetryAPI {
- /**
- * An interface for retrieving telemetry data associated with a domain
- * object.
- *
- * @interface TelemetryAPI
- * @augments module:openmct.TelemetryAPI~TelemetryProvider
- * @memberof module:openmct
- */
- function TelemetryAPI(openmct) {
+ constructor(openmct) {
this.openmct = openmct;
this.formatMapCache = new WeakMap();
@@ -148,12 +44,14 @@ define([
this.requestProviders = [];
this.subscriptionProviders = [];
this.valueFormatterCache = new WeakMap();
+
+ this.requestInterceptorRegistry = new TelemetryRequestInterceptorRegistry();
}
- TelemetryAPI.prototype.abortAllRequests = function () {
+ abortAllRequests() {
this.requestAbortControllers.forEach((controller) => controller.abort());
this.requestAbortControllers.clear();
- };
+ }
/**
* Return Custom String Formatter
@@ -162,9 +60,9 @@ define([
* @param {string} format custom formatter string (eg: %.4f, &lts etc.)
* @returns {CustomStringFormatter}
*/
- TelemetryAPI.prototype.customStringFormatter = function (valueMetadata, format) {
- return new CustomStringFormatter.default(this.openmct, valueMetadata, format);
- };
+ customStringFormatter(valueMetadata, format) {
+ return new CustomStringFormatter(this.openmct, valueMetadata, format);
+ }
/**
* Return true if the given domainObject is a telemetry object. A telemetry
@@ -174,9 +72,9 @@ define([
* @param {module:openmct.DomainObject} domainObject
* @returns {boolean} true if the object is a telemetry object.
*/
- TelemetryAPI.prototype.isTelemetryObject = function (domainObject) {
+ isTelemetryObject(domainObject) {
return Boolean(this.findMetadataProvider(domainObject));
- };
+ }
/**
* Check if this provider can supply telemetry data associated with
@@ -188,10 +86,10 @@ define([
* @returns {boolean} true if telemetry can be provided
* @memberof module:openmct.TelemetryAPI~TelemetryProvider#
*/
- TelemetryAPI.prototype.canProvideTelemetry = function (domainObject) {
+ canProvideTelemetry(domainObject) {
return Boolean(this.findSubscriptionProvider(domainObject))
- || Boolean(this.findRequestProvider(domainObject));
- };
+ || Boolean(this.findRequestProvider(domainObject));
+ }
/**
* Register a telemetry provider with the telemetry service. This
@@ -201,7 +99,7 @@ define([
* @param {module:openmct.TelemetryAPI~TelemetryProvider} provider the new
* telemetry provider
*/
- TelemetryAPI.prototype.addProvider = function (provider) {
+ addProvider(provider) {
if (provider.supportsRequest) {
this.requestProviders.unshift(provider);
}
@@ -217,54 +115,54 @@ define([
if (provider.supportsLimits) {
this.limitProviders.unshift(provider);
}
- };
+ }
/**
* @private
*/
- TelemetryAPI.prototype.findSubscriptionProvider = function () {
+ findSubscriptionProvider() {
const args = Array.prototype.slice.apply(arguments);
function supportsDomainObject(provider) {
return provider.supportsSubscribe.apply(provider, args);
}
return this.subscriptionProviders.filter(supportsDomainObject)[0];
- };
+ }
/**
* @private
*/
- TelemetryAPI.prototype.findRequestProvider = function (domainObject) {
+ findRequestProvider(domainObject) {
const args = Array.prototype.slice.apply(arguments);
function supportsDomainObject(provider) {
return provider.supportsRequest.apply(provider, args);
}
return this.requestProviders.filter(supportsDomainObject)[0];
- };
+ }
/**
* @private
*/
- TelemetryAPI.prototype.findMetadataProvider = function (domainObject) {
+ findMetadataProvider(domainObject) {
return this.metadataProviders.filter(function (p) {
return p.supportsMetadata(domainObject);
})[0];
- };
+ }
/**
* @private
*/
- TelemetryAPI.prototype.findLimitEvaluator = function (domainObject) {
+ findLimitEvaluator(domainObject) {
return this.limitProviders.filter(function (p) {
return p.supportsLimits(domainObject);
})[0];
- };
+ }
/**
* @private
*/
- TelemetryAPI.prototype.standardizeRequestOptions = function (options) {
+ standardizeRequestOptions(options) {
if (!Object.prototype.hasOwnProperty.call(options, 'start')) {
options.start = this.openmct.time.bounds().start;
}
@@ -276,7 +174,47 @@ define([
if (!Object.prototype.hasOwnProperty.call(options, 'domain')) {
options.domain = this.openmct.time.timeSystem().key;
}
- };
+ }
+
+ /**
+ * Register a request interceptor that transforms a request via module:openmct.TelemetryAPI.request
+ * The request will be modifyed when it is received and will be returned in it's modified state
+ * The request will be transformed only if the interceptor is applicable to that domain object as defined by the RequestInterceptorDef
+ *
+ * @param {module:openmct.RequestInterceptorDef} requestInterceptorDef the request interceptor definition to add
+ * @method addRequestInterceptor
+ * @memberof module:openmct.TelemetryRequestInterceptorRegistry#
+ */
+ addRequestInterceptor(requestInterceptorDef) {
+ this.requestInterceptorRegistry.addInterceptor(requestInterceptorDef);
+ }
+
+ /**
+ * Retrieve the request interceptors for a given domain object.
+ * @private
+ */
+ #getInterceptorsForRequest(identifier, request) {
+ return this.requestInterceptorRegistry.getInterceptors(identifier, request);
+ }
+
+ /**
+ * Invoke interceptors if applicable for a given domain object.
+ */
+ async applyRequestInterceptors(domainObject, request) {
+ const interceptors = this.#getInterceptorsForRequest(domainObject.identifier, request);
+
+ if (interceptors.length === 0) {
+ return request;
+ }
+
+ let modifiedRequest = { ...request };
+
+ for (let interceptor of interceptors) {
+ modifiedRequest = await interceptor.invoke(modifiedRequest);
+ }
+
+ return modifiedRequest;
+ }
/**
* Request telemetry collection for a domain object.
@@ -292,13 +230,13 @@ define([
* options for this telemetry collection request
* @returns {TelemetryCollection} a TelemetryCollection instance
*/
- TelemetryAPI.prototype.requestCollection = function (domainObject, options = {}) {
+ requestCollection(domainObject, options = {}) {
return new TelemetryCollection(
this.openmct,
domainObject,
options
);
- };
+ }
/**
* Request historical telemetry for a domain object.
@@ -315,7 +253,7 @@ define([
* @returns {Promise.<object[]>} a promise for an array of
* telemetry data
*/
- TelemetryAPI.prototype.request = function (domainObject) {
+ async request(domainObject) {
if (this.noRequestProviderForAllObjects) {
return Promise.resolve([]);
}
@@ -330,6 +268,7 @@ define([
this.requestAbortControllers.add(abortController);
this.standardizeRequestOptions(arguments[1]);
+
const provider = this.findRequestProvider.apply(this, arguments);
if (!provider) {
this.requestAbortControllers.delete(abortController);
@@ -337,6 +276,8 @@ define([
return this.handleMissingRequestProvider(domainObject);
}
+ arguments[1] = await this.applyRequestInterceptors(domainObject, arguments[1]);
+
return provider.request.apply(provider, arguments)
.catch((rejected) => {
if (rejected.name !== 'AbortError') {
@@ -348,7 +289,7 @@ define([
}).finally(() => {
this.requestAbortControllers.delete(abortController);
});
- };
+ }
/**
* Subscribe to realtime telemetry for a specific domain object.
@@ -364,7 +305,7 @@ define([
* @returns {Function} a function which may be called to terminate
* the subscription
*/
- TelemetryAPI.prototype.subscribe = function (domainObject, callback, options) {
+ subscribe(domainObject, callback, options) {
const provider = this.findSubscriptionProvider(domainObject);
if (!this.subscribeCache) {
@@ -401,7 +342,7 @@ define([
delete this.subscribeCache[keyString];
}
}.bind(this);
- };
+ }
/**
* Get telemetry metadata for a given domain object. Returns a telemetry
@@ -410,7 +351,7 @@ define([
*
* @returns {TelemetryMetadataManager}
*/
- TelemetryAPI.prototype.getMetadata = function (domainObject) {
+ getMetadata(domainObject) {
if (!this.metadataCache.has(domainObject)) {
const metadataProvider = this.findMetadataProvider(domainObject);
if (!metadataProvider) {
@@ -426,14 +367,14 @@ define([
}
return this.metadataCache.get(domainObject);
- };
+ }
/**
* Return an array of valueMetadatas that are common to all supplied
* telemetry objects and match the requested hints.
*
*/
- TelemetryAPI.prototype.commonValuesForHints = function (metadatas, hints) {
+ commonValuesForHints(metadatas, hints) {
const options = metadatas.map(function (metadata) {
const values = metadata.valuesForHints(hints);
@@ -453,14 +394,14 @@ define([
});
return _.sortBy(options, sortKeys);
- };
+ }
/**
* Get a value formatter for a given valueMetadata.
*
* @returns {TelemetryValueFormatter}
*/
- TelemetryAPI.prototype.getValueFormatter = function (valueMetadata) {
+ getValueFormatter(valueMetadata) {
if (!this.valueFormatterCache.has(valueMetadata)) {
this.valueFormatterCache.set(
valueMetadata,
@@ -469,7 +410,7 @@ define([
}
return this.valueFormatterCache.get(valueMetadata);
- };
+ }
/**
* Get a value formatter for a given key.
@@ -477,9 +418,9 @@ define([
*
* @returns {Format}
*/
- TelemetryAPI.prototype.getFormatter = function (key) {
+ getFormatter(key) {
return this.formatters.get(key);
- };
+ }
/**
* Get a format map of all value formatters for a given piece of telemetry
@@ -487,7 +428,7 @@ define([
*
* @returns {Object<String, {TelemetryValueFormatter}>}
*/
- TelemetryAPI.prototype.getFormatMap = function (metadata) {
+ getFormatMap(metadata) {
if (!metadata) {
return {};
}
@@ -502,17 +443,17 @@ define([
}
return this.formatMapCache.get(metadata);
- };
+ }
/**
* Error Handling: Missing Request provider
*
* @returns Promise
*/
- TelemetryAPI.prototype.handleMissingRequestProvider = function (domainObject) {
+ handleMissingRequestProvider(domainObject) {
this.noRequestProviderForAllObjects = this.requestProviders.every(requestProvider => {
const supportsRequest = requestProvider.supportsRequest.apply(requestProvider, arguments);
- const hasRequestProvider = Object.hasOwn(requestProvider, 'request');
+ const hasRequestProvider = Object.prototype.hasOwnProperty.call(requestProvider, 'request') && typeof requestProvider.request === 'function';
return supportsRequest && hasRequestProvider;
});
@@ -529,18 +470,18 @@ define([
}
this.openmct.notifications.error(message);
- console.error(detailMessage);
+ console.warn(detailMessage);
return Promise.resolve([]);
- };
+ }
/**
* Register a new telemetry data formatter.
* @param {Format} format the
*/
- TelemetryAPI.prototype.addFormat = function (format) {
+ addFormat(format) {
this.formatters.set(format.key, format);
- };
+ }
/**
* Get a limit evaluator for this domain object.
@@ -558,9 +499,9 @@ define([
* @method limitEvaluator
* @memberof module:openmct.TelemetryAPI~TelemetryProvider#
*/
- TelemetryAPI.prototype.limitEvaluator = function (domainObject) {
+ limitEvaluator(domainObject) {
return this.getLimitEvaluator(domainObject);
- };
+ }
/**
* Get a limits for this domain object.
@@ -578,9 +519,9 @@ define([
* @method limits
* @memberof module:openmct.TelemetryAPI~TelemetryProvider#
*/
- TelemetryAPI.prototype.limitDefinition = function (domainObject) {
+ limitDefinition(domainObject) {
return this.getLimits(domainObject);
- };
+ }
/**
* Get a limit evaluator for this domain object.
@@ -598,7 +539,7 @@ define([
* @method limitEvaluator
* @memberof module:openmct.TelemetryAPI~TelemetryProvider#
*/
- TelemetryAPI.prototype.getLimitEvaluator = function (domainObject) {
+ getLimitEvaluator(domainObject) {
const provider = this.findLimitEvaluator(domainObject);
if (!provider) {
return {
@@ -607,7 +548,7 @@ define([
}
return provider.getLimitEvaluator(domainObject);
- };
+ }
/**
* Get a limit definitions for this domain object.
@@ -636,7 +577,7 @@ define([
* supported colors are purple, red, orange, yellow and cyan
* @memberof module:openmct.TelemetryAPI~TelemetryProvider#
*/
- TelemetryAPI.prototype.getLimits = function (domainObject) {
+ getLimits(domainObject) {
const provider = this.findLimitEvaluator(domainObject);
if (!provider || !provider.getLimits) {
return {
@@ -647,7 +588,104 @@ define([
}
return provider.getLimits(domainObject);
- };
+ }
+}
+
+/**
+ * A LimitEvaluator may be used to detect when telemetry values
+ * have exceeded nominal conditions.
+ *
+ * @interface LimitEvaluator
+ * @memberof module:openmct.TelemetryAPI~
+ */
+
+/**
+ * Check for any limit violations associated with a telemetry datum.
+ * @method evaluate
+ * @param {*} datum the telemetry datum to evaluate
+ * @param {TelemetryProperty} the property to check for limit violations
+ * @memberof module:openmct.TelemetryAPI~LimitEvaluator
+ * @returns {module:openmct.TelemetryAPI~LimitViolation} metadata about
+ * the limit violation, or undefined if a value is within limits
+ */
+
+/**
+ * A violation of limits defined for a telemetry property.
+ * @typedef LimitViolation
+ * @memberof {module:openmct.TelemetryAPI~}
+ * @property {string} cssClass the class (or space-separated classes) to
+ * apply to display elements for values which violate this limit
+ * @property {string} name the human-readable name for the limit violation
+ */
+
+/**
+ * A TelemetryFormatter converts telemetry values for purposes of
+ * display as text.
+ *
+ * @interface TelemetryFormatter
+ * @memberof module:openmct.TelemetryAPI~
+ */
+
+/**
+ * Retrieve the 'key' from the datum and format it accordingly to
+ * telemetry metadata in domain object.
+ *
+ * @method format
+ * @memberof module:openmct.TelemetryAPI~TelemetryFormatter#
+ */
- return TelemetryAPI;
-});
+/**
+ * Describes a property which would be found in a datum of telemetry
+ * associated with a particular domain object.
+ *
+ * @typedef TelemetryProperty
+ * @memberof module:openmct.TelemetryAPI~
+ * @property {string} key the name of the property in the datum which
+ * contains this telemetry value
+ * @property {string} name the human-readable name for this property
+ * @property {string} [units] the units associated with this property
+ * @property {boolean} [temporal] true if this property is a timestamp, or
+ * may be otherwise used to order telemetry in a time-like
+ * fashion; default is false
+ * @property {boolean} [numeric] true if the values for this property
+ * can be interpreted plainly as numbers; default is true
+ * @property {boolean} [enumerated] true if this property may have only
+ * certain specific values; default is false
+ * @property {string} [values] for enumerated states, an ordered list
+ * of possible values
+ */
+
+/**
+ * Describes and bounds requests for telemetry data.
+ *
+ * @typedef TelemetryRequest
+ * @memberof module:openmct.TelemetryAPI~
+ * @property {string} sort the key of the property to sort by. This may
+ * be prefixed with a "+" or a "-" sign to sort in ascending
+ * or descending order respectively. If no prefix is present,
+ * ascending order will be used.
+ * @property {*} start the lower bound for values of the sorting property
+ * @property {*} end the upper bound for values of the sorting property
+ * @property {string[]} strategies symbolic identifiers for strategies
+ * (such as `minmax`) which may be recognized by providers;
+ * these will be tried in order until an appropriate provider
+ * is found
+ */
+
+/**
+ * Provides telemetry data. To connect to new data sources, new
+ * TelemetryProvider implementations should be
+ * [registered]{@link module:openmct.TelemetryAPI#addProvider}.
+ *
+ * @interface TelemetryProvider
+ * @memberof module:openmct.TelemetryAPI~
+ */
+
+/**
+ * An interface for retrieving telemetry data associated with a domain
+ * object.
+ *
+ * @interface TelemetryAPI
+ * @augments module:openmct.TelemetryAPI~TelemetryProvider
+ * @memberof module:openmct
+ */
diff --git a/src/api/telemetry/TelemetryAPISpec.js b/src/api/telemetry/TelemetryAPISpec.js
index c79e65278..5af944009 100644
--- a/src/api/telemetry/TelemetryAPISpec.js
+++ b/src/api/telemetry/TelemetryAPISpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -21,7 +21,7 @@
*****************************************************************************/
import { createOpenMct, resetApplicationState } from 'utils/testing';
import TelemetryAPI from './TelemetryAPI';
-const { TelemetryCollection } = require("./TelemetryCollection");
+import TelemetryCollection from './TelemetryCollection';
describe('Telemetry API', function () {
let openmct;
diff --git a/src/api/telemetry/TelemetryCollection.js b/src/api/telemetry/TelemetryCollection.js
index f562d3e54..b15ed8331 100644
--- a/src/api/telemetry/TelemetryCollection.js
+++ b/src/api/telemetry/TelemetryCollection.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2020, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -22,16 +22,11 @@
import _ from 'lodash';
import EventEmitter from 'EventEmitter';
-
-const ERRORS = {
- TIMESYSTEM_KEY: 'All telemetry metadata must have a telemetry value with a key that matches the key of the active time system.',
- TIMESYSTEM_KEY_NOTIFICATION: 'Telemetry metadata does not match the active time system.',
- LOADED: 'Telemetry Collection has already been loaded.'
-};
+import { LOADED_ERROR, TIMESYSTEM_KEY_NOTIFICATION, TIMESYSTEM_KEY_WARNING } from './constants';
/** Class representing a Telemetry Collection. */
-export class TelemetryCollection extends EventEmitter {
+export default class TelemetryCollection extends EventEmitter {
/**
* Creates a Telemetry Collection
*
@@ -54,6 +49,7 @@ export class TelemetryCollection extends EventEmitter {
this.pageState = undefined;
this.lastBounds = undefined;
this.requestAbort = undefined;
+ this.isStrategyLatest = this.options.strategy === 'latest';
}
/**
@@ -62,7 +58,7 @@ export class TelemetryCollection extends EventEmitter {
*/
load() {
if (this.loaded) {
- this._error(ERRORS.LOADED);
+ this._error(LOADED_ERROR);
}
this._setTimeSystem(this.openmct.time.timeSystem());
@@ -131,7 +127,8 @@ export class TelemetryCollection extends EventEmitter {
this.requestAbort = new AbortController();
options.signal = this.requestAbort.signal;
this.emit('requestStarted');
- historicalData = await historicalProvider.request(this.domainObject, options);
+ const modifiedOptions = await this.openmct.telemetry.applyRequestInterceptors(this.domainObject, options);
+ historicalData = await historicalProvider.request(this.domainObject, modifiedOptions);
} catch (error) {
if (error.name !== 'AbortError') {
console.error('Error requesting telemetry data...');
@@ -177,12 +174,14 @@ export class TelemetryCollection extends EventEmitter {
return;
}
+ let latestBoundedDatum = this.boundedTelemetry[this.boundedTelemetry.length - 1];
let data = Array.isArray(telemetryData) ? telemetryData : [telemetryData];
let parsedValue;
let beforeStartOfBounds;
let afterEndOfBounds;
let added = [];
+ // loop through, sort and dedupe
for (let datum of data) {
parsedValue = this.parseTime(datum);
beforeStartOfBounds = parsedValue < this.lastBounds.start;
@@ -222,7 +221,17 @@ export class TelemetryCollection extends EventEmitter {
}
if (added.length) {
- this.emit('add', added);
+ // if latest strategy is requested, we need to check if the value is the latest unmitted value
+ if (this.isStrategyLatest) {
+ this.boundedTelemetry = [this.boundedTelemetry[this.boundedTelemetry.length - 1]];
+
+ // if true, then this value has yet to be emitted
+ if (this.boundedTelemetry[0] !== latestBoundedDatum) {
+ this.emit('add', this.boundedTelemetry);
+ }
+ } else {
+ this.emit('add', added);
+ }
}
}
@@ -282,13 +291,20 @@ export class TelemetryCollection extends EventEmitter {
if (startChanged) {
testDatum[this.timeKey] = bounds.start;
- // Calculate the new index of the first item within the bounds
- startIndex = _.sortedIndexBy(
- this.boundedTelemetry,
- testDatum,
- datum => this.parseTime(datum)
- );
- discarded = this.boundedTelemetry.splice(0, startIndex);
+
+ // a little more complicated if not latest strategy
+ if (!this.isStrategyLatest) {
+ // Calculate the new index of the first item within the bounds
+ startIndex = _.sortedIndexBy(
+ this.boundedTelemetry,
+ testDatum,
+ datum => this.parseTime(datum)
+ );
+ discarded = this.boundedTelemetry.splice(0, startIndex);
+ } else if (this.parseTime(testDatum) > this.parseTime(this.boundedTelemetry[0])) {
+ discarded = this.boundedTelemetry;
+ this.boundedTelemetry = [];
+ }
}
if (endChanged) {
@@ -300,7 +316,6 @@ export class TelemetryCollection extends EventEmitter {
datum => this.parseTime(datum)
);
added = this.futureBuffer.splice(0, endIndex);
- this.boundedTelemetry = [...this.boundedTelemetry, ...added];
}
if (discarded.length > 0) {
@@ -308,6 +323,13 @@ export class TelemetryCollection extends EventEmitter {
}
if (added.length > 0) {
+ if (!this.isStrategyLatest) {
+ this.boundedTelemetry = [...this.boundedTelemetry, ...added];
+ } else {
+ added = [added[added.length - 1]];
+ this.boundedTelemetry = added;
+ }
+
this.emit('add', added);
}
} else {
@@ -326,18 +348,26 @@ export class TelemetryCollection extends EventEmitter {
* @private
*/
_setTimeSystem(timeSystem) {
- let domains = this.metadata.valuesForHints(['domain']);
+ let domains = [];
+ let metadataValue = { format: timeSystem.key };
+
+ if (this.metadata) {
+ domains = this.metadata.valuesForHints(['domain']);
+ metadataValue = this.metadata.value(timeSystem.key) || { format: timeSystem.key };
+ }
+
let domain = domains.find((d) => d.key === timeSystem.key);
if (domain !== undefined) {
// timeKey is used to create a dummy datum used for sorting
this.timeKey = domain.source;
} else {
- this._warn(ERRORS.TIMESYSTEM_KEY);
- this.openmct.notifications.alert(ERRORS.TIMESYSTEM_KEY_NOTIFICATION);
+ this.timeKey = undefined;
+
+ this._warn(TIMESYSTEM_KEY_WARNING);
+ this.openmct.notifications.alert(TIMESYSTEM_KEY_NOTIFICATION);
}
- let metadataValue = this.metadata.value(timeSystem.key) || { format: timeSystem.key };
let valueFormatter = this.openmct.telemetry.getValueFormatter(metadataValue);
this.parseTime = (datum) => {
diff --git a/src/api/telemetry/TelemetryCollectionSpec.js b/src/api/telemetry/TelemetryCollectionSpec.js
new file mode 100644
index 000000000..49907c3f5
--- /dev/null
+++ b/src/api/telemetry/TelemetryCollectionSpec.js
@@ -0,0 +1,101 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+import {
+ createOpenMct,
+ resetApplicationState
+} from 'utils/testing';
+import { TIMESYSTEM_KEY_WARNING } from './constants';
+
+describe('Telemetry Collection', () => {
+ let openmct;
+ let mockMetadataProvider;
+ let mockMetadata = {};
+ let domainObject;
+
+ beforeEach(done => {
+ openmct = createOpenMct();
+ openmct.on('start', done);
+
+ domainObject = {
+ identifier: {
+ key: 'a',
+ namespace: 'b'
+ },
+ type: 'sample-type'
+ };
+
+ mockMetadataProvider = {
+ key: 'mockMetadataProvider',
+ supportsMetadata() {
+ return true;
+ },
+ getMetadata() {
+ return mockMetadata;
+ }
+ };
+
+ openmct.telemetry.addProvider(mockMetadataProvider);
+ openmct.startHeadless();
+ });
+
+ afterEach(() => {
+ return resetApplicationState();
+ });
+
+ it('Warns if telemetry metadata does not match the active timesystem', () => {
+ mockMetadata.values = [
+ {
+ key: 'foo',
+ name: 'Bar',
+ hints: {
+ domain: 1
+ }
+ }
+ ];
+
+ const telemetryCollection = openmct.telemetry.requestCollection(domainObject);
+ spyOn(telemetryCollection, '_warn');
+ telemetryCollection.load();
+
+ expect(telemetryCollection._warn).toHaveBeenCalledOnceWith(TIMESYSTEM_KEY_WARNING);
+ });
+
+ it('Does not warn if telemetry metadata matches the active timesystem', () => {
+ mockMetadata.values = [
+ {
+ key: 'utc',
+ name: 'Timestamp',
+ format: 'utc',
+ hints: {
+ domain: 1
+ }
+ }
+ ];
+
+ const telemetryCollection = openmct.telemetry.requestCollection(domainObject);
+ spyOn(telemetryCollection, '_warn');
+ telemetryCollection.load();
+
+ expect(telemetryCollection._warn).not.toHaveBeenCalled();
+ });
+});
diff --git a/src/api/telemetry/TelemetryMetadataManager.js b/src/api/telemetry/TelemetryMetadataManager.js
index b002f9e19..4d9761e3f 100644
--- a/src/api/telemetry/TelemetryMetadataManager.js
+++ b/src/api/telemetry/TelemetryMetadataManager.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -121,6 +121,18 @@ define([
return _.sortBy(matchingMetadata, ...iteratees);
};
+ /**
+ * check out of a given metadata has array values
+ */
+ TelemetryMetadataManager.prototype.isArrayValue = function (metadata) {
+ const regex = /\[\]$/g;
+ if (!metadata.format && !metadata.formatString) {
+ return false;
+ }
+
+ return (metadata.format || metadata.formatString).match(regex) !== null;
+ };
+
TelemetryMetadataManager.prototype.getFilterableValues = function () {
return this.valueMetadatas.filter(metadatum => metadatum.filters && metadatum.filters.length > 0);
};
@@ -138,7 +150,7 @@ define([
valueMetadata = this.values()[0];
}
- return valueMetadata.key;
+ return valueMetadata;
};
return TelemetryMetadataManager;
diff --git a/src/api/telemetry/TelemetryRequestInterceptor.js b/src/api/telemetry/TelemetryRequestInterceptor.js
new file mode 100644
index 000000000..7204ee332
--- /dev/null
+++ b/src/api/telemetry/TelemetryRequestInterceptor.js
@@ -0,0 +1,68 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+export default class TelemetryRequestInterceptorRegistry {
+ /**
+ * A TelemetryRequestInterceptorRegistry maintains the definitions for different interceptors that may be invoked on telemetry
+ * requests.
+ * @interface TelemetryRequestInterceptorRegistry
+ * @memberof module:openmct
+ */
+ constructor() {
+ this.interceptors = [];
+ }
+
+ /**
+ * @interface TelemetryRequestInterceptorDef
+ * @property {function} appliesTo function that determines if this interceptor should be called for the given identifier/request
+ * @property {function} invoke function that transforms the provided request and returns the transformed request
+ * @property {function} priority the priority for this interceptor. A higher number returned has more weight than a lower number
+ * @memberof module:openmct TelemetryRequestInterceptorRegistry#
+ */
+
+ /**
+ * Register a new telemetry request interceptor.
+ *
+ * @param {module:openmct.RequestInterceptorDef} requestInterceptorDef the interceptor to add
+ * @method addInterceptor
+ * @memberof module:openmct.TelemetryRequestInterceptorRegistry#
+ */
+ addInterceptor(interceptorDef) {
+ //TODO: sort by priority
+ this.interceptors.push(interceptorDef);
+ }
+
+ /**
+ * Retrieve all interceptors applicable to a domain object/request.
+ * @method getInterceptors
+ * @returns [module:openmct.RequestInterceptorDef] the registered interceptors for this identifier/request
+ * @memberof module:openmct.TelemetryRequestInterceptorRegistry#
+ */
+ getInterceptors(identifier, request) {
+ return this.interceptors.filter(interceptor => {
+ return typeof interceptor.appliesTo === 'function'
+ && interceptor.appliesTo(identifier, request);
+ });
+ }
+
+}
+
diff --git a/src/api/telemetry/TelemetryValueFormatter.js b/src/api/telemetry/TelemetryValueFormatter.js
index 148b08366..3e8c0b62c 100644
--- a/src/api/telemetry/TelemetryValueFormatter.js
+++ b/src/api/telemetry/TelemetryValueFormatter.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -43,9 +43,23 @@ define([
};
this.valueMetadata = valueMetadata;
- this.formatter = formatMap.get(valueMetadata.format) || numberFormatter;
- if (valueMetadata.format === 'enum') {
+ 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;
+ }
+
+ 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;
+
+ if (valueMetadataFormat === 'enum') {
this.formatter = {};
this.enumerations = valueMetadata.enumerations.reduce(function (vm, e) {
vm.byValue[e.value] = e.string;
@@ -77,13 +91,13 @@ define([
// Check for formatString support once instead of per format call.
if (valueMetadata.formatString) {
const baseFormat = this.formatter.format;
- const formatString = valueMetadata.formatString;
+ const formatString = getNonArrayValue(valueMetadata.formatString);
this.formatter.format = function (value) {
return printj.sprintf(formatString, baseFormat.call(this, value));
};
}
- if (valueMetadata.format === 'string') {
+ if (valueMetadataFormat === 'string') {
this.formatter.parse = function (value) {
if (value === undefined) {
return '';
@@ -108,7 +122,14 @@ define([
TelemetryValueFormatter.prototype.parse = function (datum) {
if (_.isObject(datum)) {
- return this.formatter.parse(datum[this.valueMetadata.source]);
+ const objectDatum = 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.parse(datum);
@@ -116,7 +137,14 @@ define([
TelemetryValueFormatter.prototype.format = function (datum) {
if (_.isObject(datum)) {
- return this.formatter.format(datum[this.valueMetadata.source]);
+ const objectDatum = datum[this.valueMetadata.source];
+ if (Array.isArray(objectDatum)) {
+ return objectDatum.map((item) => {
+ return this.formatter.format(item);
+ });
+ } else {
+ return this.formatter.format(objectDatum);
+ }
}
return this.formatter.format(datum);
diff --git a/platform/entanglement/src/actions/CancelError.js b/src/api/telemetry/constants.js
index 31f00aaf5..66b5c50bb 100644
--- a/platform/entanglement/src/actions/CancelError.js
+++ b/src/api/telemetry/constants.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -20,13 +20,6 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
-define(function () {
- function CancelError() {
- Error.apply(this, arguments);
- this.name = CancelError;
- }
-
- CancelError.prototype = Object.create(Error.prototype);
-
- return CancelError;
-});
+export const TIMESYSTEM_KEY_WARNING = 'All telemetry metadata must have a telemetry value with a key that matches the key of the active time system.';
+export const TIMESYSTEM_KEY_NOTIFICATION = 'Telemetry metadata does not match the active time system.';
+export const LOADED_ERROR = 'Telemetry Collection has already been loaded.';
diff --git a/src/api/time/GlobalTimeContext.js b/src/api/time/GlobalTimeContext.js
index 8ad5a2472..9f55c2221 100644
--- a/src/api/time/GlobalTimeContext.js
+++ b/src/api/time/GlobalTimeContext.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/api/time/IndependentTimeContext.js b/src/api/time/IndependentTimeContext.js
index f98dcac7d..1ff6d6a54 100644
--- a/src/api/time/IndependentTimeContext.js
+++ b/src/api/time/IndependentTimeContext.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/api/time/TimeAPI.js b/src/api/time/TimeAPI.js
index abe487cdf..c7e3ddeff 100644
--- a/src/api/time/TimeAPI.js
+++ b/src/api/time/TimeAPI.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/api/time/TimeAPISpec.js b/src/api/time/TimeAPISpec.js
index 97bd583f5..c7dea1081 100644
--- a/src/api/time/TimeAPISpec.js
+++ b/src/api/time/TimeAPISpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/api/time/TimeContext.js b/src/api/time/TimeContext.js
index cbd38fcd0..29f95b72a 100644
--- a/src/api/time/TimeContext.js
+++ b/src/api/time/TimeContext.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -365,3 +365,7 @@ class TimeContext extends EventEmitter {
}
export default TimeContext;
+
+/**
+@typedef {{start: number, end: number}} Bounds
+*/
diff --git a/src/api/time/independentTimeAPISpec.js b/src/api/time/independentTimeAPISpec.js
index 485637d9c..0e8f215eb 100644
--- a/src/api/time/independentTimeAPISpec.js
+++ b/src/api/time/independentTimeAPISpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/api/types/Type.js b/src/api/types/Type.js
index 7753f20fd..2c1890f9b 100644
--- a/src/api/types/Type.js
+++ b/src/api/types/Type.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/api/types/TypeRegistry.js b/src/api/types/TypeRegistry.js
index 9ab6a916e..cb8856a99 100644
--- a/src/api/types/TypeRegistry.js
+++ b/src/api/types/TypeRegistry.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/api/types/TypeRegistrySpec.js b/src/api/types/TypeRegistrySpec.js
index e7148bd29..0618e0b00 100644
--- a/src/api/types/TypeRegistrySpec.js
+++ b/src/api/types/TypeRegistrySpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/api/user/StatusAPI.js b/src/api/user/StatusAPI.js
new file mode 100644
index 000000000..9c1318c38
--- /dev/null
+++ b/src/api/user/StatusAPI.js
@@ -0,0 +1,295 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+import EventEmitter from "EventEmitter";
+
+export default class StatusAPI extends EventEmitter {
+ #userAPI;
+ #openmct;
+
+ constructor(userAPI, openmct) {
+ super();
+ this.#userAPI = userAPI;
+ this.#openmct = openmct;
+
+ this.onProviderStatusChange = this.onProviderStatusChange.bind(this);
+ this.onProviderPollQuestionChange = this.onProviderPollQuestionChange.bind(this);
+ this.listenToStatusEvents = this.listenToStatusEvents.bind(this);
+
+ this.#openmct.once('destroy', () => {
+ const provider = this.#userAPI.getProvider();
+
+ if (typeof provider?.off === 'function') {
+ provider.off('statusChange', this.onProviderStatusChange);
+ provider.off('pollQuestionChange', this.onProviderPollQuestionChange);
+ }
+ });
+
+ this.#userAPI.on('providerAdded', this.listenToStatusEvents);
+ }
+
+ /**
+ * Fetch the currently defined operator status poll question. When presented with a status poll question, all operators will reply with their current status.
+ * @returns {Promise<PollQuestion>}
+ */
+ getPollQuestion() {
+ const provider = this.#userAPI.getProvider();
+
+ if (provider.getPollQuestion) {
+ return provider.getPollQuestion();
+ } else {
+ this.#userAPI.error("User provider does not support polling questions");
+ }
+ }
+
+ /**
+ * Set a poll question for operators to respond to. When presented with a status poll question, all operators will reply with their current status.
+ * @param {String} questionText - The text of the question
+ * @returns {Promise<Boolean>} true if operation was successful, otherwise false.
+ */
+ async setPollQuestion(questionText) {
+ const canSetPollQuestion = await this.canSetPollQuestion();
+
+ if (canSetPollQuestion) {
+ const provider = this.#userAPI.getProvider();
+
+ const result = await provider.setPollQuestion(questionText);
+
+ try {
+ await this.resetAllStatuses();
+ } catch (error) {
+ console.warn("Poll question set but unable to clear operator statuses.");
+ console.error(error);
+ }
+
+ return result;
+ } else {
+ this.#userAPI.error("User provider does not support setting polling question");
+ }
+ }
+
+ /**
+ * Can the currently logged in user set the operator status poll question.
+ * @returns {Promise<Boolean>}
+ */
+ canSetPollQuestion() {
+ const provider = this.#userAPI.getProvider();
+
+ if (provider.canSetPollQuestion) {
+ return provider.canSetPollQuestion();
+ } else {
+ return Promise.resolve(false);
+ }
+ }
+
+ /**
+ * @returns {Promise<Array<Status>>} the complete list of possible states that an operator can reply to a poll question with.
+ */
+ async getPossibleStatuses() {
+ const provider = this.#userAPI.getProvider();
+
+ if (provider.getPossibleStatuses) {
+ const possibleStatuses = await provider.getPossibleStatuses() || [];
+
+ return possibleStatuses.map(status => status);
+ } else {
+ this.#userAPI.error("User provider cannot provide statuses");
+ }
+ }
+
+ /**
+ * @param {import("./UserAPI").Role} role The role to fetch the current status for.
+ * @returns {Promise<Status>} the current status of the provided role
+ */
+ async getStatusForRole(role) {
+ const provider = this.#userAPI.getProvider();
+
+ if (provider.getStatusForRole) {
+ const status = await provider.getStatusForRole(role);
+
+ return status;
+ } else {
+ this.#userAPI.error("User provider does not support role status");
+ }
+ }
+
+ /**
+ * @param {import("./UserAPI").Role} role
+ * @returns {Promise<Boolean>} true if the configured UserProvider can provide status for the given role
+ * @see StatusUserProvider
+ */
+ canProvideStatusForRole(role) {
+ const provider = this.#userAPI.getProvider();
+
+ if (provider.canProvideStatusForRole) {
+ return provider.canProvideStatusForRole(role);
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * @param {import("./UserAPI").Role} role The role to set the status for.
+ * @param {Status} status The status to set for the provided role
+ * @returns {Promise<Boolean>} true if operation was successful, otherwise false.
+ */
+ setStatusForRole(role, status) {
+ const provider = this.#userAPI.getProvider();
+
+ if (provider.setStatusForRole) {
+ return provider.setStatusForRole(role, status);
+ } else {
+ this.#userAPI.error("User provider does not support setting role status");
+ }
+ }
+
+ /**
+ * Resets the status of the provided role back to its default status.
+ * @param {import("./UserAPI").Role} role The role to set the status for.
+ * @returns {Promise<Boolean>} true if operation was successful, otherwise false.
+ */
+ async resetStatusForRole(role) {
+ const provider = this.#userAPI.getProvider();
+ const defaultStatus = await this.getDefaultStatusForRole(role);
+
+ if (provider.setStatusForRole) {
+ return provider.setStatusForRole(role, defaultStatus);
+ } else {
+ this.#userAPI.error("User provider does not support resetting role status");
+ }
+ }
+
+ /**
+ * Resets the status of all operators to their default status
+ * @returns {Promise<Boolean>} true if operation was successful, otherwise false.
+ */
+ async resetAllStatuses() {
+ const allStatusRoles = await this.getAllStatusRoles();
+
+ return Promise.all(allStatusRoles.map(role => this.resetStatusForRole(role)));
+ }
+
+ /**
+ * The default status. This is the status that will be used before the user has selected any status.
+ * @param {import("./UserAPI").Role} role
+ * @returns {Promise<Status>} the default operator status if no other has been set.
+ */
+ async getDefaultStatusForRole(role) {
+ const provider = this.#userAPI.getProvider();
+ const defaultStatus = await provider.getDefaultStatusForRole(role);
+
+ return defaultStatus;
+ }
+
+ /**
+ * All possible status roles. A status role is a user role that can provide status. In some systems
+ * this may be all user roles, but there may be cases where some users are not are not polled
+ * for status if they do not have a real-time operational role.
+ *
+ * @returns {Promise<Array<import("./UserAPI").Role>>} the default operator status if no other has been set.
+ */
+ getAllStatusRoles() {
+ const provider = this.#userAPI.getProvider();
+
+ if (provider.getAllStatusRoles) {
+ return provider.getAllStatusRoles();
+ } else {
+ this.#userAPI.error("User provider cannot provide all status roles");
+ }
+ }
+
+ /**
+ * The status role of the current user. A user may have multiple roles, but will only have one role
+ * that provides status at any time.
+ * @returns {Promise<import("./UserAPI").Role>} the role for which the current user can provide status.
+ */
+ getStatusRoleForCurrentUser() {
+ const provider = this.#userAPI.getProvider();
+
+ if (provider.getStatusRoleForCurrentUser) {
+ return provider.getStatusRoleForCurrentUser();
+ } else {
+ this.#userAPI.error("User provider cannot provide role status for this user");
+ }
+ }
+
+ /**
+ * @returns {Promise<Boolean>} true if the configured UserProvider can provide status for the currently logged in user, false otherwise.
+ * @see StatusUserProvider
+ */
+ async canProvideStatusForCurrentUser() {
+ const provider = this.#userAPI.getProvider();
+
+ if (provider.getStatusRoleForCurrentUser) {
+ const activeStatusRole = await this.#userAPI.getProvider().getStatusRoleForCurrentUser();
+ const canProvideStatus = await this.canProvideStatusForRole(activeStatusRole);
+
+ return canProvideStatus;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Private internal function that cannot be made #private because it needs to be registered as a callback to the user provider
+ * @private
+ */
+ listenToStatusEvents(provider) {
+ if (typeof provider.on === 'function') {
+ provider.on('statusChange', this.onProviderStatusChange);
+ provider.on('pollQuestionChange', this.onProviderPollQuestionChange);
+ }
+ }
+
+ /**
+ * @private
+ */
+ onProviderStatusChange(newStatus) {
+ this.emit('statusChange', newStatus);
+ }
+
+ /**
+ * @private
+ */
+ onProviderPollQuestionChange(pollQuestion) {
+ this.emit('pollQuestionChange', pollQuestion);
+ }
+}
+
+/**
+ * @typedef {import('./UserProvider')} UserProvider
+ */
+/**
+ * @typedef {import('./StatusUserProvider')} StatusUserProvider
+ */
+/**
+ * The PollQuestion type
+ * @typedef {Object} PollQuestion
+ * @property {String} question - The question to be presented to users
+ * @property {Number} timestamp - The time that the poll question was set.
+ */
+
+/**
+ * The Status type
+ * @typedef {Object} Status
+ * @property {String} key - A unique identifier for this status
+ * @property {Number} label - A human readable label for this status
+ */
diff --git a/src/api/user/StatusUserProvider.js b/src/api/user/StatusUserProvider.js
new file mode 100644
index 000000000..b474fdbed
--- /dev/null
+++ b/src/api/user/StatusUserProvider.js
@@ -0,0 +1,81 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+import UserProvider from "./UserProvider";
+
+export default class StatusUserProvider extends UserProvider {
+ /**
+ * @param {('statusChange'|'pollQuestionChange')} event the name of the event to listen to
+ * @param {Function} callback a function to invoke when this event occurs
+ */
+ on(event, callback) {}
+ /**
+ * @param {('statusChange'|'pollQuestionChange')} event the name of the event to stop listen to
+ * @param {Function} callback the callback function used to register the listener
+ */
+ off(event, callback) {}
+ /**
+ * @returns {import("./StatusAPI").PollQuestion} the current status poll question
+ */
+ async getPollQuestion() {}
+ /**
+ * @param {import("./StatusAPI").PollQuestion} pollQuestion a new poll question to set
+ * @returns {Promise<Boolean>} true if operation was successful, otherwise false
+ */
+ async setPollQuestion(pollQuestion) {}
+ /**
+ * @returns {Promise<Boolean>} true if the current user can set the poll question, otherwise false
+ */
+ async canSetPollQuestion() {}
+ /**
+ * @returns {Promise<Array<import("./StatusAPI").Status>>} a list of the possible statuses that an operator can be in
+ */
+ async getPossibleStatuses() {}
+ /**
+ * @param {import("./UserAPI").Role} role
+ * @returns {Promise<import("./StatusAPI").Status}
+ */
+ async getStatusForRole(role) {}
+ /**
+ * @param {import("./UserAPI").Role} role
+ * @returns {Promise<import("./StatusAPI").Status}
+ */
+ async getDefaultStatusForRole(role) {}
+ /**
+ * @param {import("./UserAPI").Role} role
+ * @param {*} status
+ * @returns {Promise<Boolean>} true if operation was successful, otherwise false.
+ */
+ async setStatusForRole(role, status) {}
+ /**
+ * @param {import("./UserAPI").Role} role
+ * @returns {Promise<Boolean} true if the user provider can provide status for the given role
+ */
+ async canProvideStatusForRole(role) {}
+ /**
+ * @returns {Promise<Array<import("./UserAPI").Role>>} a list of all available status roles, if user permissions allow it.
+ */
+ async getAllStatusRoles() {}
+ /**
+ * @returns {Promise<import("./UserAPI").Role>} the active status role for the currently logged in user
+ */
+ async getStatusRoleForCurrentUser() {}
+}
diff --git a/platform/commonUI/mobile/src/AgentService.js b/src/api/user/User.js
index 33c0b99a1..f4400df2f 100644
--- a/platform/commonUI/mobile/src/AgentService.js
+++ b/src/api/user/User.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -20,12 +20,20 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
-define(["../../../../src/utils/agent/Agent.js"], function (Agent) {
- function AngularAgentServiceWrapper(window) {
- const AS = Agent.default;
+export default class User {
+ constructor(id, name) {
+ this.id = id;
+ this.name = name;
- return new AS(window);
+ this.getId = this.getId.bind(this);
+ this.getName = this.getName.bind(this);
}
- return AngularAgentServiceWrapper;
-});
+ getId() {
+ return this.id;
+ }
+
+ getName() {
+ return this.name;
+ }
+}
diff --git a/src/api/user/UserAPI.js b/src/api/user/UserAPI.js
new file mode 100644
index 000000000..0ab2d9156
--- /dev/null
+++ b/src/api/user/UserAPI.js
@@ -0,0 +1,163 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+import EventEmitter from 'EventEmitter';
+import {
+ MULTIPLE_PROVIDER_ERROR,
+ NO_PROVIDER_ERROR
+} from './constants';
+import StatusAPI from './StatusAPI';
+import User from './User';
+
+class UserAPI extends EventEmitter {
+ /**
+ * @param {OpenMCT} openmct
+ * @param {UserAPIConfiguration} config
+ */
+ constructor(openmct, config) {
+ super();
+
+ this._openmct = openmct;
+ this._provider = undefined;
+
+ this.User = User;
+ this.status = new StatusAPI(this, openmct, config);
+ }
+
+ /**
+ * Set the user provider for the user API. This allows you
+ * to specifiy ONE user provider to be used with Open MCT.
+ * @method setProvider
+ * @memberof module:openmct.UserAPI#
+ * @param {module:openmct.UserAPI~UserProvider} provider the new
+ * user provider
+ */
+ setProvider(provider) {
+ if (this.hasProvider()) {
+ this.error(MULTIPLE_PROVIDER_ERROR);
+ }
+
+ this._provider = provider;
+ this.emit('providerAdded', this._provider);
+ }
+
+ getProvider() {
+ return this._provider;
+ }
+
+ /**
+ * Return true if the user provider has been set.
+ *
+ * @memberof module:openmct.UserAPI#
+ * @returns {boolean} true if the user provider exists
+ */
+ hasProvider() {
+ return this._provider !== undefined;
+ }
+
+ /**
+ * If a user provider is set, it will return a copy of a user object from
+ * the provider. If the user is not logged in, it will return undefined;
+ *
+ * @memberof module:openmct.UserAPI#
+ * @returns {Function|Promise} user provider 'getCurrentUser' method
+ * @throws Will throw an error if no user provider is set
+ */
+ getCurrentUser() {
+ this.noProviderCheck();
+
+ return this._provider.getCurrentUser();
+ }
+
+ /**
+ * If a user provider is set, it will return the user provider's
+ * 'isLoggedIn' method
+ *
+ * @memberof module:openmct.UserAPI#
+ * @returns {Function|Boolean} user provider 'isLoggedIn' method
+ * @throws Will throw an error if no user provider is set
+ */
+ isLoggedIn() {
+ if (!this.hasProvider()) {
+ return false;
+ }
+
+ return this._provider.isLoggedIn();
+ }
+
+ /**
+ * If a user provider is set, it will return a call to it's
+ * 'hasRole' method
+ *
+ * @memberof module:openmct.UserAPI#
+ * @returns {Function|Boolean} user provider 'isLoggedIn' method
+ * @param {string} roleId id of role to check for
+ * @throws Will throw an error if no user provider is set
+ */
+ hasRole(roleId) {
+ this.noProviderCheck();
+
+ return this._provider.hasRole(roleId);
+ }
+
+ /**
+ * Checks if a provider is set and if not, will throw error
+ *
+ * @private
+ * @throws Will throw an error if no user provider is set
+ */
+ noProviderCheck() {
+ if (!this.hasProvider()) {
+ this.error(NO_PROVIDER_ERROR);
+ }
+ }
+
+ /**
+ * Utility function for throwing errors
+ *
+ * @private
+ * @param {string} error description of error
+ * @throws Will throw error passed in
+ */
+ error(error) {
+ throw new Error(error);
+ }
+}
+
+export default UserAPI;
+/**
+ * @typedef {String} Role
+ */
+/**
+ * @typedef {Object} OpenMCT
+ */
+/**
+ * @typedef {{statusStyles: Object.<string, StatusStyleDefinition>}} UserAPIConfiguration
+ */
+/**
+ * @typedef {Object} StatusStyleDefinition
+ * @property {String} iconClass The icon class to apply to the status indicator when this status is active "icon-circle-slash",
+ * @property {String} iconClassPoll The icon class to apply to the poll question indicator when this style is active eg. "icon-status-poll-question-mark"
+ * @property {String} statusClass The class to apply to the indicator when this status is active eg. "s-status-error"
+ * @property {String} statusBgColor The background color to apply in the status summary section of the poll question popup for this status eg."#9900cc"
+ * @property {String} statusFgColor The foreground color to apply in the status summary section of the poll question popup for this status eg. "#fff"
+ */
diff --git a/src/api/user/UserAPISpec.js b/src/api/user/UserAPISpec.js
new file mode 100644
index 000000000..d85bd8c04
--- /dev/null
+++ b/src/api/user/UserAPISpec.js
@@ -0,0 +1,114 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+import {
+ createOpenMct,
+ resetApplicationState
+} from '../../utils/testing';
+import {
+ MULTIPLE_PROVIDER_ERROR
+} from './constants';
+import ExampleUserProvider from '../../../example/exampleUser/ExampleUserProvider';
+
+const USERNAME = 'Test User';
+const EXAMPLE_ROLE = 'example-role';
+
+describe("The User API", () => {
+ let openmct;
+
+ beforeEach(() => {
+ openmct = createOpenMct();
+ });
+
+ afterEach(() => {
+ const activeOverlays = openmct.overlays.activeOverlays;
+ activeOverlays.forEach(overlay => overlay.dismiss());
+
+ return resetApplicationState(openmct);
+ });
+
+ describe('with regard to user providers', () => {
+ it('allows you to specify a user provider', () => {
+ openmct.user.on('providerAdded', (provider) => {
+ expect(provider).toBeInstanceOf(ExampleUserProvider);
+ });
+ openmct.user.setProvider(new ExampleUserProvider(openmct));
+ });
+
+ it('prevents more than one user provider from being set', () => {
+ openmct.user.setProvider(new ExampleUserProvider(openmct));
+
+ expect(() => {
+ openmct.user.setProvider({});
+ }).toThrow(new Error(MULTIPLE_PROVIDER_ERROR));
+ });
+
+ it('provides a check for an existing user provider', () => {
+ expect(openmct.user.hasProvider()).toBeFalse();
+
+ openmct.user.setProvider(new ExampleUserProvider(openmct));
+
+ expect(openmct.user.hasProvider()).toBeTrue();
+ });
+ });
+
+ describe('provides the ability', () => {
+ let provider;
+
+ beforeEach(() => {
+ provider = new ExampleUserProvider(openmct);
+ provider.autoLogin(USERNAME);
+ });
+
+ it('to check if a user (not specific) is loged in', (done) => {
+ expect(openmct.user.isLoggedIn()).toBeFalse();
+
+ openmct.user.on('providerAdded', () => {
+ expect(openmct.user.isLoggedIn()).toBeTrue();
+ done();
+ });
+
+ // this will trigger the user indicator plugin,
+ // which will in turn login the user
+ openmct.user.setProvider(provider);
+ });
+
+ it('to get the current user', (done) => {
+ openmct.user.setProvider(provider);
+ openmct.user.getCurrentUser().then((apiUser) => {
+ expect(apiUser.name).toEqual(USERNAME);
+ }).finally(done);
+ });
+
+ it('to check if a user has a specific role (by id)', (done) => {
+ openmct.user.setProvider(provider);
+ let junkIdCheckPromise = openmct.user.hasRole('junk-id').then((hasRole) => {
+ expect(hasRole).toBeFalse();
+ });
+ let realIdCheckPromise = openmct.user.hasRole(EXAMPLE_ROLE).then((hasRole) => {
+ expect(hasRole).toBeTrue();
+ });
+
+ Promise.all([junkIdCheckPromise, realIdCheckPromise]).finally(done);
+ });
+ });
+});
diff --git a/src/plugins/nonEditableFolder/plugin.js b/src/api/user/UserProvider.js
index 46212b8ec..8502dd54e 100644
--- a/src/plugins/nonEditableFolder/plugin.js
+++ b/src/api/user/UserProvider.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -19,15 +19,18 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
-
-export default function () {
- return function (openmct) {
- openmct.types.addType("noneditable.folder", {
- name: "Non-Editable Folder",
- key: "noneditable.folder",
- description: "Create folders to organize other objects or links to objects without the ability to edit it's properties.",
- cssClass: "icon-folder",
- creatable: false
- });
- };
+export default class UserProvider {
+ /**
+ * @returns {Promise<User>} A promise that resolves with the currently logged in user
+ */
+ getCurrentUser() {}
+ /**
+ * @returns {Boolean} true if a user is currently logged in, otherwise false
+ */
+ isLoggedIn() {}
+ /**
+ * @param {String} role
+ * @returns {Promise<Boolean>} true if the current user has the given role
+ */
+ hasRole(role) {}
}
diff --git a/src/api/user/UserStatusAPISpec.js b/src/api/user/UserStatusAPISpec.js
new file mode 100644
index 000000000..30df2820c
--- /dev/null
+++ b/src/api/user/UserStatusAPISpec.js
@@ -0,0 +1,103 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+import {
+ createOpenMct,
+ resetApplicationState
+} from '../../utils/testing';
+
+describe("The User Status API", () => {
+ let openmct;
+ let userProvider;
+ let mockUser;
+
+ beforeEach(() => {
+ userProvider = jasmine.createSpyObj("userProvider", [
+ "setPollQuestion",
+ "getPollQuestion",
+ "getCurrentUser",
+ "getPossibleStatuses",
+ "getAllStatusRoles",
+ "canSetPollQuestion",
+ "isLoggedIn",
+ "on"
+ ]);
+ openmct = createOpenMct();
+ mockUser = new openmct.user.User("test-user", "A test user");
+ userProvider.getCurrentUser.and.returnValue(Promise.resolve(mockUser));
+ userProvider.getPossibleStatuses.and.returnValue(Promise.resolve([]));
+ userProvider.getAllStatusRoles.and.returnValue(Promise.resolve([]));
+ userProvider.canSetPollQuestion.and.returnValue(Promise.resolve(false));
+ userProvider.isLoggedIn.and.returnValue(true);
+ });
+
+ afterEach(() => {
+ return resetApplicationState(openmct);
+ });
+
+ describe("the poll question", () => {
+ it('can be set via a user status provider if supported', () => {
+ openmct.user.setProvider(userProvider);
+ userProvider.canSetPollQuestion.and.returnValue(Promise.resolve(true));
+
+ return openmct.user.status.setPollQuestion('This is a poll question').then(() => {
+ expect(userProvider.setPollQuestion).toHaveBeenCalledWith('This is a poll question');
+ });
+ });
+ // fit('emits an event when the poll question changes', () => {
+ // const pollQuestionChangeCallback = jasmine.createSpy('pollQuestionChangeCallback');
+ // let pollQuestionListener;
+
+ // userProvider.canSetPollQuestion.and.returnValue(Promise.resolve(true));
+ // userProvider.on.and.callFake((eventName, listener) => {
+ // if (eventName === 'pollQuestionChange') {
+ // pollQuestionListener = listener;
+ // }
+ // });
+
+ // openmct.user.on('pollQuestionChange', pollQuestionChangeCallback);
+
+ // openmct.user.setProvider(userProvider);
+
+ // return openmct.user.status.setPollQuestion('This is a poll question').then(() => {
+ // expect(pollQuestionListener).toBeDefined();
+ // pollQuestionListener();
+ // expect(pollQuestionChangeCallback).toHaveBeenCalled();
+
+ // const pollQuestion = pollQuestionChangeCallback.calls.mostRecent().args[0];
+ // expect(pollQuestion.question).toBe('This is a poll question');
+
+ // openmct.user.off('pollQuestionChange', pollQuestionChangeCallback);
+ // });
+ // });
+ it('cannot be set if the user is not permitted', () => {
+ openmct.user.setProvider(userProvider);
+ userProvider.canSetPollQuestion.and.returnValue(Promise.resolve(false));
+
+ return openmct.user.status.setPollQuestion('This is a poll question').catch((error) => {
+ expect(error).toBeInstanceOf(Error);
+ }).finally(() => {
+ expect(userProvider.setPollQuestion).not.toHaveBeenCalled();
+ });
+ });
+ });
+});
diff --git a/src/plugins/legacySupport/legacyRegistry.js b/src/api/user/constants.js
index 7d247e5c9..6822ce325 100644
--- a/src/plugins/legacySupport/legacyRegistry.js
+++ b/src/api/user/constants.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -20,6 +20,5 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
-define(['./BundleRegistry'], function (BundleRegistry) {
- return new BundleRegistry();
-});
+export const MULTIPLE_PROVIDER_ERROR = 'Only one user provider may be set at a time.';
+export const NO_PROVIDER_ERROR = 'No user provider has been set.';
diff --git a/src/end.frag b/src/end.frag
deleted file mode 100644
index 9746cce6b..000000000
--- a/src/end.frag
+++ /dev/null
@@ -1,2 +0,0 @@
- return require('openmct');
-}));
diff --git a/src/exporters/CSVExporter.js b/src/exporters/CSVExporter.js
index 3453d3ab2..89c517987 100644
--- a/src/exporters/CSVExporter.js
+++ b/src/exporters/CSVExporter.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -21,7 +21,7 @@
*****************************************************************************/
import CSV from 'comma-separated-values';
-import {saveAs} from 'file-saver/FileSaver';
+import {saveAs} from 'saveAs';
class CSVExporter {
export(rows, options) {
diff --git a/src/exporters/ImageExporter.js b/src/exporters/ImageExporter.js
index 023603114..d0459451d 100644
--- a/src/exporters/ImageExporter.js
+++ b/src/exporters/ImageExporter.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -31,9 +31,9 @@ function replaceDotsWithUnderscores(filename) {
return filename.replace(regex, '_');
}
-import {saveAs} from 'file-saver/FileSaver';
+import {saveAs} from 'saveAs';
import html2canvas from 'html2canvas';
-import uuid from 'uuid';
+import { v4 as uuid } from 'uuid';
class ImageExporter {
constructor(openmct) {
@@ -51,7 +51,7 @@ class ImageExporter {
const overlays = this.openmct.overlays;
const dialog = overlays.dialog({
iconClass: 'info',
- message: 'Caputuring an image',
+ message: 'Capturing image, please wait...',
buttons: [
{
label: 'Cancel',
diff --git a/src/exporters/ImageExporterSpec.js b/src/exporters/ImageExporterSpec.js
index bb00cfd4a..7127745a0 100644
--- a/src/exporters/ImageExporterSpec.js
+++ b/src/exporters/ImageExporterSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/exporters/JSONExporter.js b/src/exporters/JSONExporter.js
index 4baa718c4..262c043c6 100644
--- a/src/exporters/JSONExporter.js
+++ b/src/exporters/JSONExporter.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -20,7 +20,7 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
-import {saveAs} from 'file-saver/FileSaver';
+import {saveAs} from 'saveAs';
class JSONExporter {
export(obj, options) {
diff --git a/src/plugins/CouchDBSearchFolder/plugin.js b/src/plugins/CouchDBSearchFolder/plugin.js
index 02aed49d9..7aee30829 100644
--- a/src/plugins/CouchDBSearchFolder/plugin.js
+++ b/src/plugins/CouchDBSearchFolder/plugin.js
@@ -15,7 +15,8 @@ export default function (folderName, couchPlugin, searchFilter) {
return Promise.resolve({
identifier,
type: 'folder',
- name: folderName || "CouchDB Documents"
+ name: folderName || "CouchDB Documents",
+ location: 'ROOT'
});
}
}
diff --git a/src/plugins/CouchDBSearchFolder/pluginSpec.js b/src/plugins/CouchDBSearchFolder/pluginSpec.js
index 34ca26587..3eb23d9cc 100644
--- a/src/plugins/CouchDBSearchFolder/pluginSpec.js
+++ b/src/plugins/CouchDBSearchFolder/pluginSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -85,7 +85,8 @@ describe('the plugin', function () {
expect(object).toEqual({
identifier,
type: 'folder',
- name: "CouchDB Documents"
+ name: 'CouchDB Documents',
+ location: 'ROOT'
});
});
});
diff --git a/src/plugins/DeviceClassifier/plugin.js b/src/plugins/DeviceClassifier/plugin.js
index 6a34435e3..defa27297 100644
--- a/src/plugins/DeviceClassifier/plugin.js
+++ b/src/plugins/DeviceClassifier/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/DeviceClassifier/src/DeviceClassifier.js b/src/plugins/DeviceClassifier/src/DeviceClassifier.js
index 0d50a59cd..7bba65d96 100644
--- a/src/plugins/DeviceClassifier/src/DeviceClassifier.js
+++ b/src/plugins/DeviceClassifier/src/DeviceClassifier.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -52,7 +52,6 @@ export default (agent, document) => {
if (agent.isMobile()) {
const mediaQuery = window.matchMedia("(orientation: landscape)");
function eventHandler(event) {
- console.log("changed");
if (event.matches) {
body.classList.remove("portrait");
body.classList.add("landscape");
diff --git a/src/plugins/DeviceClassifier/src/DeviceClassifierSpec.js b/src/plugins/DeviceClassifier/src/DeviceClassifierSpec.js
index cd75ac89a..52b9ee860 100644
--- a/src/plugins/DeviceClassifier/src/DeviceClassifierSpec.js
+++ b/src/plugins/DeviceClassifier/src/DeviceClassifierSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/DeviceClassifier/src/DeviceMatchers.js b/src/plugins/DeviceClassifier/src/DeviceMatchers.js
index 01cd830db..5fdded670 100644
--- a/src/plugins/DeviceClassifier/src/DeviceMatchers.js
+++ b/src/plugins/DeviceClassifier/src/DeviceMatchers.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/DeviceClassifier/src/DeviceMatchersSpec.js b/src/plugins/DeviceClassifier/src/DeviceMatchersSpec.js
index db8eca20a..0df4b5f33 100644
--- a/src/plugins/DeviceClassifier/src/DeviceMatchersSpec.js
+++ b/src/plugins/DeviceClassifier/src/DeviceMatchersSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/ISOTimeFormat/ISOTimeFormat.js b/src/plugins/ISOTimeFormat/ISOTimeFormat.js
index 44bab4a8a..5fea5a4d1 100644
--- a/src/plugins/ISOTimeFormat/ISOTimeFormat.js
+++ b/src/plugins/ISOTimeFormat/ISOTimeFormat.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/ISOTimeFormat/plugin.js b/src/plugins/ISOTimeFormat/plugin.js
index e12d86d39..fb3e1a42a 100644
--- a/src/plugins/ISOTimeFormat/plugin.js
+++ b/src/plugins/ISOTimeFormat/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/ISOTimeFormat/pluginSpec.js b/src/plugins/ISOTimeFormat/pluginSpec.js
index 329d6f7d2..8c6a87844 100644
--- a/src/plugins/ISOTimeFormat/pluginSpec.js
+++ b/src/plugins/ISOTimeFormat/pluginSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/LADTable/LADTableCompositionPolicy.js b/src/plugins/LADTable/LADTableCompositionPolicy.js
index d98fa2608..17405c3df 100644
--- a/src/plugins/LADTable/LADTableCompositionPolicy.js
+++ b/src/plugins/LADTable/LADTableCompositionPolicy.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/LADTable/LADTableSetViewProvider.js b/src/plugins/LADTable/LADTableSetViewProvider.js
index 2fec17fd7..3db2c123c 100644
--- a/src/plugins/LADTable/LADTableSetViewProvider.js
+++ b/src/plugins/LADTable/LADTableSetViewProvider.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/LADTable/LADTableViewProvider.js b/src/plugins/LADTable/LADTableViewProvider.js
index ebe2f3294..fdfb65186 100644
--- a/src/plugins/LADTable/LADTableViewProvider.js
+++ b/src/plugins/LADTable/LADTableViewProvider.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/LADTable/components/LADRow.vue b/src/plugins/LADTable/components/LADRow.vue
index 1621fa86f..929a4e201 100644
--- a/src/plugins/LADTable/components/LADRow.vue
+++ b/src/plugins/LADTable/components/LADRow.vue
@@ -1,6 +1,5 @@
-
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -114,14 +113,12 @@ export default {
this.metadata = this.openmct.telemetry.getMetadata(this.domainObject);
this.formats = this.openmct.telemetry.getFormatMap(this.metadata);
this.keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
- this.bounds = this.openmct.time.bounds();
this.limitEvaluator = this.openmct
.telemetry
.limitEvaluator(this.domainObject);
this.openmct.time.on('timeSystem', this.updateTimeSystem);
- this.openmct.time.on('bounds', this.updateBounds);
this.timestampKey = this.openmct.time.timeSystem().key;
@@ -135,72 +132,41 @@ export default {
this.valueKey = this.valueMetadata ? this.valueMetadata.key : undefined;
- this.unsubscribe = this.openmct
- .telemetry
- .subscribe(this.domainObject, this.setLatestValues);
-
- this.requestHistory();
+ this.telemetryCollection = this.openmct.telemetry.requestCollection(this.domainObject, {
+ size: 1,
+ strategy: 'latest'
+ });
+ this.telemetryCollection.on('add', this.setLatestValues);
+ this.telemetryCollection.on('clear', this.resetValues);
+ this.telemetryCollection.load();
if (this.hasUnits) {
this.setUnit();
}
},
destroyed() {
- this.unsubscribe();
this.openmct.time.off('timeSystem', this.updateTimeSystem);
- this.openmct.time.off('bounds', this.updateBounds);
+ this.telemetryCollection.off('add', this.setLatestValues);
+ this.telemetryCollection.off('clear', this.resetValues);
+
+ this.telemetryCollection.destroy();
},
methods: {
updateView() {
if (!this.updatingView) {
this.updatingView = true;
requestAnimationFrame(() => {
- let newTimestamp = this.getParsedTimestamp(this.latestDatum);
-
- if (this.shouldUpdate(newTimestamp)) {
- this.timestamp = newTimestamp;
- this.datum = this.latestDatum;
- }
-
+ this.timestamp = this.getParsedTimestamp(this.latestDatum);
+ this.datum = this.latestDatum;
this.updatingView = false;
});
}
},
- setLatestValues(datum) {
- this.latestDatum = datum;
-
+ setLatestValues(data) {
+ this.latestDatum = data[data.length - 1];
this.updateView();
},
- shouldUpdate(newTimestamp) {
- return this.inBounds(newTimestamp)
- && (this.timestamp === undefined || newTimestamp > this.timestamp);
- },
- requestHistory() {
- this.openmct
- .telemetry
- .request(this.domainObject, {
- start: this.bounds.start,
- end: this.bounds.end,
- size: 1,
- strategy: 'latest'
- })
- .then((array) => this.setLatestValues(array[array.length - 1]))
- .catch((error) => {
- console.warn('Error fetching data', error);
- });
- },
- updateBounds(bounds, isTick) {
- this.bounds = bounds;
- if (!isTick) {
- this.resetValues();
- this.requestHistory();
- }
- },
- inBounds(timestamp) {
- return timestamp >= this.bounds.start && timestamp <= this.bounds.end;
- },
updateTimeSystem(timeSystem) {
- this.resetValues();
this.timestampKey = timeSystem.key;
},
updateViewContext() {
@@ -231,7 +197,7 @@ export default {
}
},
setUnit() {
- this.unit = this.valueMetadata.unit || '';
+ this.unit = this.valueMetadata ? this.valueMetadata.unit : '';
},
firstNonDomainAttribute(metadata) {
return metadata
@@ -241,4 +207,3 @@ export default {
}
};
</script>
-
diff --git a/src/plugins/LADTable/components/LADTable.vue b/src/plugins/LADTable/components/LADTable.vue
index 26d305c30..ad3f878db 100644
--- a/src/plugins/LADTable/components/LADTable.vue
+++ b/src/plugins/LADTable/components/LADTable.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/LADTable/components/LadTableSet.vue b/src/plugins/LADTable/components/LadTableSet.vue
index 8d208cdc0..d03fb8318 100644
--- a/src/plugins/LADTable/components/LadTableSet.vue
+++ b/src/plugins/LADTable/components/LadTableSet.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -83,9 +83,12 @@ export default {
for (let ladTable of ladTables) {
for (let telemetryObject of ladTable) {
let metadata = this.openmct.telemetry.getMetadata(telemetryObject.domainObject);
- for (let metadatum of metadata.valueMetadatas) {
- if (metadatum.unit) {
- return true;
+
+ if (metadata) {
+ for (let metadatum of metadata.valueMetadatas) {
+ if (metadatum.unit) {
+ return true;
+ }
}
}
}
diff --git a/src/plugins/LADTable/plugin.js b/src/plugins/LADTable/plugin.js
index 535863460..e686ce9a8 100644
--- a/src/plugins/LADTable/plugin.js
+++ b/src/plugins/LADTable/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/LADTable/pluginSpec.js b/src/plugins/LADTable/pluginSpec.js
index 1ca631cf2..33985983e 100644
--- a/src/plugins/LADTable/pluginSpec.js
+++ b/src/plugins/LADTable/pluginSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -46,6 +46,7 @@ describe("The LAD Table", () => {
let openmct;
let ladPlugin;
+ let historicalProvider;
let parent;
let child;
let telemetryCount = 3;
@@ -81,6 +82,13 @@ describe("The LAD Table", () => {
spyOn(openmct.objects, 'get').and.returnValue(Promise.resolve({}));
+ historicalProvider = {
+ request: () => {
+ return Promise.resolve([]);
+ }
+ };
+ spyOn(openmct.telemetry, 'findRequestProvider').and.returnValue(historicalProvider);
+
openmct.time.bounds({
start: bounds.start,
end: bounds.end
@@ -147,7 +155,7 @@ describe("The LAD Table", () => {
// add another telemetry object as composition in lad table to test multi rows
mockObj.ladTable.composition.push(anotherTelemetryObj.identifier);
- beforeEach(async () => {
+ beforeEach(async (done) => {
let telemetryRequestResolve;
let telemetryObjectResolve;
let anotherTelemetryObjectResolve;
@@ -166,11 +174,12 @@ describe("The LAD Table", () => {
callBack();
});
- openmct.telemetry.request.and.callFake(() => {
+ historicalProvider.request = () => {
telemetryRequestResolve(mockTelemetry);
return telemetryRequestPromise;
- });
+ };
+
openmct.objects.get.and.callFake((obj) => {
if (obj.key === 'telemetry-object') {
telemetryObjectResolve(mockObj.telemetry);
@@ -195,6 +204,8 @@ describe("The LAD Table", () => {
await Promise.all([telemetryRequestPromise, telemetryObjectPromise, anotherTelemetryObjectPromise]);
await Vue.nextTick();
+
+ done();
});
it("should show one row per object in the composition", () => {
diff --git a/src/plugins/URLIndicatorPlugin/URLIndicator.js b/src/plugins/URLIndicatorPlugin/URLIndicator.js
index 9c560bd57..5a6785e54 100644
--- a/src/plugins/URLIndicatorPlugin/URLIndicator.js
+++ b/src/plugins/URLIndicatorPlugin/URLIndicator.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -20,10 +20,8 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
-define(
- ['zepto'],
- function ($) {
-
+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.
@@ -75,12 +73,17 @@ define(
};
URLIndicator.prototype.fetchUrl = function () {
- $.ajax({
- type: 'GET',
- url: this.URLpath,
- success: this.handleSuccess,
- error: this.handleError
- });
+ fetch(this.URLpath)
+ .then(response => {
+ if (response.ok) {
+ this.handleSuccess();
+ } else {
+ this.handleError();
+ }
+ })
+ .catch(error => {
+ this.handleError();
+ });
};
URLIndicator.prototype.handleError = function (e) {
diff --git a/src/plugins/URLIndicatorPlugin/URLIndicatorPlugin.js b/src/plugins/URLIndicatorPlugin/URLIndicatorPlugin.js
index 930a2b969..bec6df1dc 100644
--- a/src/plugins/URLIndicatorPlugin/URLIndicatorPlugin.js
+++ b/src/plugins/URLIndicatorPlugin/URLIndicatorPlugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/URLIndicatorPlugin/URLIndicatorSpec.js b/src/plugins/URLIndicatorPlugin/URLIndicatorSpec.js
index 0c45938f5..cf6cd39ff 100644
--- a/src/plugins/URLIndicatorPlugin/URLIndicatorSpec.js
+++ b/src/plugins/URLIndicatorPlugin/URLIndicatorSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -25,37 +25,35 @@ define(
"utils/testing",
"./URLIndicator",
"./URLIndicatorPlugin",
- "../../MCT",
- "zepto"
+ "../../MCT"
],
function (
testingUtils,
URLIndicator,
URLIndicatorPlugin,
- MCT,
- $
+ MCT
) {
- const defaultAjaxFunction = $.ajax;
-
describe("The URLIndicator", function () {
let openmct;
let indicatorElement;
let pluginOptions;
- let ajaxOptions;
let urlIndicator; // eslint-disable-line
+ let fetchSpy;
beforeEach(function () {
jasmine.clock().install();
openmct = new testingUtils.createOpenMct();
spyOn(openmct.indicators, 'add');
- spyOn($, 'ajax');
- $.ajax.and.callFake(function (options) {
- ajaxOptions = options;
- });
+ fetchSpy = spyOn(window, 'fetch').and.callFake(() => Promise.resolve({
+ ok: true
+ }));
});
afterEach(function () {
- $.ajax = defaultAjaxFunction;
+ if (window.fetch.restore) {
+ window.fetch.restore();
+ }
+
jasmine.clock().uninstall();
return testingUtils.resetApplicationState(openmct);
@@ -96,11 +94,11 @@ define(
expect(indicatorElement.classList.contains('iconClass-checked')).toBe(true);
});
it("uses custom interval", function () {
- expect($.ajax.calls.count()).toEqual(1);
+ expect(window.fetch).toHaveBeenCalledTimes(1);
jasmine.clock().tick(1);
- expect($.ajax.calls.count()).toEqual(1);
+ expect(window.fetch).toHaveBeenCalledTimes(1);
jasmine.clock().tick(pluginOptions.interval + 1);
- expect($.ajax.calls.count()).toEqual(2);
+ expect(window.fetch).toHaveBeenCalledTimes(2);
});
it("uses custom label if supplied in initialization", function () {
expect(indicatorElement.textContent.indexOf(pluginOptions.label) >= 0).toBe(true);
@@ -120,18 +118,21 @@ define(
it("requests the provided URL", function () {
jasmine.clock().tick(pluginOptions.interval + 1);
- expect(ajaxOptions.url).toEqual(pluginOptions.url);
+ expect(window.fetch).toHaveBeenCalledWith(pluginOptions.url);
});
- it("indicates success if connection is nominal", function () {
+ it("indicates success if connection is nominal", async function () {
jasmine.clock().tick(pluginOptions.interval + 1);
- ajaxOptions.success();
+ await urlIndicator.fetchUrl();
expect(indicatorElement.classList.contains('s-status-on')).toBe(true);
});
- it("indicates an error when the server cannot be reached", function () {
+ 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);
- ajaxOptions.error();
+ await urlIndicator.fetchUrl();
expect(indicatorElement.classList.contains('s-status-warning-hi')).toBe(true);
});
});
diff --git a/src/plugins/URLTimeSettingsSynchronizer/URLTimeSettingsSynchronizer.js b/src/plugins/URLTimeSettingsSynchronizer/URLTimeSettingsSynchronizer.js
index 1d63be713..dcd9f5c24 100644
--- a/src/plugins/URLTimeSettingsSynchronizer/URLTimeSettingsSynchronizer.js
+++ b/src/plugins/URLTimeSettingsSynchronizer/URLTimeSettingsSynchronizer.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/URLTimeSettingsSynchronizer/plugin.js b/src/plugins/URLTimeSettingsSynchronizer/plugin.js
index 0dd888563..94bce5d42 100644
--- a/src/plugins/URLTimeSettingsSynchronizer/plugin.js
+++ b/src/plugins/URLTimeSettingsSynchronizer/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/URLTimeSettingsSynchronizer/pluginSpec.js b/src/plugins/URLTimeSettingsSynchronizer/pluginSpec.js
index 32435acb6..ad09b3b30 100644
--- a/src/plugins/URLTimeSettingsSynchronizer/pluginSpec.js
+++ b/src/plugins/URLTimeSettingsSynchronizer/pluginSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/autoflow/AutoflowTabularConstants.js b/src/plugins/autoflow/AutoflowTabularConstants.js
index 53f00dfe8..9dcc58ff4 100644
--- a/src/plugins/autoflow/AutoflowTabularConstants.js
+++ b/src/plugins/autoflow/AutoflowTabularConstants.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/autoflow/AutoflowTabularController.js b/src/plugins/autoflow/AutoflowTabularController.js
index cf762d780..aa1f0570c 100644
--- a/src/plugins/autoflow/AutoflowTabularController.js
+++ b/src/plugins/autoflow/AutoflowTabularController.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/autoflow/AutoflowTabularPlugin.js b/src/plugins/autoflow/AutoflowTabularPlugin.js
index 4a39d7b47..16ced0a16 100644
--- a/src/plugins/autoflow/AutoflowTabularPlugin.js
+++ b/src/plugins/autoflow/AutoflowTabularPlugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/autoflow/AutoflowTabularPluginSpec.js b/src/plugins/autoflow/AutoflowTabularPluginSpec.js
index f04cd51d7..5e5d49489 100644
--- a/src/plugins/autoflow/AutoflowTabularPluginSpec.js
+++ b/src/plugins/autoflow/AutoflowTabularPluginSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -21,7 +21,6 @@
*****************************************************************************/
import AutoflowTabularPlugin from './AutoflowTabularPlugin';
import AutoflowTabularConstants from './AutoflowTabularConstants';
-import $ from 'zepto';
import DOMObserver from './dom-observer';
import {
createOpenMct,
@@ -122,7 +121,7 @@ xdescribe("AutoflowTabularPlugin", () => {
name: "Object " + key
};
});
- testContainer = $('<div>')[0];
+ testContainer = document.createElement('div');
domObserver = new DOMObserver(testContainer);
testHistories = testKeys.reduce((histories, key, index) => {
@@ -195,7 +194,7 @@ xdescribe("AutoflowTabularPlugin", () => {
describe("when rows have been populated", () => {
function rowsMatch() {
- const rows = $(testContainer).find(".l-autoflow-row").length;
+ const rows = testContainer.querySelectorAll(".l-autoflow-row").length;
return rows === testChildren.length;
}
@@ -241,20 +240,20 @@ xdescribe("AutoflowTabularPlugin", () => {
const nextWidth =
initialWidth + AutoflowTabularConstants.COLUMN_WIDTH_STEP;
- expect($(testContainer).find('.l-autoflow-col').css('width'))
+ expect(testContainer.querySelector('.l-autoflow-col').css('width'))
.toEqual(initialWidth + 'px');
- $(testContainer).find('.change-column-width').click();
+ testContainer.querySelector('.change-column-width').click();
function widthHasChanged() {
- const width = $(testContainer).find('.l-autoflow-col').css('width');
+ const width = testContainer.querySelector('.l-autoflow-col').css('width');
return width !== initialWidth + 'px';
}
return domObserver.when(widthHasChanged)
.then(() => {
- expect($(testContainer).find('.l-autoflow-col').css('width'))
+ expect(testContainer.querySelector('.l-autoflow-col').css('width'))
.toEqual(nextWidth + 'px');
});
});
@@ -267,13 +266,13 @@ xdescribe("AutoflowTabularPlugin", () => {
it("displays historical telemetry", () => {
function rowTextDefined() {
- return $(testContainer).find(".l-autoflow-item").filter(".r").text() !== "";
+ return testContainer.querySelector(".l-autoflow-item").filter(".r").text() !== "";
}
return domObserver.when(rowTextDefined).then(() => {
testKeys.forEach((key, index) => {
const datum = testHistories[key];
- const $cell = $(testContainer).find(".l-autoflow-row").eq(index).find(".r");
+ const $cell = testContainer.querySelector(".l-autoflow-row").eq(index).find(".r");
expect($cell.text()).toEqual(String(datum.range));
});
});
@@ -294,7 +293,7 @@ xdescribe("AutoflowTabularPlugin", () => {
return waitsForChange().then(() => {
testData.forEach((datum, index) => {
- const $cell = $(testContainer).find(".l-autoflow-row").eq(index).find(".r");
+ const $cell = testContainer.querySelector(".l-autoflow-row").eq(index).find(".r");
expect($cell.text()).toEqual(String(datum.range));
});
});
@@ -312,7 +311,7 @@ xdescribe("AutoflowTabularPlugin", () => {
return waitsForChange().then(() => {
testKeys.forEach((datum, index) => {
- const $cell = $(testContainer).find(".l-autoflow-row").eq(index).find(".r");
+ const $cell = testContainer.querySelector(".l-autoflow-row").eq(index).find(".r");
expect($cell.hasClass(testClass)).toBe(true);
});
});
@@ -322,16 +321,16 @@ xdescribe("AutoflowTabularPlugin", () => {
const rowHeight = AutoflowTabularConstants.ROW_HEIGHT;
const sliderHeight = AutoflowTabularConstants.SLIDER_HEIGHT;
const count = testKeys.length;
- const $container = $(testContainer);
+ const $container = testContainer;
let promiseChain = Promise.resolve();
function columnsHaveAutoflowed() {
- const itemsHeight = $container.find('.l-autoflow-items').height();
+ const itemsHeight = $container.querySelector('.l-autoflow-items').height();
const availableHeight = itemsHeight - sliderHeight;
const availableRows = Math.max(Math.floor(availableHeight / rowHeight), 1);
const columns = Math.ceil(count / availableRows);
- return $container.find('.l-autoflow-col').length === columns;
+ return $container.querySelector('.l-autoflow-col').length === columns;
}
$container.find('.abs').css({
diff --git a/src/plugins/autoflow/AutoflowTabularRowController.js b/src/plugins/autoflow/AutoflowTabularRowController.js
index 5c11af889..0f687ac9a 100644
--- a/src/plugins/autoflow/AutoflowTabularRowController.js
+++ b/src/plugins/autoflow/AutoflowTabularRowController.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/autoflow/AutoflowTabularView.js b/src/plugins/autoflow/AutoflowTabularView.js
index 6dd9734d5..3dbdb17b5 100644
--- a/src/plugins/autoflow/AutoflowTabularView.js
+++ b/src/plugins/autoflow/AutoflowTabularView.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/autoflow/VueView.js b/src/plugins/autoflow/VueView.js
index 481a4c20e..0872b5368 100644
--- a/src/plugins/autoflow/VueView.js
+++ b/src/plugins/autoflow/VueView.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/autoflow/autoflow-tabular.html b/src/plugins/autoflow/autoflow-tabular.html
index d98f4c5a7..c466c80ab 100644
--- a/src/plugins/autoflow/autoflow-tabular.html
+++ b/src/plugins/autoflow/autoflow-tabular.html
@@ -1,5 +1,5 @@
<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
+ Open MCT, Copyright (c) 2014-2022, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
diff --git a/src/plugins/autoflow/dom-observer.js b/src/plugins/autoflow/dom-observer.js
index 35eb571a3..e018e566b 100644
--- a/src/plugins/autoflow/dom-observer.js
+++ b/src/plugins/autoflow/dom-observer.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/charts/BarGraphCompositionPolicy.js b/src/plugins/charts/bar/BarGraphCompositionPolicy.js
index 0d18d7a39..7da0a7fc7 100644
--- a/src/plugins/charts/BarGraphCompositionPolicy.js
+++ b/src/plugins/charts/bar/BarGraphCompositionPolicy.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/charts/BarGraphConstants.js b/src/plugins/charts/bar/BarGraphConstants.js
index bdd88a7ce..bdd88a7ce 100644
--- a/src/plugins/charts/BarGraphConstants.js
+++ b/src/plugins/charts/bar/BarGraphConstants.js
diff --git a/src/plugins/charts/BarGraphPlot.vue b/src/plugins/charts/bar/BarGraphPlot.vue
index b27dcf122..ad5894afc 100644
--- a/src/plugins/charts/BarGraphPlot.vue
+++ b/src/plugins/charts/bar/BarGraphPlot.vue
@@ -1,28 +1,34 @@
<template>
-<div ref="plotWrapper"
- class="has-local-controls"
- :class="{ 's-unsynced' : isZoomed }"
+<div
+ ref="plotWrapper"
+ class="has-local-controls"
+ :class="{ 's-unsynced' : isZoomed }"
>
- <div v-if="isZoomed"
- class="l-state-indicators"
+ <div
+ v-if="isZoomed"
+ class="l-state-indicators"
>
- <span class="l-state-indicators__alert-no-lad t-object-alert t-alert-unsynced icon-alert-triangle"
- title="This plot is not currently displaying the latest data. Reset pan/zoom to view latest data."
+ <span
+ class="l-state-indicators__alert-no-lad t-object-alert t-alert-unsynced icon-alert-triangle"
+ title="This plot is not currently displaying the latest data. Reset pan/zoom to view latest data."
></span>
</div>
- <div ref="plot"
- class="c-bar-chart"
- @plotly_relayout="zoom"
+ <div
+ ref="plot"
+ class="c-bar-chart"
+ @plotly_relayout="zoom"
></div>
- <div v-if="false"
- ref="localControl"
- class="gl-plot__local-controls h-local-controls h-local-controls--overlay-content c-local-controls--show-on-hover"
+ <div
+ v-if="false"
+ ref="localControl"
+ class="gl-plot__local-controls h-local-controls h-local-controls--overlay-content c-local-controls--show-on-hover"
>
- <button v-if="data.length"
- class="c-button icon-reset"
- :disabled="!isZoomed"
- title="Reset pan/zoom"
- @click="reset()"
+ <button
+ v-if="data.length"
+ class="c-button icon-reset"
+ :disabled="!isZoomed"
+ title="Reset pan/zoom"
+ @click="reset()"
>
</button>
</div>
diff --git a/src/plugins/charts/BarGraphView.vue b/src/plugins/charts/bar/BarGraphView.vue
index 20997f117..11b1a7a07 100644
--- a/src/plugins/charts/BarGraphView.vue
+++ b/src/plugins/charts/bar/BarGraphView.vue
@@ -1,5 +1,5 @@
<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
+ Open MCT, Copyright (c) 2014-2022, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
@@ -21,12 +21,13 @@
-->
<template>
-<BarGraph ref="barGraph"
- class="c-plot c-bar-chart-view"
- :data="trace"
- :plot-axis-title="plotAxisTitle"
- @subscribe="subscribeToAll"
- @unsubscribe="removeAllSubscriptions"
+<BarGraph
+ ref="barGraph"
+ class="c-plot c-bar-chart-view"
+ :data="trace"
+ :plot-axis-title="plotAxisTitle"
+ @subscribe="subscribeToAll"
+ @unsubscribe="removeAllSubscriptions"
/>
</template>
@@ -62,12 +63,16 @@ export default {
}
},
mounted() {
- this.loadComposition();
+ this.refreshData = this.refreshData.bind(this);
+ this.setTimeContext();
- this.openmct.time.on('bounds', this.refreshData);
+ this.loadComposition();
+ this.unobserveAxes = this.openmct.objects.observe(this.domainObject, 'configuration.axes', this.refreshData);
+ this.unobserveInterpolation = this.openmct.objects.observe(this.domainObject, 'configuration.useInterpolation', this.refreshData);
+ this.unobserveBar = this.openmct.objects.observe(this.domainObject, 'configuration.useBar', this.refreshData);
},
beforeDestroy() {
- this.openmct.time.off('bounds', this.refreshData);
+ this.stopFollowingTimeContext();
this.removeAllSubscriptions();
@@ -75,10 +80,72 @@ export default {
return;
}
- this.composition.off('add', this.addTelemetryObject);
+ this.composition.off('add', this.addToComposition);
this.composition.off('remove', this.removeTelemetryObject);
+ if (this.unobserveAxes) {
+ this.unobserveAxes();
+ }
+
+ if (this.unobserveInterpolation) {
+ this.unobserveInterpolation();
+ }
+
+ if (this.unobserveBar) {
+ this.unobserveBar();
+ }
},
methods: {
+ setTimeContext() {
+ this.stopFollowingTimeContext();
+
+ this.timeContext = this.openmct.time.getContextForView(this.path);
+ this.followTimeContext();
+
+ },
+ followTimeContext() {
+ this.timeContext.on('bounds', this.refreshData);
+ },
+ stopFollowingTimeContext() {
+ if (this.timeContext) {
+ this.timeContext.off('bounds', this.refreshData);
+ }
+ },
+ addToComposition(telemetryObject) {
+ if (Object.values(this.telemetryObjects).length > 0) {
+ this.confirmRemoval(telemetryObject);
+ } else {
+ this.addTelemetryObject(telemetryObject);
+ }
+ },
+ confirmRemoval(telemetryObject) {
+ const dialog = this.openmct.overlays.dialog({
+ iconClass: 'alert',
+ message: 'This action will replace the current telemetry source. Do you want to continue?',
+ buttons: [
+ {
+ label: 'Ok',
+ emphasis: true,
+ callback: () => {
+ const oldTelemetryObject = Object.values(this.telemetryObjects)[0];
+ this.removeFromComposition(oldTelemetryObject);
+ this.removeTelemetryObject(oldTelemetryObject.identifier);
+ this.addTelemetryObject(telemetryObject);
+ dialog.dismiss();
+ }
+ },
+ {
+ label: 'Cancel',
+ callback: () => {
+ this.removeFromComposition(telemetryObject);
+ dialog.dismiss();
+ }
+ }
+ ]
+ });
+ },
+ removeFromComposition(telemetryObject) {
+ this.composition.remove(telemetryObject);
+ },
addTelemetryObject(telemetryObject) {
// grab information we need from the added telmetry object
const key = this.openmct.objects.makeKeyString(telemetryObject.identifier);
@@ -111,6 +178,26 @@ export default {
this.requestDataFor(telemetryObject);
this.subscribeToObject(telemetryObject);
},
+ setTrace(key, name, axisMetadata, xValues, yValues) {
+ let trace = {
+ key,
+ name: name,
+ x: xValues,
+ y: yValues,
+ xAxisMetadata: {},
+ yAxisMetadata: axisMetadata.yAxisMetadata,
+ type: this.domainObject.configuration.useBar ? 'bar' : 'scatter',
+ mode: 'lines',
+ line: {
+ shape: this.domainObject.configuration.useInterpolation
+ },
+ marker: {
+ color: this.domainObject.configuration.barStyles.series[key].color
+ },
+ hoverinfo: this.domainObject.configuration.useBar ? 'skip' : 'x+y'
+ };
+ this.addTrace(trace, key);
+ },
addTrace(trace, key) {
if (!this.trace.length) {
this.trace = this.trace.concat([trace]);
@@ -139,7 +226,12 @@ export default {
const yAxisMetadata = metadata.valuesForHints(['range'])[0];
//Exclude 'name' and 'time' based metadata specifically, from the x-Axis values by using range hints only
- const xAxisMetadata = metadata.valuesForHints(['range']);
+ const xAxisMetadata = metadata.valuesForHints(['range'])
+ .map((metaDatum) => {
+ metaDatum.isArrayValue = metadata.isArrayValue(metaDatum);
+
+ return metaDatum;
+ });
return {
xAxisMetadata,
@@ -147,7 +239,7 @@ export default {
};
},
getOptions() {
- const { start, end } = this.openmct.time.bounds();
+ const { start, end } = this.timeContext.bounds();
return {
end,
@@ -157,20 +249,22 @@ export default {
loadComposition() {
this.composition = this.openmct.composition.get(this.domainObject);
- if (!this.composition) {
- this.addTelemetryObject(this.domainObject);
-
- return;
- }
-
- this.composition.on('add', this.addTelemetryObject);
+ this.composition.on('add', this.addToComposition);
this.composition.on('remove', this.removeTelemetryObject);
this.composition.load();
},
refreshData(bounds, isTick) {
if (!isTick) {
const telemetryObjects = Object.values(this.telemetryObjects);
- telemetryObjects.forEach(this.requestDataFor);
+ telemetryObjects.forEach((telemetryObject) => {
+ //clear existing data
+ const key = this.openmct.objects.makeKeyString(telemetryObject.identifier);
+ const axisMetadata = this.getAxisMetadata(telemetryObject);
+ this.setTrace(key, telemetryObject.name, axisMetadata, [], []);
+ //request new data
+ this.requestDataFor(telemetryObject);
+ this.subscribeToObject(telemetryObject);
+ });
}
},
removeAllSubscriptions() {
@@ -186,7 +280,10 @@ export default {
},
removeTelemetryObject(identifier) {
const key = this.openmct.objects.makeKeyString(identifier);
- delete this.telemetryObjects[key];
+ if (this.telemetryObjects[key]) {
+ delete this.telemetryObjects[key];
+ }
+
if (this.telemetryObjectFormats && this.telemetryObjectFormats[key]) {
delete this.telemetryObjectFormats[key];
}
@@ -211,46 +308,56 @@ export default {
this.openmct.notifications.alert(data.message);
}
- if (!this.isDataInTimeRange(data, key)) {
+ if (!this.isDataInTimeRange(data, key, telemetryObject)) {
+ return;
+ }
+
+ if (this.domainObject.configuration.axes.xKey === undefined || this.domainObject.configuration.axes.yKey === undefined) {
return;
}
let xValues = [];
let yValues = [];
-
- //populate X and Y values for plotly
- axisMetadata.xAxisMetadata.forEach((metadata) => {
- xValues.push(metadata.name);
- if (data[metadata.key]) {
- const formattedValue = this.format(key, metadata.key, data);
- yValues.push(formattedValue);
- } else {
- yValues.push(null);
+ let xAxisMetadata = axisMetadata.xAxisMetadata.find(metadata => metadata.key === this.domainObject.configuration.axes.xKey);
+ if (xAxisMetadata && xAxisMetadata.isArrayValue) {
+ //populate x and y values
+ let metadataKey = this.domainObject.configuration.axes.xKey;
+ if (data[metadataKey] !== undefined) {
+ xValues = this.parse(key, metadataKey, data);
}
- });
- const trace = {
- key,
- name: telemetryObject.name,
- x: xValues,
- y: yValues,
- text: yValues.map(String),
- xAxisMetadata: axisMetadata.xAxisMetadata,
- yAxisMetadata: axisMetadata.yAxisMetadata,
- type: 'bar',
- marker: {
- color: this.domainObject.configuration.barStyles.series[key].color
- },
- hoverinfo: 'skip'
- };
+ metadataKey = this.domainObject.configuration.axes.yKey;
+ if (data[metadataKey] !== undefined) {
+ yValues = this.parse(key, metadataKey, data);
+ }
+ } else {
+ //populate X and Y values for plotly
+ axisMetadata.xAxisMetadata.filter(metadataObj => !metadataObj.isArrayValue).forEach((metadata) => {
+ if (!xAxisMetadata) {
+ //Assign the first metadata to use for any formatting
+ xAxisMetadata = metadata;
+ }
+
+ xValues.push(metadata.name);
+ if (data[metadata.key]) {
+ const parsedValue = this.parse(key, metadata.key, data);
+ yValues.push(parsedValue);
+ } else {
+ yValues.push(null);
+ }
+ });
+ }
- this.addTrace(trace, key);
+ this.setTrace(key, telemetryObject.name, axisMetadata, xValues, yValues);
},
- isDataInTimeRange(datum, key) {
- const timeSystemKey = this.openmct.time.timeSystem().key;
- let currentTimestamp = this.parse(key, timeSystemKey, datum);
+ isDataInTimeRange(datum, key, telemetryObject) {
+ const timeSystemKey = this.timeContext.timeSystem().key;
+ const metadata = this.openmct.telemetry.getMetadata(telemetryObject);
+ let metadataValue = metadata.value(timeSystemKey) || { key: timeSystemKey };
+
+ let currentTimestamp = this.parse(key, metadataValue.key, datum);
- return currentTimestamp && this.openmct.time.bounds().end >= currentTimestamp;
+ return currentTimestamp && this.timeContext.bounds().end >= currentTimestamp;
},
format(telemetryObjectKey, metadataKey, data) {
const formats = this.telemetryObjectFormats[telemetryObjectKey];
@@ -268,7 +375,8 @@ export default {
},
requestDataFor(telemetryObject) {
const axisMetadata = this.getAxisMetadata(telemetryObject);
- this.openmct.telemetry.request(telemetryObject)
+ const options = this.getOptions();
+ this.openmct.telemetry.request(telemetryObject, options)
.then(data => {
data.forEach((datum) => {
this.addDataToGraph(telemetryObject, datum, axisMetadata);
diff --git a/src/plugins/charts/BarGraphViewProvider.js b/src/plugins/charts/bar/BarGraphViewProvider.js
index eae37bd41..a5cbbaca4 100644
--- a/src/plugins/charts/BarGraphViewProvider.js
+++ b/src/plugins/charts/bar/BarGraphViewProvider.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -66,12 +66,15 @@ export default function BarGraphViewProvider(openmct) {
}
};
},
- template: '<bar-graph-view :options="options"></bar-graph-view>'
+ template: '<bar-graph-view ref="graphComponent" :options="options"></bar-graph-view>'
});
},
destroy: function () {
component.$destroy();
component = undefined;
+ },
+ onClearData() {
+ component.$refs.graphComponent.refreshData();
}
};
}
diff --git a/src/plugins/charts/inspector/BarGraphInspectorViewProvider.js b/src/plugins/charts/bar/inspector/BarGraphInspectorViewProvider.js
index 0028ea40d..0028ea40d 100644
--- a/src/plugins/charts/inspector/BarGraphInspectorViewProvider.js
+++ b/src/plugins/charts/bar/inspector/BarGraphInspectorViewProvider.js
diff --git a/src/plugins/charts/bar/inspector/BarGraphOptions.vue b/src/plugins/charts/bar/inspector/BarGraphOptions.vue
new file mode 100644
index 000000000..24bb0b617
--- /dev/null
+++ b/src/plugins/charts/bar/inspector/BarGraphOptions.vue
@@ -0,0 +1,399 @@
+<!--
+ Open MCT, Copyright (c) 2014-2022, United States Government
+ as represented by the Administrator of the National Aeronautics and Space
+ Administration. All rights reserved.
+
+ Open MCT is licensed under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0.
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ License for the specific language governing permissions and limitations
+ under the License.
+
+ Open MCT includes source code licensed under additional open source
+ licenses. See the Open Source Licenses file (LICENSES.md) included with
+ this source code distribution or the Licensing information page available
+ at runtime from the About dialog for additional information.
+-->
+<template>
+<div class="c-bar-graph-options js-bar-plot-option">
+ <ul class="c-tree">
+ <h2 title="Display properties for this object">Bar Graph Series</h2>
+ <li>
+ <series-options
+ v-for="series in plotSeries"
+ :key="series.key"
+ :item="series"
+ :color-palette="colorPalette"
+ />
+ </li>
+ </ul>
+ <div class="grid-properties">
+ <ul class="l-inspector-part">
+ <h2 title="Y axis settings for this object">Axes</h2>
+ <li class="grid-row">
+ <div
+ class="grid-cell label"
+ title="X axis selection."
+ >X Axis</div>
+ <div
+ v-if="isEditing"
+ class="grid-cell value"
+ >
+ <select
+ v-model="xKey"
+ @change="updateForm('xKey')"
+ >
+ <option
+ v-for="option in xKeyOptions"
+ :key="`xKey-${option.value}`"
+ :value="option.value"
+ :selected="option.value === xKey"
+ >
+ {{ option.name }}
+ </option>
+ </select>
+ </div>
+ <div
+ v-else
+ class="grid-cell value"
+ >{{ xKeyLabel }}</div>
+ </li>
+ <li
+ v-if="yKey !== ''"
+ class="grid-row"
+ >
+ <div
+ class="grid-cell label"
+ title="Y axis selection."
+ >Y Axis</div>
+ <div
+ v-if="isEditing"
+ class="grid-cell value"
+ >
+ <select
+ v-model="yKey"
+ @change="updateForm('yKey')"
+ >
+ <option
+ v-for="option in yKeyOptions"
+ :key="`yKey-${option.value}`"
+ :value="option.value"
+ :selected="option.value === yKey"
+ >
+ {{ option.name }}
+ </option>
+ </select>
+ </div>
+ <div
+ v-else
+ class="grid-cell value"
+ >{{ yKeyLabel }}</div>
+ </li>
+ </ul>
+ </div>
+ <div class="grid-properties">
+ <ul class="l-inspector-part">
+ <h2 title="Settings for plot">Settings</h2>
+ <li class="grid-row">
+ <div
+ v-if="isEditing"
+ class="grid-cell label"
+ title="Display style for the plot"
+ >Display Style</div>
+ <div
+ v-if="isEditing"
+ class="grid-cell value"
+ >
+ <select
+ v-model="useBar"
+ @change="updateBar"
+ >
+ <option :value="true">Bar</option>
+ <option :value="false">Line</option>
+ </select>
+ </div>
+ <div
+ v-if="!isEditing"
+ class="grid-cell label"
+ title="Display style for plot"
+ >Display Style</div>
+ <div
+ v-if="!isEditing"
+ class="grid-cell value"
+ >{{ {
+ 'true': 'Bar',
+ 'false': 'Line'
+ }[useBar] }}
+ </div>
+ </li>
+ <li
+ v-if="!useBar"
+ class="grid-row"
+ >
+ <div
+ v-if="isEditing"
+ class="grid-cell label"
+ title="The rendering method to join lines for this series."
+ >Line Method</div>
+ <div
+ v-if="isEditing"
+ class="grid-cell value"
+ >
+ <select
+ v-model="useInterpolation"
+ @change="updateInterpolation"
+ >
+ <option value="linear">Linear interpolate</option>
+ <option value="hv">Step after</option>
+ </select>
+ </div>
+ <div
+ v-if="!isEditing"
+ class="grid-cell label"
+ title="The rendering method to join lines for this series."
+ >Line Method</div>
+ <div
+ v-if="!isEditing"
+ class="grid-cell value"
+ >{{ {
+ 'linear': 'Linear interpolation',
+ 'hv': 'Step After'
+ }[useInterpolation] }}
+ </div>
+ </li>
+ </ul>
+ </div>
+</div>
+</template>
+
+<script>
+import SeriesOptions from "./SeriesOptions.vue";
+import ColorPalette from '@/ui/color/ColorPalette';
+
+export default {
+ components: {
+ SeriesOptions
+ },
+ inject: ['openmct', 'domainObject'],
+ data() {
+ return {
+ xKey: this.domainObject.configuration.axes.xKey,
+ yKey: this.domainObject.configuration.axes.yKey,
+ xKeyLabel: '',
+ yKeyLabel: '',
+ plotSeries: [],
+ yKeyOptions: [],
+ xKeyOptions: [],
+ isEditing: this.openmct.editor.isEditing(),
+ colorPalette: this.colorPalette,
+ useInterpolation: this.domainObject.configuration.useInterpolation,
+ useBar: this.domainObject.configuration.useBar
+ };
+ },
+ computed: {
+ canEdit() {
+ return this.isEditing && !this.domainObject.locked;
+ }
+ },
+ beforeMount() {
+ this.colorPalette = new ColorPalette();
+ },
+ mounted() {
+ this.openmct.editor.on('isEditing', this.setEditState);
+ this.composition = this.openmct.composition.get(this.domainObject);
+ this.registerListeners();
+ this.composition.load();
+ },
+ beforeDestroy() {
+ this.openmct.editor.off('isEditing', this.setEditState);
+ this.stopListening();
+ },
+ methods: {
+ setEditState(isEditing) {
+ this.isEditing = isEditing;
+ },
+ registerListeners() {
+ this.composition.on('add', this.addSeries);
+ this.composition.on('remove', this.removeSeries);
+ this.unobserve = this.openmct.objects.observe(this.domainObject, 'configuration.axes', this.setKeysAndSetupOptions);
+ },
+ stopListening() {
+ this.composition.off('add', this.addSeries);
+ this.composition.off('remove', this.removeSeries);
+ if (this.unobserve) {
+ this.unobserve();
+ }
+ },
+ addSeries(series, index) {
+ this.$set(this.plotSeries, this.plotSeries.length, series);
+ this.setupOptions();
+ },
+ removeSeries(seriesIdentifier) {
+ const index = this.plotSeries.findIndex(plotSeries => this.openmct.objects.areIdsEqual(seriesIdentifier, plotSeries.identifier));
+ if (index >= 0) {
+ this.$delete(this.plotSeries, index);
+ this.setupOptions();
+ }
+ },
+ setKeysAndSetupOptions() {
+ this.xKey = this.domainObject.configuration.axes.xKey;
+ this.yKey = this.domainObject.configuration.axes.yKey;
+ this.setupOptions();
+ },
+ setupOptions() {
+ this.xKeyOptions = [];
+ this.yKeyOptions = [];
+ if (this.plotSeries.length <= 0) {
+ return;
+ }
+
+ let update = false;
+ const series = this.plotSeries[0];
+ const metadata = this.openmct.telemetry.getMetadata(series);
+ const metadataRangeValues = metadata.valuesForHints(['range']).map((metaDatum) => {
+ metaDatum.isArrayValue = metadata.isArrayValue(metaDatum);
+
+ return metaDatum;
+ });
+ const metadataArrayValues = metadataRangeValues.filter(metadataObj => metadataObj.isArrayValue);
+ const metadataValues = metadataRangeValues.filter(metadataObj => !metadataObj.isArrayValue);
+ metadataArrayValues.forEach((metadataValue) => {
+ this.xKeyOptions.push({
+ name: metadataValue.name || metadataValue.key,
+ value: metadataValue.key,
+ isArrayValue: metadataValue.isArrayValue
+ });
+ this.yKeyOptions.push({
+ name: metadataValue.name || metadataValue.key,
+ value: metadataValue.key,
+ isArrayValue: metadataValue.isArrayValue
+ });
+ });
+
+ //Metadata values that are not array values will be grouped together as x-axis only option.
+ // Here, the y-axis is not relevant.
+ if (metadataValues.length) {
+ this.xKeyOptions.push(
+ metadataValues.reduce((previousValue, currentValue) => {
+ return {
+ name: previousValue?.name ? `${previousValue.name}, ${currentValue.name}` : `${currentValue.name}`,
+ value: currentValue.key,
+ isArrayValue: currentValue.isArrayValue
+ };
+ }, {name: ''})
+ );
+ }
+
+ let xKeyOptionIndex;
+ let yKeyOptionIndex;
+
+ if (this.domainObject.configuration.axes.xKey) {
+ xKeyOptionIndex = this.xKeyOptions.findIndex(option => option.value === this.domainObject.configuration.axes.xKey);
+ if (xKeyOptionIndex > -1) {
+ this.xKey = this.xKeyOptions[xKeyOptionIndex].value;
+ this.xKeyLabel = this.xKeyOptions[xKeyOptionIndex].name;
+ }
+ } else {
+ if (this.xKey === undefined) {
+ update = true;
+ xKeyOptionIndex = 0;
+ this.xKey = this.xKeyOptions[xKeyOptionIndex].value;
+ this.xKeyLabel = this.xKeyOptions[xKeyOptionIndex].name;
+ }
+ }
+
+ if (metadataRangeValues.length > 1) {
+ if (this.domainObject.configuration.axes.yKey && this.domainObject.configuration.axes.yKey !== 'none') {
+ yKeyOptionIndex = this.yKeyOptions.findIndex(option => option.value === this.domainObject.configuration.axes.yKey);
+ if (yKeyOptionIndex > -1 && yKeyOptionIndex !== xKeyOptionIndex) {
+ this.yKey = this.yKeyOptions[yKeyOptionIndex].value;
+ this.yKeyLabel = this.yKeyOptions[yKeyOptionIndex].name;
+ }
+ } else {
+ if (this.yKey === undefined) {
+ if (metadataValues.length && metadataArrayValues.length === 0) {
+ update = true;
+ this.yKey = 'none';
+ } else {
+ yKeyOptionIndex = this.yKeyOptions.findIndex((option, index) => index !== xKeyOptionIndex);
+ if (yKeyOptionIndex > -1) {
+ update = true;
+ this.yKey = this.yKeyOptions[yKeyOptionIndex].value;
+ this.yKeyLabel = this.yKeyOptions[yKeyOptionIndex].name;
+ }
+ }
+ }
+ }
+
+ this.yKeyOptions = this.yKeyOptions.map((option, index) => {
+ if (index === xKeyOptionIndex) {
+ option.name = `${option.name} (swap)`;
+ option.swap = yKeyOptionIndex;
+ } else {
+ option.name = option.name.replace(' (swap)', '');
+ option.swap = undefined;
+ }
+
+ return option;
+ });
+ } else if (this.xKey !== undefined && this.domainObject.configuration.axes.yKey === undefined) {
+ this.domainObject.configuration.axes.yKey = 'none';
+ }
+
+ this.xKeyOptions = this.xKeyOptions.map((option, index) => {
+ if (index === yKeyOptionIndex) {
+ option.name = `${option.name} (swap)`;
+ option.swap = xKeyOptionIndex;
+ } else {
+ option.name = option.name.replace(' (swap)', '');
+ option.swap = undefined;
+ }
+
+ return option;
+ });
+
+ if (update === true) {
+ this.saveConfiguration();
+ }
+ },
+ updateForm(property) {
+ if (property === 'xKey') {
+ const xKeyOption = this.xKeyOptions.find(option => option.value === this.xKey);
+ if (xKeyOption.swap !== undefined) {
+ //swap
+ this.yKey = this.xKeyOptions[xKeyOption.swap].value;
+ } else if (!xKeyOption.isArrayValue) {
+ this.yKey = 'none';
+ } else {
+ this.yKey = undefined;
+ }
+ } else if (property === 'yKey') {
+ const yKeyOption = this.yKeyOptions.find(option => option.value === this.yKey);
+ if (yKeyOption.swap !== undefined) {
+ //swap
+ this.xKey = this.yKeyOptions[yKeyOption.swap].value;
+ }
+ }
+
+ this.saveConfiguration();
+ },
+ saveConfiguration() {
+ this.openmct.objects.mutate(this.domainObject, `configuration.axes`, {
+ xKey: this.xKey,
+ yKey: this.yKey
+ });
+ },
+ updateInterpolation(event) {
+ this.openmct.objects.mutate(this.domainObject, `configuration.useInterpolation`, this.useInterpolation);
+ },
+ updateBar(event) {
+ this.openmct.objects.mutate(this.domainObject, `configuration.useBar`, this.useBar);
+ }
+ }
+};
+</script>
diff --git a/src/plugins/charts/inspector/SeriesOptions.vue b/src/plugins/charts/bar/inspector/SeriesOptions.vue
index 6db50db3f..8bb892446 100644
--- a/src/plugins/charts/inspector/SeriesOptions.vue
+++ b/src/plugins/charts/bar/inspector/SeriesOptions.vue
@@ -1,5 +1,5 @@
<!--
- Open MCT, Copyright (c) 2014-2020, United States Government
+ Open MCT, Copyright (c) 2014-2022, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
@@ -21,12 +21,14 @@
-->
<template>
<ul>
- <li class="c-tree__item menus-to-left"
+ <li
+ class="c-tree__item menus-to-left"
:class="aliasCss"
>
- <span class="c-disclosure-triangle is-enabled flex-elem"
- :class="expandedCssClass"
- @click="expanded = !expanded"
+ <span
+ class="c-disclosure-triangle is-enabled flex-elem"
+ :class="expandedCssClass"
+ @click="expanded = !expanded"
>
</span>
@@ -36,15 +38,19 @@
<div class="c-object-label__name">{{ name }}</div>
</div>
</li>
- <ColorSwatch v-if="expanded"
- :current-color="currentColor"
- title="Manually set the color for this bar graph series."
- edit-title="Manually set the color for this bar graph series"
- view-title="The color for this bar graph series."
- short-label="Color"
- class="grid-properties"
- @colorSet="setColor"
- />
+ <ul class="grid-properties">
+ <li class="grid-row">
+ <ColorSwatch
+ v-if="expanded"
+ :current-color="currentColor"
+ title="Manually set the color for this bar graph series."
+ edit-title="Manually set the color for this bar graph series."
+ view-title="The color for this bar graph series."
+ short-label="Color"
+ @colorSet="setColor"
+ />
+ </li>
+ </ul>
</ul>
</template>
@@ -106,7 +112,6 @@ export default {
}
},
mounted() {
- this.key = this.openmct.objects.makeKeyString(this.item);
this.initColorAndName();
this.removeBarStylesListener = this.openmct.objects.observe(this.domainObject, `configuration.barStyles.series["${this.key}"]`, this.initColorAndName);
},
@@ -117,6 +122,7 @@ export default {
},
methods: {
initColorAndName() {
+ this.key = this.openmct.objects.makeKeyString(this.item.identifier);
// this is called before the plot is initialized
if (!this.domainObject.configuration.barStyles.series[this.key]) {
const color = this.colorPalette.getNextColor().asHexString();
diff --git a/src/plugins/charts/plugin.js b/src/plugins/charts/bar/plugin.js
index de331e756..c0117b07c 100644
--- a/src/plugins/charts/plugin.js
+++ b/src/plugins/charts/bar/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -28,14 +28,17 @@ export default function () {
return function install(openmct) {
openmct.types.addType(BAR_GRAPH_KEY, {
key: BAR_GRAPH_KEY,
- name: "Bar Graph",
+ name: "Graph",
cssClass: "icon-bar-chart",
- description: "View data as a bar graph. Can be added to Display Layouts.",
+ description: "Visualize data as a bar or line graph.",
creatable: true,
initialize: function (domainObject) {
domainObject.composition = [];
domainObject.configuration = {
- barStyles: { series: {} }
+ barStyles: { series: {} },
+ axes: {},
+ useInterpolation: 'linear',
+ useBar: true
};
},
priority: 891
diff --git a/src/plugins/charts/pluginSpec.js b/src/plugins/charts/bar/pluginSpec.js
index ff412485d..b63906bd5 100644
--- a/src/plugins/charts/pluginSpec.js
+++ b/src/plugins/charts/bar/pluginSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -57,18 +57,18 @@ describe("the plugin", function () {
const testTelemetry = [
{
'utc': 1,
- 'some-key': 'some-value 1',
- 'some-other-key': 'some-other-value 1'
+ 'some-key': ['1.3222'],
+ 'some-other-key': [1]
},
{
'utc': 2,
- 'some-key': 'some-value 2',
- 'some-other-key': 'some-other-value 2'
+ 'some-key': ['2.555'],
+ 'some-other-key': [2]
},
{
'utc': 3,
- 'some-key': 'some-value 3',
- 'some-other-key': 'some-other-value 3'
+ 'some-key': ['3.888'],
+ 'some-other-key': [3]
}
];
@@ -123,7 +123,6 @@ describe("the plugin", function () {
});
describe("The bar graph view", () => {
- let testDomainObject;
let barGraphObject;
// eslint-disable-next-line no-unused-vars
let component;
@@ -135,22 +134,62 @@ describe("the plugin", function () {
namespace: "",
key: "test-plot"
},
+ configuration: {
+ barStyles: {
+ series: {}
+ },
+ axes: {},
+ useInterpolation: 'linear',
+ useBar: true
+ },
type: "telemetry.plot.bar-graph",
name: "Test Bar Graph"
};
- testDomainObject = {
- identifier: {
- namespace: "",
- key: "test-object"
+ mockComposition = new EventEmitter();
+ mockComposition.load = () => {
+ return [];
+ };
+
+ spyOn(openmct.composition, 'get').and.returnValue(mockComposition);
+
+ let viewContainer = document.createElement("div");
+ child.append(viewContainer);
+ component = new Vue({
+ el: viewContainer,
+ components: {
+ BarGraph
},
- configuration: {
- barStyles: {
- series: {}
- }
+ provide: {
+ openmct: openmct,
+ domainObject: barGraphObject,
+ composition: openmct.composition.get(barGraphObject)
},
- type: "test-object",
- name: "Test Object",
+ template: "<BarGraph></BarGraph>"
+ });
+
+ await Vue.nextTick();
+ });
+
+ it("provides a bar graph view", () => {
+ const applicableViews = openmct.objectViews.get(barGraphObject, mockObjectPath);
+ const plotViewProvider = applicableViews.find((viewProvider) => viewProvider.key === BAR_GRAPH_VIEW);
+ expect(plotViewProvider).toBeDefined();
+ });
+
+ it("Renders plotly bar graph", () => {
+ let barChartElement = element.querySelectorAll(".plotly");
+ expect(barChartElement.length).toBe(1);
+ });
+
+ it("Handles dots in telemetry id", () => {
+ const dotFullTelemetryObject = {
+ identifier: {
+ namespace: "someNamespace",
+ key: "~OpenMCT~outer.test-object.foo.bar"
+ },
+ type: "test-dotful-object",
+ name: "A Dotful Object",
telemetry: {
values: [{
key: "utc",
@@ -160,14 +199,14 @@ describe("the plugin", function () {
domain: 1
}
}, {
- key: "some-key",
- name: "Some attribute",
+ key: "some-key.foo.name.45",
+ name: "Some dotful attribute",
hints: {
range: 1
}
}, {
- key: "some-other-key",
- name: "Another attribute",
+ key: "some-other-key.bar.344.rad",
+ name: "Another dotful attribute",
hints: {
range: 2
}
@@ -175,11 +214,46 @@ describe("the plugin", function () {
}
};
+ const applicableViews = openmct.objectViews.get(barGraphObject, mockObjectPath);
+ const plotViewProvider = applicableViews.find((viewProvider) => viewProvider.key === BAR_GRAPH_VIEW);
+ const barGraphView = plotViewProvider.view(barGraphObject, [barGraphObject]);
+ barGraphView.show(child, true);
+ mockComposition.emit('add', dotFullTelemetryObject);
+ expect(barGraphObject.configuration.barStyles.series["someNamespace:~OpenMCT~outer.test-object.foo.bar"].name).toEqual("A Dotful Object");
+ barGraphView.destroy();
+ });
+ });
+
+ describe("The spectral plot view for telemetry objects with array values", () => {
+ let barGraphObject;
+ // eslint-disable-next-line no-unused-vars
+ let component;
+ let mockComposition;
+
+ beforeEach(async () => {
+ barGraphObject = {
+ identifier: {
+ namespace: "",
+ key: "test-plot"
+ },
+ configuration: {
+ barStyles: {
+ series: {}
+ },
+ axes: {
+ xKey: 'some-key',
+ yKey: 'some-other-key'
+ },
+ useInterpolation: 'linear',
+ useBar: false
+ },
+ type: "telemetry.plot.bar-graph",
+ name: "Test Bar Graph"
+ };
+
mockComposition = new EventEmitter();
mockComposition.load = () => {
- mockComposition.emit('add', testDomainObject);
-
- return [testDomainObject];
+ return [];
};
spyOn(openmct.composition, 'get').and.returnValue(mockComposition);
@@ -202,18 +276,7 @@ describe("the plugin", function () {
await Vue.nextTick();
});
- it("provides a bar graph view", () => {
- const applicableViews = openmct.objectViews.get(barGraphObject, mockObjectPath);
- const plotViewProvider = applicableViews.find((viewProvider) => viewProvider.key === BAR_GRAPH_VIEW);
- expect(plotViewProvider).toBeDefined();
- });
-
- it("Renders plotly bar graph", () => {
- let barChartElement = element.querySelectorAll(".plotly");
- expect(barChartElement.length).toBe(1);
- });
-
- it("Handles dots in telemetry id", () => {
+ it("Renders spectral plots", () => {
const dotFullTelemetryObject = {
identifier: {
namespace: "someNamespace",
@@ -230,29 +293,36 @@ describe("the plugin", function () {
domain: 1
}
}, {
- key: "some-key.foo.name.45",
- name: "Some dotful attribute",
+ key: "some-key",
+ name: "Some attribute",
+ formatString: '%0.2f[]',
hints: {
range: 1
- }
+ },
+ source: 'some-key'
}, {
- key: "some-other-key.bar.344.rad",
- name: "Another dotful attribute",
+ key: "some-other-key",
+ name: "Another attribute",
+ format: "number[]",
hints: {
range: 2
- }
+ },
+ source: 'some-other-key'
}]
}
};
const applicableViews = openmct.objectViews.get(barGraphObject, mockObjectPath);
const plotViewProvider = applicableViews.find((viewProvider) => viewProvider.key === BAR_GRAPH_VIEW);
- const barGraphView = plotViewProvider.view(testDomainObject, [testDomainObject]);
+ const barGraphView = plotViewProvider.view(barGraphObject, [barGraphObject]);
barGraphView.show(child, true);
- expect(testDomainObject.configuration.barStyles.series["test-object"].name).toEqual("Test Object");
mockComposition.emit('add', dotFullTelemetryObject);
- expect(testDomainObject.configuration.barStyles.series["someNamespace:~OpenMCT~outer.test-object.foo.bar"].name).toEqual("A Dotful Object");
- barGraphView.destroy();
+
+ return Vue.nextTick().then(() => {
+ const plotElement = element.querySelector('.cartesianlayer .scatterlayer .trace .lines');
+ expect(plotElement).not.toBeNull();
+ barGraphView.destroy();
+ });
});
});
@@ -297,19 +367,26 @@ describe("the plugin", function () {
type: "test-object",
name: "Test Object",
telemetry: {
- values: [{
- key: "some-key",
- name: "Some attribute",
- hints: {
- domain: 1
- }
- }, {
- key: "some-other-key",
- name: "Another attribute",
- hints: {
- range: 1
- }
- }]
+ values: [
+ {
+ key: "some-key",
+ source: "some-key",
+ name: "Some attribute",
+ format: "enum",
+ enumerations: [
+ {
+ value: 0,
+ string: "OFF"
+ },
+ {
+ value: 1,
+ string: "ON"
+ }
+ ],
+ hints: {
+ range: 1
+ }
+ }]
}
};
const composition = openmct.composition.get(parent);
@@ -412,7 +489,7 @@ describe("the plugin", function () {
testDomainObject = {
identifier: {
namespace: "",
- key: "test-object"
+ key: "~Some~foo.bar"
},
type: "test-object",
name: "Test Object",
@@ -460,11 +537,16 @@ describe("the plugin", function () {
isAlias: true
}
}
- }
+ },
+ axes: {},
+ useInterpolation: 'linear',
+ useBar: true
},
composition: [
{
- key: '~Some~foo.bar'
+ identifier: {
+ key: '~Some~foo.bar'
+ }
}
]
}
diff --git a/platform/identity/src/IdentityCreationDecorator.js b/src/plugins/charts/scatter/ScatterPlotCompositionPolicy.js
index 5ecbc3758..710fb3402 100644
--- a/platform/identity/src/IdentityCreationDecorator.js
+++ b/src/plugins/charts/scatter/ScatterPlotCompositionPolicy.js
@@ -20,34 +20,38 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
-define(
- function () {
-
- /**
- * Adds a `creator` property to newly-created domain objects.
- * @constructor
- * @augments {platform/commonUI/browse.CreationService}
- * @memberof platform/entanglement
- */
- function IdentityCreationDecorator(identityService, creationService) {
- this.identityService = identityService;
- this.creationService = creationService;
- }
+import { SCATTER_PLOT_KEY } from './scatterPlotConstants';
- IdentityCreationDecorator.prototype.createObject = function (model, parent) {
- var creationService = this.creationService,
- identityService = this.identityService;
+export default function ScatterPlotCompositionPolicy(openmct) {
+ function hasRange(metadata) {
+ const rangeValues = metadata.valuesForHints(['range']).map((value) => {
+ return value.source;
+ });
- return identityService.getUser().then(function (user) {
- if (user && user.key) {
- model.creator = user.key;
- }
+ const uniqueRangeValues = new Set(rangeValues);
- return creationService.createObject(model, parent);
- });
- };
+ return uniqueRangeValues && uniqueRangeValues.size > 1;
+ }
+
+ function hasScatterPlotTelemetry(domainObject) {
+ if (!openmct.telemetry.isTelemetryObject(domainObject)) {
+ return false;
+ }
- return IdentityCreationDecorator;
+ let metadata = openmct.telemetry.getMetadata(domainObject);
+
+ return metadata.values().length > 0 && hasRange(metadata);
}
-);
+ return {
+ allow: function (parent, child) {
+ if (parent.type === SCATTER_PLOT_KEY) {
+ if ((child.type === 'conditionSet') || (!hasScatterPlotTelemetry(child))) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ };
+}
diff --git a/src/plugins/charts/scatter/ScatterPlotForm.vue b/src/plugins/charts/scatter/ScatterPlotForm.vue
new file mode 100644
index 000000000..adef666dc
--- /dev/null
+++ b/src/plugins/charts/scatter/ScatterPlotForm.vue
@@ -0,0 +1,146 @@
+/*****************************************************************************
+* Open MCT, Copyright (c) 2014-2022, United States Government
+* as represented by the Administrator of the National Aeronautics and Space
+* Administration. All rights reserved.
+*
+* Open MCT is licensed under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+* http://www.apache.org/licenses/LICENSE-2.0.
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+* License for the specific language governing permissions and limitations
+* under the License.
+*
+* Open MCT includes source code licensed under additional open source
+* licenses. See the Open Source Licenses file (LICENSES.md) included with
+* this source code distribution or the Licensing information page available
+* at runtime from the About dialog for additional information.
+*****************************************************************************/
+
+<template>
+<span class="form-control">
+ <span
+ class="field control"
+ :class="model.cssClass"
+ >
+ <div
+ class="c-form--sub-grid"
+ >
+ <div class="c-form__row">
+ <span
+ class="req-indicator"
+ :class="{'req': isRequired}"
+ >
+ </span>
+ <label>Minimum X axis value</label>
+ <input
+ ref="domainMin"
+ v-model.number="domainMin"
+ data-field-name="domainMin"
+ type="number"
+ @input="onChange('domainMin')"
+ >
+ </div>
+
+ <div class="c-form__row">
+ <span
+ class="req-indicator"
+ :class="{'req': isRequired}"
+ >
+ </span>
+ <label>Maximum X axis value</label>
+ <input
+ ref="domainMax"
+ v-model.number="domainMax"
+ data-field-name="domainMax"
+ type="number"
+ @input="onChange('domainMax')"
+ >
+ </div>
+
+ <div class="c-form__row">
+ <span
+ class="req-indicator"
+ :class="{'req': isRequired}"
+ >
+ </span>
+ <label>Minimum Y axis value</label>
+ <input
+ ref="rangeMin"
+ v-model.number="rangeMin"
+ data-field-name="rangeMin"
+ type="number"
+ @input="onChange('rangeMin')"
+ >
+ </div>
+
+ <div class="c-form__row">
+ <span
+ class="req-indicator"
+ :class="{'req': isRequired}"
+ >
+ </span>
+ <label>Maximum Y axis value</label>
+ <input
+ ref="rangeMax"
+ v-model.number="rangeMax"
+ data-field-name="rangeMax"
+ type="number"
+ @input="onChange('rangeMax')"
+ >
+ </div>
+ </div>
+ </span>
+</span>
+</template>
+
+<script>
+
+export default {
+ props: {
+ model: {
+ type: Object,
+ required: true
+ }
+ },
+ data() {
+ return {
+ rangeMax: this.model.value.rangeMax,
+ rangeMin: this.model.value.rangeMin,
+ domainMax: this.model.value.domainMax,
+ domainMin: this.model.value.domainMin
+ };
+ },
+ computed: {
+ isRequired() {
+ return [this.rangeMax, this.rangeMin, this.domainMin, this.domainMax].some(value => value !== undefined && value !== '');
+ }
+ },
+ methods: {
+ onChange(property) {
+ if (this[property] === '') {
+ this[property] = undefined;
+ }
+
+ const data = {
+ model: this.model,
+ value: {
+ rangeMax: this.rangeMax,
+ rangeMin: this.rangeMin,
+ domainMax: this.domainMax,
+ domainMin: this.domainMin
+ }
+ };
+
+ if (property) {
+ this.model.validate(data);
+ }
+
+ this.$emit('onChange', data);
+ }
+ }
+};
+</script>
diff --git a/src/plugins/charts/scatter/ScatterPlotView.vue b/src/plugins/charts/scatter/ScatterPlotView.vue
new file mode 100644
index 000000000..f6a69228e
--- /dev/null
+++ b/src/plugins/charts/scatter/ScatterPlotView.vue
@@ -0,0 +1,346 @@
+<!--
+ Open MCT, Copyright (c) 2014-2021, United States Government
+ as represented by the Administrator of the National Aeronautics and Space
+ Administration. All rights reserved.
+
+ Open MCT is licensed under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0.
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ License for the specific language governing permissions and limitations
+ under the License.
+
+ Open MCT includes source code licensed under additional open source
+ licenses. See the Open Source Licenses file (LICENSES.md) included with
+ this source code distribution or the Licensing information page available
+ at runtime from the About dialog for additional information.
+-->
+
+<template>
+<ScatterPlotWithUnderlay
+ class="c-plot c-scatter-chart-view"
+ :data="trace"
+ :plot-axis-title="plotAxisTitle"
+ @subscribe="subscribeToAll"
+ @unsubscribe="removeAllSubscriptions"
+/>
+</template>
+
+<script>
+import ScatterPlotWithUnderlay from './ScatterPlotWithUnderlay.vue';
+import _ from 'lodash';
+
+export default {
+ components: {
+ ScatterPlotWithUnderlay
+ },
+ inject: ['openmct', 'domainObject', 'path'],
+ data() {
+ this.telemetryObjects = {};
+ this.telemetryObjectFormats = {};
+ this.valuesByTimestamp = {};
+ this.subscriptions = [];
+
+ return {
+ trace: []
+ };
+ },
+ computed: {
+ plotAxisTitle() {
+ const { xAxisMetadata = {}, yAxisMetadata = {} } = this.trace[0] || {};
+ const xAxisUnit = xAxisMetadata.units ? `(${xAxisMetadata.units})` : '';
+ const yAxisUnit = yAxisMetadata.units ? `(${yAxisMetadata.units})` : '';
+
+ return {
+ xAxisTitle: `${xAxisMetadata.name || ''} ${xAxisUnit}`,
+ yAxisTitle: `${yAxisMetadata.name || ''} ${yAxisUnit}`
+ };
+ }
+ },
+ mounted() {
+ this.setTimeContext();
+ this.loadComposition();
+ this.reloadTelemetry = this.reloadTelemetry.bind(this);
+ this.reloadTelemetry = _.debounce(this.reloadTelemetry, 500);
+ this.unobserve = this.openmct.objects.observe(this.domainObject, 'configuration.axes', this.reloadTelemetry);
+ this.unobserveUnderlayRanges = this.openmct.objects.observe(this.domainObject, 'configuration.ranges', this.reloadTelemetry);
+ },
+ beforeDestroy() {
+ this.stopFollowingTimeContext();
+
+ if (!this.composition) {
+ return;
+ }
+
+ this.removeAllSubscriptions();
+
+ this.composition.off('add', this.addToComposition);
+ this.composition.off('remove', this.removeTelemetryObject);
+ if (this.unobserve) {
+ this.unobserve();
+ }
+
+ if (this.unobserveUnderlayRanges) {
+ this.unobserveUnderlayRanges();
+ }
+ },
+ methods: {
+ setTimeContext() {
+ this.stopFollowingTimeContext();
+
+ this.timeContext = this.openmct.time.getContextForView(this.path);
+ this.followTimeContext();
+
+ },
+ followTimeContext() {
+ this.timeContext.on('bounds', this.reloadTelemetry);
+ },
+ stopFollowingTimeContext() {
+ if (this.timeContext) {
+ this.timeContext.off('bounds', this.reloadTelemetry);
+ }
+ },
+ addToComposition(telemetryObject) {
+ if (Object.values(this.telemetryObjects).length > 0) {
+ this.confirmRemoval(telemetryObject);
+ } else {
+ this.addTelemetryObject(telemetryObject);
+ }
+ },
+ removeFromComposition(telemetryObject) {
+ let composition = this.domainObject.composition.filter(id =>
+ !this.openmct.objects.areIdsEqual(id, telemetryObject.identifier)
+ );
+
+ this.openmct.objects.mutate(this.domainObject, 'composition', composition);
+ },
+ addTelemetryObject(telemetryObject) {
+ // grab information we need from the added telmetry object
+ const key = this.openmct.objects.makeKeyString(telemetryObject.identifier);
+ this.telemetryObjects[key] = telemetryObject;
+ const metadata = this.openmct.telemetry.getMetadata(telemetryObject);
+ this.telemetryObjectFormats[key] = this.openmct.telemetry.getFormatMap(metadata);
+ this.getDataForTelemetry(key);
+ },
+ confirmRemoval(telemetryObject) {
+ const dialog = this.openmct.overlays.dialog({
+ iconClass: 'alert',
+ message: 'This action will replace the current telemetry source. Do you want to continue?',
+ buttons: [
+ {
+ label: 'Ok',
+ emphasis: true,
+ callback: () => {
+ const oldTelemetryObject = Object.values(this.telemetryObjects)[0];
+ this.removeFromComposition(oldTelemetryObject);
+ this.removeTelemetryObject(oldTelemetryObject.identifier);
+ this.valuesByTimestamp = {};
+ this.addTelemetryObject(telemetryObject);
+ dialog.dismiss();
+ }
+ },
+ {
+ label: 'Cancel',
+ callback: () => {
+ this.removeFromComposition(telemetryObject);
+ dialog.dismiss();
+ }
+ }
+ ]
+ });
+ },
+ getTelemetryProcessor(keyString) {
+ return (telemetry) => {
+ //Check that telemetry object has not been removed since telemetry was requested.
+ const telemetryObject = this.telemetryObjects[keyString];
+ if (!telemetryObject) {
+ return;
+ }
+
+ telemetry.forEach(datum => {
+ this.addDataToGraph(telemetryObject, datum);
+ });
+ this.updateTrace(telemetryObject);
+ };
+ },
+ getAxisMetadata(telemetryObject) {
+ const metadata = this.openmct.telemetry.getMetadata(telemetryObject);
+ if (!metadata) {
+ return {};
+ }
+
+ return metadata.valuesForHints(['range']);
+ },
+ loadComposition() {
+ this.composition = this.openmct.composition.get(this.domainObject);
+ this.composition.on('add', this.addToComposition);
+ this.composition.on('remove', this.removeTelemetryObject);
+ this.composition.load();
+ },
+ reloadTelemetry() {
+ this.valuesByTimestamp = {};
+
+ Object.keys(this.telemetryObjects).forEach(key => {
+ this.getDataForTelemetry(key);
+ });
+ },
+ getDataForTelemetry(key) {
+ const telemetryObject = this.telemetryObjects[key];
+ if (!telemetryObject) {
+ return;
+ }
+
+ const telemetryProcessor = this.getTelemetryProcessor(key);
+ const options = this.getOptions();
+ this.openmct.telemetry.request(telemetryObject, options).then(telemetryProcessor);
+ this.subscribeToObject(telemetryObject);
+ },
+ removeTelemetryObject(identifier) {
+ const key = this.openmct.objects.makeKeyString(identifier);
+ if (this.telemetryObjects[key]) {
+ delete this.telemetryObjects[key];
+ }
+
+ if (this.telemetryObjectFormats && this.telemetryObjectFormats[key]) {
+ delete this.telemetryObjectFormats[key];
+ }
+
+ this.removeSubscription(key);
+ },
+ addDataToGraph(telemetryObject, data) {
+ const key = this.openmct.objects.makeKeyString(telemetryObject.identifier);
+
+ if (data.message) {
+ this.openmct.notifications.alert(data.message);
+ }
+
+ if (!this.domainObject.configuration.axes.xKey || !this.domainObject.configuration.axes.yKey) {
+ return;
+ }
+
+ const timestamp = this.getTimestampForDatum(data, key, telemetryObject);
+ let valueForTimestamp = this.valuesByTimestamp[timestamp] || {};
+
+ //populate x values
+ let metadataKey = this.domainObject.configuration.axes.xKey;
+ if (data[metadataKey] !== undefined) {
+ valueForTimestamp.x = this.format(key, metadataKey, data);
+ }
+
+ metadataKey = this.domainObject.configuration.axes.yKey;
+ if (data[metadataKey] !== undefined) {
+ valueForTimestamp.y = this.format(key, metadataKey, data);
+ }
+
+ this.valuesByTimestamp[timestamp] = valueForTimestamp;
+ },
+ updateTrace(telemetryObject) {
+ const xAndyValues = Object.values(this.valuesByTimestamp);
+ const xValues = xAndyValues.map(value => value.x);
+ const yValues = xAndyValues.map(value => value.y);
+ const axisMetadata = this.getAxisMetadata(telemetryObject);
+ const xAxisMetadata = axisMetadata.find(metadata => metadata.source === this.domainObject.configuration.axes.xKey);
+ let yAxisMetadata = {};
+ if (this.domainObject.configuration.axes.yKey) {
+ yAxisMetadata = axisMetadata.find(metadata => metadata.source === this.domainObject.configuration.axes.yKey);
+ }
+
+ let trace = {
+ key: this.openmct.objects.makeKeyString(this.domainObject.identifier),
+ name: this.domainObject.name,
+ x: xValues,
+ y: yValues,
+ text: yValues.map(String),
+ xAxisMetadata: xAxisMetadata,
+ yAxisMetadata: yAxisMetadata,
+ type: 'scatter',
+ mode: 'markers',
+ marker: {
+ color: this.domainObject.configuration.styles.color
+ },
+ hoverinfo: 'x+y'
+ };
+
+ if (this.domainObject.configuration.ranges !== undefined && this.domainObject.configuration.ranges.domainMin !== undefined && this.domainObject.configuration.ranges.domainMax !== undefined) {
+ trace.xaxis = {
+ min: this.domainObject.configuration.ranges.domainMin,
+ max: this.domainObject.configuration.ranges.domainMax
+ };
+ }
+
+ if (this.domainObject.configuration.ranges !== undefined && this.domainObject.configuration.ranges.rangeMin !== undefined && this.domainObject.configuration.ranges.rangeMax !== undefined) {
+ trace.yaxis = {
+ min: this.domainObject.configuration.ranges.rangeMin,
+ max: this.domainObject.configuration.ranges.rangeMax
+ };
+ }
+
+ this.trace = [trace];
+ },
+ getTimestampForDatum(datum, key, telemetryObject) {
+ const timeSystemKey = this.timeContext.timeSystem().key;
+ const metadata = this.openmct.telemetry.getMetadata(telemetryObject);
+ let metadataValue = metadata.value(timeSystemKey) || { format: timeSystemKey };
+
+ return this.parse(key, metadataValue.source, datum);
+ },
+ format(telemetryObjectKey, metadataKey, data) {
+ const formats = this.telemetryObjectFormats[telemetryObjectKey];
+
+ return formats[metadataKey].format(data);
+ },
+ parse(telemetryObjectKey, metadataKey, datum) {
+ if (!datum) {
+ return;
+ }
+
+ const formats = this.telemetryObjectFormats[telemetryObjectKey];
+
+ return formats[metadataKey].parse(datum);
+ },
+ getOptions() {
+ const { start, end } = this.timeContext.bounds();
+
+ return {
+ end,
+ start
+ };
+ },
+ subscribeToObject(telemetryObject) {
+ const key = this.openmct.objects.makeKeyString(telemetryObject.identifier);
+
+ this.removeSubscription(key);
+
+ const options = this.getOptions();
+ const unsubscribe = this.openmct.telemetry.subscribe(telemetryObject,
+ data => this.addDataToGraph(telemetryObject, data)
+ , options);
+
+ this.subscriptions.push({
+ key,
+ unsubscribe
+ });
+ },
+ subscribeToAll() {
+ const telemetryObjects = Object.values(this.telemetryObjects);
+ telemetryObjects.forEach(this.subscribeToObject);
+ },
+ removeAllSubscriptions() {
+ this.subscriptions.forEach(subscription => subscription.unsubscribe());
+ this.subscriptions = [];
+ },
+ removeSubscription(key) {
+ const found = this.subscriptions.findIndex(subscription => subscription.key === key);
+ if (found > -1) {
+ this.subscriptions[found].unsubscribe();
+ this.subscriptions.splice(found, 1);
+ }
+ }
+ }
+};
+
+</script>
diff --git a/src/plugins/charts/scatter/ScatterPlotViewProvider.js b/src/plugins/charts/scatter/ScatterPlotViewProvider.js
new file mode 100644
index 000000000..338d2eb3e
--- /dev/null
+++ b/src/plugins/charts/scatter/ScatterPlotViewProvider.js
@@ -0,0 +1,79 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2021, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+import ScatterPlotView from './ScatterPlotView.vue';
+import { SCATTER_PLOT_KEY, SCATTER_PLOT_VIEW, TIME_STRIP_KEY } from './scatterPlotConstants.js';
+import Vue from 'vue';
+
+export default function ScatterPlotViewProvider(openmct) {
+ function isCompactView(objectPath) {
+ let isChildOfTimeStrip = objectPath.find(object => object.type === TIME_STRIP_KEY);
+
+ return isChildOfTimeStrip && !openmct.router.isNavigatedObject(objectPath);
+ }
+
+ return {
+ key: SCATTER_PLOT_VIEW,
+ name: 'Scatter Plot',
+ cssClass: 'icon-telemetry',
+ canView(domainObject, objectPath) {
+ return domainObject && domainObject.type === SCATTER_PLOT_KEY;
+ },
+
+ canEdit(domainObject, objectPath) {
+ return domainObject && domainObject.type === SCATTER_PLOT_KEY;
+ },
+
+ view: function (domainObject, objectPath) {
+ let component;
+
+ return {
+ show: function (element) {
+ let isCompact = isCompactView(objectPath);
+ component = new Vue({
+ el: element,
+ components: {
+ ScatterPlotView
+ },
+ provide: {
+ openmct,
+ domainObject,
+ path: objectPath
+ },
+ data() {
+ return {
+ options: {
+ compact: isCompact
+ }
+ };
+ },
+ template: '<scatter-plot-view :options="options"></scatter-plot-view>'
+ });
+ },
+ destroy: function () {
+ component.$destroy();
+ component = undefined;
+ }
+ };
+ }
+ };
+}
diff --git a/src/plugins/charts/scatter/ScatterPlotWithUnderlay.vue b/src/plugins/charts/scatter/ScatterPlotWithUnderlay.vue
new file mode 100644
index 000000000..796a252ac
--- /dev/null
+++ b/src/plugins/charts/scatter/ScatterPlotWithUnderlay.vue
@@ -0,0 +1,393 @@
+<template>
+<div
+ ref="plotWrapper"
+ class="has-local-controls"
+ :class="{ 's-unsynced' : isZoomed }"
+>
+ <div
+ v-if="isZoomed"
+ class="l-state-indicators"
+ >
+ <span
+ class="l-state-indicators__alert-no-lad t-object-alert t-alert-unsynced icon-alert-triangle"
+ title="This plot is not currently displaying the latest data. Reset pan/zoom to view latest data."
+ ></span>
+ </div>
+ <div
+ ref="plot"
+ class="c-scatter-chart"
+ ></div>
+ <div
+ ref="localControl"
+ class="gl-plot__local-controls h-local-controls h-local-controls--overlay-content c-local-controls--show-on-hover"
+ >
+ <button
+ v-if="data.length"
+ class="c-button icon-reset"
+ :disabled="!isZoomed"
+ title="Reset pan/zoom"
+ @click="reset()"
+ >
+ </button>
+ </div>
+</div>
+</template>
+<script>
+import Plotly from 'plotly-basic';
+
+const MULTI_AXES_X_PADDING_PERCENT = {
+ LEFT: 8,
+ RIGHT: 94
+};
+
+import { getValidatedData } from "@/plugins/plan/util";
+
+const PATH_COLORS = ['blue', 'red', 'green'];
+const MARKER_COLOR = 'white';
+
+export default {
+ inject: ['openmct', 'domainObject'],
+ props: {
+ data: {
+ type: Array,
+ default() {
+ return [];
+ }
+ },
+ plotAxisTitle: {
+ type: Object,
+ default() {
+ return {};
+ }
+ }
+ },
+ data() {
+ return {
+ isZoomed: false,
+ yAxisRange: {
+ min: '',
+ max: ''
+ },
+ xAxisRange: {
+ min: '',
+ max: ''
+ }
+ };
+ },
+ watch: {
+ data: {
+ immediate: false,
+ handler: 'updateData'
+ }
+ },
+ mounted() {
+ this.getUnderlayPlotData();
+
+ Plotly.newPlot(this.$refs.plot, Array.from(this.data.concat(this.getShapes(this.shapesData))), this.getLayout(), {
+ responsive: true,
+ displayModeBar: false
+ });
+ this.registerListeners();
+
+ this.$refs.plot.on('plotly_relayout', this.zoom);
+ },
+ beforeDestroy() {
+ if (this.$refs.plot && this.$refs.plot.off) {
+ this.$refs.plot.off('plotly_relayout', this.zoom);
+ }
+
+ if (this.plotResizeObserver) {
+ this.plotResizeObserver.unobserve(this.$refs.plotWrapper);
+ clearTimeout(this.resizeTimer);
+ }
+
+ if (this.unlistenUnderlay) {
+ this.unlistenUnderlay();
+ }
+
+ if (this.unlistenUnderlayRanges) {
+ this.unlistenUnderlayRanges();
+ }
+
+ if (this.unobserveColorChanges) {
+ this.unobserveColorChanges();
+ }
+ },
+ methods: {
+ getUnderlayPlotData() {
+ if (this.domainObject.selectFile) {
+ this.shapesData = getValidatedData(this.domainObject);
+ } else {
+ this.shapesData = [];
+ }
+ },
+ observeForUnderlayPlotChanges() {
+ this.getUnderlayPlotData();
+ this.updateData();
+ },
+ getAxisMinMax() {
+ if (!this.data.length) {
+ return;
+ }
+
+ // For now, use x and y axes min, max values only if an underlay is available
+ if (this.shapesData.length && this.data[0].xaxis) {
+ this.xAxisRange = this.data[0].xaxis;
+ }
+
+ if (this.shapesData.length && this.data[0].yaxis) {
+ this.yAxisRange = this.data[0].yaxis;
+ }
+ },
+ getLayout() {
+ this.getAxisMinMax();
+
+ const yAxesMeta = this.getYAxisMeta();
+ const primaryYaxis = this.getYaxisLayout(yAxesMeta['1']);
+ const xAxisDomain = this.getXAxisDomain(yAxesMeta);
+
+ const shapes = this.shapesData.map((shapeData, index) => {
+ if (!shapeData.x || !shapeData.y
+ || !shapeData.x.length || !shapeData.y.length
+ || shapeData.x.length !== shapeData.y.length) {
+ return "";
+ }
+
+ let path = `M ${shapeData.x[0]},${shapeData.y[0]}`;
+ shapeData.x.forEach((point, shapeIndex) => {
+ if (shapeIndex > 0) {
+ path = `${path} L${point},${shapeData.y[shapeIndex]}`;
+ }
+ });
+
+ return {
+ path,
+ type: 'path',
+ line: {
+ color: PATH_COLORS[index]
+ },
+ opacity: 0.5
+ };
+ });
+
+ return {
+ autosize: true,
+ showlegend: false,
+ textposition: 'auto',
+ font: {
+ family: 'Helvetica Neue, Helvetica, Arial, sans-serif',
+ size: '12px',
+ color: '#666'
+ },
+ xaxis: {
+ domain: xAxisDomain,
+ range: [this.xAxisRange.min, this.xAxisRange.max],
+ title: this.plotAxisTitle.xAxisTitle,
+ automargin: true
+ },
+ yaxis: primaryYaxis,
+ margin: {
+ l: 5,
+ r: 5,
+ t: 5,
+ b: 0
+ },
+ paper_bgcolor: 'transparent',
+ plot_bgcolor: 'transparent',
+ shapes,
+ layer: 'below'
+ };
+ },
+ getYAxisMeta() {
+ const yAxisMeta = {};
+
+ this.data.forEach(datum => {
+ const yAxisMetadata = datum.yAxisMetadata;
+ const range = '1';
+ const side = 'left';
+ const name = yAxisMetadata.name;
+ const unit = yAxisMetadata.units;
+
+ yAxisMeta[range] = {
+ range,
+ side,
+ name,
+ unit
+ };
+ });
+
+ return yAxisMeta;
+ },
+ getXAxisDomain(yAxisMeta) {
+ let leftPaddingPerc = 0;
+ let rightPaddingPerc = 100;
+ let rightSide = yAxisMeta && Object.values(yAxisMeta).filter((axisMeta => axisMeta.side === 'right'));
+ let leftSide = yAxisMeta && Object.values(yAxisMeta).filter((axisMeta => axisMeta.side === 'left'));
+ if (yAxisMeta && rightSide.length > 1) {
+ rightPaddingPerc = MULTI_AXES_X_PADDING_PERCENT.RIGHT;
+ }
+
+ if (yAxisMeta && leftSide.length > 1) {
+ leftPaddingPerc = MULTI_AXES_X_PADDING_PERCENT.LEFT;
+ }
+
+ return [leftPaddingPerc / 100, rightPaddingPerc / 100];
+ },
+ getYaxisLayout(yAxisMeta) {
+ if (!yAxisMeta) {
+ return {};
+ }
+
+ const { name, range, side = 'left', unit } = yAxisMeta;
+ const title = `${name} ${unit ? '(' + unit + ')' : ''}`;
+ const yaxis = {
+ automargin: true,
+ title
+ };
+
+ let yRange = this.yAxisRange;
+ if (range === '1') {
+ yaxis.range = [yRange.min, yRange.max];
+
+ return yaxis;
+ }
+
+ yaxis.range = [yRange.min, yRange.max];
+ yaxis.anchor = side.toLowerCase() === 'left'
+ ? 'free'
+ : 'x';
+ yaxis.showline = side.toLowerCase() === 'left';
+ yaxis.side = side.toLowerCase();
+ yaxis.overlaying = 'y';
+ yaxis.position = 0.01;
+
+ return yaxis;
+ },
+ registerListeners() {
+ this.unobserveColorChanges = this.openmct.objects.observe(this.domainObject, 'configuration.styles.color', this.updateColors);
+ this.unlistenUnderlay = this.openmct.objects.observe(this.domainObject, 'selectFile', this.observeForUnderlayPlotChanges);
+ this.unlistenUnderlayRanges = this.openmct.objects.observe(this.domainObject, 'configuration.ranges', this.updateData);
+ this.resizeTimer = false;
+ if (window.ResizeObserver) {
+ this.plotResizeObserver = new ResizeObserver(() => {
+ // debounce and trigger window resize so that plotly can resize the plot
+ clearTimeout(this.resizeTimer);
+ this.resizeTimer = setTimeout(() => {
+ window.dispatchEvent(new Event('resize'));
+ }, 250);
+ });
+ this.plotResizeObserver.observe(this.$refs.plotWrapper);
+ }
+ },
+ updateColors() {
+ const colors = [];
+ const indices = [];
+ this.data.forEach((item, index) => {
+ const colorExists = this.domainObject.configuration.styles.color;
+ indices.push(index);
+ if (colorExists) {
+ colors.push(this.domainObject.configuration.styles.color);
+ } else {
+ colors.push(item.marker.color);
+ }
+ });
+ const plotUpdate = {
+ 'marker.color': colors
+ };
+
+ Plotly.restyle(this.$refs.plot, plotUpdate, indices);
+ },
+ reset() {
+ this.isZoomed = false;
+
+ this.updatePlot();
+ this.$emit('subscribe');
+ },
+ updateData() {
+ this.updatePlot();
+ },
+ updateLocalControlPosition() {
+ const localControl = this.$refs.localControl;
+ localControl.style.display = 'none';
+
+ const plot = this.$refs.plot;
+ const bgLayer = this.$el.querySelector('.bglayer');
+
+ const plotBoundingRect = plot.getBoundingClientRect();
+ const bgLayerBoundingRect = bgLayer.getBoundingClientRect();
+
+ const top = bgLayerBoundingRect.top - plotBoundingRect.top + 5;
+ const left = bgLayerBoundingRect.left - plotBoundingRect.left + 5;
+
+ localControl.style.top = `${top}px`;
+ localControl.style.left = `${left}px`;
+ localControl.style.display = 'block';
+ },
+ updatePlot() {
+ if (!this.$refs || !this.$refs.plot || this.isZoomed) {
+ return;
+ }
+
+ Plotly.react(this.$refs.plot, Array.from(this.data.concat(this.getShapes(this.shapesData))), this.getLayout());
+ },
+ zoom(eventData) {
+ const autorange = eventData['xaxis.autorange'];
+ const { autosize } = eventData;
+
+ if (autosize || autorange) {
+ return;
+ }
+
+ this.isZoomed = true;
+ this.$emit('unsubscribe');
+ },
+ getShapes() {
+ let markerData = {
+ x: [],
+ y: []
+ };
+ const shapes = this.shapesData.map((shapeData, index) => {
+ if (!shapeData.x || !shapeData.y
+ || !shapeData.x.length || !shapeData.y.length
+ || shapeData.x.length !== shapeData.y.length) {
+ return "";
+ }
+
+ let text = [];
+ shapeData.x.forEach((point) => {
+ text.push(`${parseFloat(point).toPrecision(2)}`);
+ });
+
+ markerData.x = markerData.x.concat(shapeData.x);
+ markerData.y = markerData.y.concat(shapeData.y);
+
+ return {
+ x: shapeData.x,
+ y: shapeData.y,
+ mode: 'text',
+ text,
+ textfont: {
+ family: 'Helvetica Neue, Helvetica, Arial, sans-serif',
+ size: '12px',
+ color: PATH_COLORS[index]
+ },
+ opacity: 0.5
+ };
+ });
+
+ shapes.push({
+ x: markerData.x,
+ y: markerData.y,
+ mode: "markers",
+ marker: {
+ size: 6,
+ color: MARKER_COLOR
+ }
+ });
+
+ return shapes;
+ }
+ }
+};
+</script>
+
diff --git a/src/plugins/charts/inspector/BarGraphOptions.vue b/src/plugins/charts/scatter/inspector/PlotOptions.vue
index 097d4f852..a72fcb8c9 100644
--- a/src/plugins/charts/inspector/BarGraphOptions.vue
+++ b/src/plugins/charts/scatter/inspector/PlotOptions.vue
@@ -1,5 +1,5 @@
<!--
- Open MCT, Copyright (c) 2014-2020, United States Government
+ Open MCT, Copyright (c) 2014-2022, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
@@ -20,31 +20,28 @@
at runtime from the About dialog for additional information.
-->
<template>
-<ul class="c-tree c-bar-graph-options">
- <h2 title="Display properties for this object">Bar Graph Series</h2>
- <li v-for="series in domainObject.composition"
- :key="series.key"
- >
- <series-options :item="series"
- :color-palette="colorPalette"
- />
- </li>
-</ul>
+<div>
+ <div v-if="canEdit">
+ <plot-options-edit />
+ </div>
+ <div v-else>
+ <plot-options-browse />
+ </div>
+</div>
</template>
<script>
-import SeriesOptions from "./SeriesOptions.vue";
-import ColorPalette from '@/ui/color/ColorPalette';
-
+import PlotOptionsBrowse from "./PlotOptionsBrowse.vue";
+import PlotOptionsEdit from "./PlotOptionsEdit.vue";
export default {
components: {
- SeriesOptions
+ PlotOptionsBrowse,
+ PlotOptionsEdit
},
inject: ['openmct', 'domainObject'],
data() {
return {
- isEditing: this.openmct.editor.isEditing(),
- colorPalette: this.colorPalette
+ isEditing: this.openmct.editor.isEditing()
};
},
computed: {
@@ -52,9 +49,6 @@ export default {
return this.isEditing && !this.domainObject.locked;
}
},
- beforeMount() {
- this.colorPalette = new ColorPalette();
- },
mounted() {
this.openmct.editor.on('isEditing', this.setEditState);
},
diff --git a/src/plugins/charts/scatter/inspector/PlotOptionsBrowse.vue b/src/plugins/charts/scatter/inspector/PlotOptionsBrowse.vue
new file mode 100644
index 000000000..c7af21973
--- /dev/null
+++ b/src/plugins/charts/scatter/inspector/PlotOptionsBrowse.vue
@@ -0,0 +1,153 @@
+<!--
+ Open MCT, Copyright (c) 2014-2022, United States Government
+ as represented by the Administrator of the National Aeronautics and Space
+ Administration. All rights reserved.
+
+ Open MCT is licensed under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0.
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ License for the specific language governing permissions and limitations
+ under the License.
+
+ Open MCT includes source code licensed under additional open source
+ licenses. See the Open Source Licenses file (LICENSES.md) included with
+ this source code distribution or the Licensing information page available
+ at runtime from the About dialog for additional information.
+-->
+<template>
+<div class="js-plot-options-browse grid-properties">
+ <ul class="l-inspector-part">
+ <h2 title="Object view settings">Settings</h2>
+ <li class="grid-row">
+ <div
+ class="grid-cell label"
+ title="X axis selection"
+ >X Axis</div>
+ <div class="grid-cell value">{{ xKeyLabel }}</div>
+ </li>
+ <li class="grid-row">
+ <div
+ class="grid-cell label"
+ title="Y axis selection"
+ >Y Axis</div>
+ <div class="grid-cell value">{{ yKeyLabel }}</div>
+ </li>
+ <ColorSwatch
+ :current-color="currentColor"
+ edit-title="Manually set the color for this plot"
+ view-title="The marker color for this plot"
+ short-label="Color"
+ />
+ </ul>
+</div>
+</template>
+
+<script>
+import ColorSwatch from "../../../../ui/color/ColorSwatch.vue";
+import Color from "../../../../ui/color/Color";
+import ColorPalette from "../../../../ui/color/ColorPalette";
+
+export default {
+ components: { ColorSwatch },
+ inject: ['openmct', 'domainObject'],
+ data() {
+ return {
+ xKeyLabel: '',
+ yKeyLabel: '',
+ currentColor: undefined
+ };
+ },
+ mounted() {
+ this.plotSeries = [];
+ this.colorPalette = new ColorPalette();
+ this.initColor();
+ this.composition = this.openmct.composition.get(this.domainObject);
+ this.registerListeners();
+ this.composition.load();
+ },
+ beforeDestroy() {
+ this.stopListening();
+ },
+ methods: {
+ initColor() {
+ // this is called before the plot is initialized
+ if (!this.domainObject.configuration.styles || !this.domainObject.configuration.styles.color) {
+ const color = this.colorPalette.getNextColor().asHexString();
+ this.domainObject.configuration.styles = {
+ color
+ };
+ }
+
+ this.currentColor = this.domainObject.configuration.styles.color;
+ const colorObject = Color.fromHexString(this.currentColor);
+
+ this.colorPalette.remove(colorObject);
+ },
+ registerListeners() {
+ this.composition.on('add', this.addSeries);
+ this.composition.on('remove', this.removeSeries);
+ this.unobserve = this.openmct.objects.observe(this.domainObject, 'configuration.axes', this.setAxesLabels);
+ },
+ stopListening() {
+ this.composition.off('add', this.addSeries);
+ this.composition.off('remove', this.removeSeries);
+ if (this.unobserve) {
+ this.unobserve();
+ }
+ },
+ addSeries(series, index) {
+ this.$set(this.plotSeries, this.plotSeries.length, series);
+ this.setAxesLabels();
+ },
+ removeSeries(series) {
+ const index = this.plotSeries.findIndex(plotSeries => this.openmct.objects.areIdsEqual(series.identifier, plotSeries.identifier));
+ if (index !== undefined) {
+ this.$delete(this.plotSeries, index);
+ this.setAxesLabels();
+ }
+ },
+ setAxesLabels() {
+ let xKeyOptions = [];
+ let yKeyOptions = [];
+ if (this.plotSeries.length <= 0) {
+ return;
+ }
+
+ const series = this.plotSeries[0];
+ const metadataValues = this.openmct.telemetry.getMetadata(series).valuesForHints(['range']);
+
+ metadataValues.forEach((metadataValue) => {
+ xKeyOptions.push({
+ name: metadataValue.name || metadataValue.key,
+ value: metadataValue.source || metadataValue.key
+ });
+ yKeyOptions.push({
+ name: metadataValue.name || metadataValue.key,
+ value: metadataValue.source || metadataValue.key
+ });
+ });
+ let xKeyOptionIndex;
+ let yKeyOptionIndex;
+
+ if (this.domainObject.configuration.axes.xKey) {
+ xKeyOptionIndex = xKeyOptions.findIndex(option => option.value === this.domainObject.configuration.axes.xKey);
+ if (xKeyOptionIndex > -1) {
+ this.xKeyLabel = xKeyOptions[xKeyOptionIndex].name;
+ }
+ }
+
+ if (metadataValues.length > 1 && this.domainObject.configuration.axes.yKey) {
+ yKeyOptionIndex = yKeyOptions.findIndex(option => option.value === this.domainObject.configuration.axes.yKey);
+ if (yKeyOptionIndex > -1) {
+ this.yKeyLabel = yKeyOptions[yKeyOptionIndex].name;
+ }
+ }
+ }
+ }
+};
+</script>
diff --git a/src/plugins/charts/scatter/inspector/PlotOptionsEdit.vue b/src/plugins/charts/scatter/inspector/PlotOptionsEdit.vue
new file mode 100644
index 000000000..6781a2777
--- /dev/null
+++ b/src/plugins/charts/scatter/inspector/PlotOptionsEdit.vue
@@ -0,0 +1,262 @@
+<!--
+ Open MCT, Copyright (c) 2014-2022, United States Government
+ as represented by the Administrator of the National Aeronautics and Space
+ Administration. All rights reserved.
+
+ Open MCT is licensed under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0.
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ License for the specific language governing permissions and limitations
+ under the License.
+
+ Open MCT includes source code licensed under additional open source
+ licenses. See the Open Source Licenses file (LICENSES.md) included with
+ this source code distribution or the Licensing information page available
+ at runtime from the About dialog for additional information.
+-->
+<template>
+<div class="js-plot-options-edit grid-properties">
+ <ul class="l-inspector-part">
+ <h2 title="Object view settings">Settings</h2>
+ <li class="grid-row">
+ <div
+ class="grid-cell label"
+ title="X axis selection."
+ >X Axis</div>
+ <div class="grid-cell value">
+ <select
+ v-model="xKey"
+ @change="updateForm('xKey')"
+ >
+ <option
+ v-for="option in xKeyOptions"
+ :key="`xKey-${option.value}`"
+ :value="option.value"
+ :selected="option.value == xKey"
+ >
+ {{ option.name }}
+ </option>
+ </select>
+ </div>
+ </li>
+ <li class="grid-row">
+ <div
+ class="grid-cell label"
+ title="Y axis selection."
+ >Y Axis</div>
+ <div class="grid-cell value">
+ <select
+ v-model="yKey"
+ @change="updateForm('yKey')"
+ >
+ <option
+ v-for="option in yKeyOptions"
+ :key="`yKey-${option.value}`"
+ :value="option.value"
+ :selected="option.value == yKey"
+ >
+ {{ option.name }}
+ </option>
+ </select>
+ </div>
+ </li>
+ <ColorSwatch
+ :current-color="currentColor"
+ title="Manually set the line and marker color for this plot."
+ edit-title="Manually set the line and marker color for this plot."
+ view-title="The line and marker color for this plot."
+ short-label="Color"
+ @colorSet="setColor"
+ />
+ </ul>
+</div>
+</template>
+<script>
+import Color from "../../../../ui/color/Color";
+import ColorPalette from "../../../../ui/color/ColorPalette";
+import ColorSwatch from "../../../../ui/color/ColorSwatch.vue";
+
+export default {
+ components: { ColorSwatch },
+ inject: ['openmct', 'domainObject'],
+ data() {
+ return {
+ xKey: undefined,
+ yKey: undefined,
+ xKeyOptions: [],
+ yKeyOptions: [],
+ currentColor: undefined
+ };
+ },
+ mounted() {
+ this.plotSeries = [];
+ this.colorPalette = new ColorPalette();
+ this.initColor();
+ this.composition = this.openmct.composition.get(this.domainObject);
+ this.registerListeners();
+ this.composition.load();
+ },
+ beforeDestroy() {
+ this.stopListening();
+ },
+ methods: {
+ initColor() {
+ // this is called before the plot is initialized
+ if (!this.domainObject.configuration.styles || !this.domainObject.configuration.styles.color) {
+ const color = this.colorPalette.getNextColor().asHexString();
+ this.domainObject.configuration.styles = {
+ color
+ };
+ }
+
+ this.currentColor = this.domainObject.configuration.styles.color;
+ const colorObject = Color.fromHexString(this.currentColor);
+
+ this.colorPalette.remove(colorObject);
+ },
+ setColor(chosenColor) {
+ this.currentColor = chosenColor.asHexString();
+ this.openmct.objects.mutate(
+ this.domainObject,
+ `configuration.styles.color`,
+ this.currentColor
+ );
+ },
+ registerListeners() {
+ this.composition.on('add', this.addSeries);
+ this.composition.on('remove', this.removeSeries);
+ this.unobserve = this.openmct.objects.observe(this.domainObject, 'configuration.axes', this.setupOptions);
+ },
+ stopListening() {
+ this.composition.off('add', this.addSeries);
+ this.composition.off('remove', this.removeSeries);
+ if (this.unobserve) {
+ this.unobserve();
+ }
+ },
+ addSeries(series, index) {
+ this.$set(this.plotSeries, this.plotSeries.length, series);
+ this.setupOptions();
+ },
+ removeSeries(seriesIdentifier) {
+ const index = this.plotSeries.findIndex(plotSeries => this.openmct.objects.areIdsEqual(seriesIdentifier, plotSeries.identifier));
+ if (index >= 0) {
+ this.$delete(this.plotSeries, index);
+ this.setupOptions();
+ }
+ },
+ setupOptions() {
+ this.xKeyOptions = [];
+ this.yKeyOptions = [];
+ if (this.plotSeries.length <= 0) {
+ return;
+ }
+
+ let update = false;
+ const series = this.plotSeries[0];
+ const metadataValues = this.openmct.telemetry.getMetadata(series).valuesForHints(['range']);
+ metadataValues.forEach((metadataValue) => {
+ this.xKeyOptions.push({
+ name: metadataValue.name || metadataValue.key,
+ value: metadataValue.source || metadataValue.key
+ });
+ this.yKeyOptions.push({
+ name: metadataValue.name || metadataValue.key,
+ value: metadataValue.source || metadataValue.key
+ });
+ });
+
+ let xKeyOptionIndex;
+ let yKeyOptionIndex;
+
+ if (this.domainObject.configuration.axes.xKey) {
+ xKeyOptionIndex = this.xKeyOptions.findIndex(option => option.value === this.domainObject.configuration.axes.xKey);
+ if (xKeyOptionIndex > -1) {
+ this.xKey = this.xKeyOptions[xKeyOptionIndex].value;
+ } else {
+ this.xKey = undefined;
+ }
+ }
+
+ if (this.xKey === undefined) {
+ update = true;
+ xKeyOptionIndex = 0;
+ this.xKey = this.xKeyOptions[xKeyOptionIndex].value;
+ }
+
+ if (metadataValues.length > 1) {
+ if (this.domainObject.configuration.axes.yKey) {
+ yKeyOptionIndex = this.yKeyOptions.findIndex(option => option.value === this.domainObject.configuration.axes.yKey);
+ if (yKeyOptionIndex > -1 && yKeyOptionIndex !== xKeyOptionIndex) {
+ this.yKey = this.yKeyOptions[yKeyOptionIndex].value;
+ } else {
+ this.yKey = undefined;
+ }
+ }
+
+ if (this.yKey === undefined) {
+ update = true;
+ yKeyOptionIndex = this.yKeyOptions.findIndex((option, index) => index !== xKeyOptionIndex);
+ this.yKey = this.yKeyOptions[yKeyOptionIndex].value;
+ }
+
+ this.yKeyOptions = this.yKeyOptions.map((option, index) => {
+ if (index === xKeyOptionIndex) {
+ option.name = `${option.name} (swap)`;
+ option.swap = yKeyOptionIndex;
+ } else {
+ option.name = option.name.replace(' (swap)', '');
+ option.swap = undefined;
+ }
+
+ return option;
+ });
+ }
+
+ this.xKeyOptions = this.xKeyOptions.map((option, index) => {
+ if (index === yKeyOptionIndex) {
+ option.name = `${option.name} (swap)`;
+ option.swap = xKeyOptionIndex;
+ } else {
+ option.name = option.name.replace(' (swap)', '');
+ option.swap = undefined;
+ }
+
+ return option;
+ });
+
+ if (update === true) {
+ this.saveConfiguration();
+ }
+ },
+ updateForm(property) {
+ if (property === 'xKey') {
+ const xKeyOption = this.xKeyOptions.find(option => option.value === this.xKey);
+ if (xKeyOption.swap !== undefined) {
+ //swap
+ this.yKey = this.xKeyOptions[xKeyOption.swap].value;
+ }
+ } else if (property === 'yKey') {
+ const yKeyOption = this.yKeyOptions.find(option => option.value === this.yKey);
+ if (yKeyOption.swap !== undefined) {
+ //swap
+ this.xKey = this.yKeyOptions[yKeyOption.swap].value;
+ }
+ }
+
+ this.saveConfiguration();
+ },
+ saveConfiguration() {
+ this.openmct.objects.mutate(this.domainObject, `configuration.axes`, {
+ xKey: this.xKey,
+ yKey: this.yKey
+ });
+ }
+ }
+};
+</script>
diff --git a/src/plugins/charts/scatter/inspector/ScatterPlotInspectorViewProvider.js b/src/plugins/charts/scatter/inspector/ScatterPlotInspectorViewProvider.js
new file mode 100644
index 000000000..54487dfe3
--- /dev/null
+++ b/src/plugins/charts/scatter/inspector/ScatterPlotInspectorViewProvider.js
@@ -0,0 +1,48 @@
+import { SCATTER_PLOT_INSPECTOR_KEY, SCATTER_PLOT_KEY } from '../scatterPlotConstants';
+import Vue from 'vue';
+import PlotOptions from "./PlotOptions.vue";
+
+export default function ScatterPlotInspectorViewProvider(openmct) {
+ return {
+ key: SCATTER_PLOT_INSPECTOR_KEY,
+ name: 'Bar Graph Inspector View',
+ canView: function (selection) {
+ if (selection.length === 0 || selection[0].length === 0) {
+ return false;
+ }
+
+ let object = selection[0][0].context.item;
+
+ return object
+ && object.type === SCATTER_PLOT_KEY;
+ },
+ view: function (selection) {
+ let component;
+
+ return {
+ show: function (element) {
+ component = new Vue({
+ el: element,
+ components: {
+ PlotOptions
+ },
+ provide: {
+ openmct,
+ domainObject: selection[0][0].context.item
+ },
+ template: '<plot-options></plot-options>'
+ });
+ },
+ destroy: function () {
+ if (component) {
+ component.$destroy();
+ component = undefined;
+ }
+ }
+ };
+ },
+ priority: function () {
+ return 1;
+ }
+ };
+}
diff --git a/src/plugins/charts/scatter/plugin.js b/src/plugins/charts/scatter/plugin.js
new file mode 100644
index 000000000..600c2970f
--- /dev/null
+++ b/src/plugins/charts/scatter/plugin.js
@@ -0,0 +1,127 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+import { SCATTER_PLOT_KEY } from './scatterPlotConstants.js';
+import ScatterPlotViewProvider from './ScatterPlotViewProvider';
+import ScatterPlotInspectorViewProvider from './inspector/ScatterPlotInspectorViewProvider';
+import ScatterPlotCompositionPolicy from './ScatterPlotCompositionPolicy';
+import Vue from "vue";
+import ScatterPlotForm from "./ScatterPlotForm.vue";
+
+export default function () {
+ return function install(openmct) {
+ openmct.forms.addNewFormControl('scatter-plot-form-control', getScatterPlotFormControl(openmct));
+
+ openmct.types.addType(SCATTER_PLOT_KEY, {
+ key: SCATTER_PLOT_KEY,
+ name: "Scatter Plot",
+ cssClass: "icon-plot-scatter",
+ description: "View data as a scatter plot.",
+ creatable: true,
+ initialize: function (domainObject) {
+ domainObject.composition = [];
+ domainObject.configuration = {
+ styles: {},
+ axes: {},
+ ranges: {}
+ };
+ },
+ form: [
+ {
+ name: 'Underlay data (JSON file)',
+ key: 'selectFile',
+ control: 'file-input',
+ text: 'Select File...',
+ type: 'application/json',
+ removable: true,
+ hideFromInspector: true,
+ property: [
+ "selectFile"
+ ]
+ },
+ {
+ name: "Underlay ranges",
+ control: "scatter-plot-form-control",
+ cssClass: "l-input",
+ key: "scatterPlotForm",
+ required: false,
+ hideFromInspector: false,
+ property: [
+ "configuration",
+ "ranges"
+ ],
+ validate: ({ value }, callback) => {
+ const { rangeMin, rangeMax, domainMin, domainMax } = value;
+ const valid = {
+ rangeMin,
+ rangeMax,
+ domainMin,
+ domainMax
+ };
+
+ if (callback) {
+ callback(valid);
+ }
+
+ const values = Object.values(valid);
+ const hasAllValues = values.every(rangeValue => rangeValue !== undefined);
+ const hasNoValues = values.every(rangeValue => rangeValue === undefined);
+
+ return hasAllValues || hasNoValues;
+ }
+ }
+ ],
+ priority: 891
+ });
+
+ openmct.objectViews.addProvider(new ScatterPlotViewProvider(openmct));
+
+ openmct.inspectorViews.addProvider(new ScatterPlotInspectorViewProvider(openmct));
+
+ openmct.composition.addPolicy(new ScatterPlotCompositionPolicy(openmct).allow);
+ };
+
+ function getScatterPlotFormControl(openmct) {
+ return {
+ show(element, model, onChange) {
+ const rowComponent = new Vue({
+ el: element,
+ components: {
+ ScatterPlotForm
+ },
+ provide: {
+ openmct
+ },
+ data() {
+ return {
+ model,
+ onChange
+ };
+ },
+ template: `<scatter-plot-form :model="model" @onChange="onChange"></scatter-plot-form>`
+ });
+
+ return rowComponent;
+ }
+ };
+ }
+}
+
diff --git a/src/plugins/charts/scatter/pluginSpec.js b/src/plugins/charts/scatter/pluginSpec.js
new file mode 100644
index 000000000..2eb17c7a4
--- /dev/null
+++ b/src/plugins/charts/scatter/pluginSpec.js
@@ -0,0 +1,421 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+import {createOpenMct, resetApplicationState} from "utils/testing";
+import Vue from "vue";
+import ScatterPlotPlugin from "./plugin";
+import ScatterPlot from './ScatterPlotView.vue';
+import EventEmitter from "EventEmitter";
+import { SCATTER_PLOT_VIEW, SCATTER_PLOT_KEY } from './scatterPlotConstants';
+
+describe("the plugin", function () {
+ let element;
+ let child;
+ let openmct;
+ let telemetryPromise;
+ let telemetryPromiseResolve;
+ let mockObjectPath;
+
+ beforeEach((done) => {
+ mockObjectPath = [
+ {
+ name: 'mock folder',
+ type: 'fake-folder',
+ identifier: {
+ key: 'mock-folder',
+ namespace: ''
+ }
+ }
+ ];
+ const testTelemetry = [
+ {
+ 'utc': 1,
+ 'some-key': 'some-value 1',
+ 'some-other-key': 'some-other-value 1'
+ },
+ {
+ 'utc': 2,
+ 'some-key': 'some-value 2',
+ 'some-other-key': 'some-other-value 2'
+ },
+ {
+ 'utc': 3,
+ 'some-key': 'some-value 3',
+ 'some-other-key': 'some-other-value 3'
+ }
+ ];
+
+ openmct = createOpenMct();
+
+ telemetryPromise = new Promise((resolve) => {
+ telemetryPromiseResolve = resolve;
+ });
+
+ spyOn(openmct.telemetry, 'request').and.callFake(() => {
+ telemetryPromiseResolve(testTelemetry);
+
+ return telemetryPromise;
+ });
+
+ openmct.install(new ScatterPlotPlugin());
+
+ element = document.createElement("div");
+ element.style.width = "640px";
+ element.style.height = "480px";
+ child = document.createElement("div");
+ child.style.width = "640px";
+ child.style.height = "480px";
+ element.appendChild(child);
+ document.body.appendChild(element);
+
+ spyOn(window, 'ResizeObserver').and.returnValue({
+ observe() {},
+ unobserve() {},
+ disconnect() {}
+ });
+
+ openmct.time.timeSystem("utc", {
+ start: 0,
+ end: 4
+ });
+
+ openmct.types.addType("test-object", {
+ creatable: true
+ });
+
+ openmct.on("start", done);
+ openmct.startHeadless();
+ });
+
+ afterEach((done) => {
+ openmct.time.timeSystem('utc', {
+ start: 0,
+ end: 1
+ });
+ resetApplicationState(openmct).then(done).catch(done);
+ });
+
+ describe("The scatter plot view", () => {
+ let testDomainObject;
+ let scatterPlotObject;
+ // eslint-disable-next-line no-unused-vars
+ let component;
+ let mockComposition;
+
+ beforeEach(async () => {
+ scatterPlotObject = {
+ identifier: {
+ namespace: "",
+ key: "test-plot"
+ },
+ type: "telemetry.plot.scatter-plot",
+ name: "Test Scatter Plot",
+ configuration: {
+ axes: {},
+ styles: {}
+ }
+ };
+
+ testDomainObject = {
+ identifier: {
+ namespace: "",
+ key: "test-object"
+ },
+ type: "test-object",
+ name: "Test Object",
+ telemetry: {
+ values: [{
+ key: "utc",
+ format: "utc",
+ name: "Time",
+ hints: {
+ domain: 1
+ }
+ }, {
+ key: "some-key",
+ name: "Some attribute",
+ hints: {
+ range: 1
+ }
+ }, {
+ key: "some-other-key",
+ name: "Another attribute",
+ hints: {
+ range: 2
+ }
+ }]
+ }
+ };
+
+ mockComposition = new EventEmitter();
+ mockComposition.load = () => {
+ mockComposition.emit('add', testDomainObject);
+
+ return [testDomainObject];
+ };
+
+ spyOn(openmct.composition, 'get').and.returnValue(mockComposition);
+
+ let viewContainer = document.createElement("div");
+ child.append(viewContainer);
+ component = new Vue({
+ el: viewContainer,
+ components: {
+ ScatterPlot
+ },
+ provide: {
+ openmct: openmct,
+ domainObject: scatterPlotObject,
+ composition: openmct.composition.get(scatterPlotObject)
+ },
+ template: "<ScatterPlot></ScatterPlot>"
+ });
+
+ await Vue.nextTick();
+ });
+
+ it("provides a scatter plot view", () => {
+ const applicableViews = openmct.objectViews.get(scatterPlotObject, mockObjectPath);
+ const plotViewProvider = applicableViews.find((viewProvider) => viewProvider.key === SCATTER_PLOT_VIEW);
+ expect(plotViewProvider).toBeDefined();
+ });
+
+ it("Renders plotly scatter plot", () => {
+ let scatterPlotElement = element.querySelectorAll(".plotly");
+ expect(scatterPlotElement.length).toBe(1);
+ });
+ });
+
+ describe("the scatter plot objects", () => {
+ const mockObject = {
+ name: 'A very nice scatter plot',
+ key: SCATTER_PLOT_KEY,
+ creatable: true
+ };
+
+ it('defines a scatter plot object type with the correct key', () => {
+ const objectDef = openmct.types.get(SCATTER_PLOT_KEY).definition;
+ expect(objectDef.key).toEqual(mockObject.key);
+ });
+
+ it('is creatable', () => {
+ const objectDef = openmct.types.get(SCATTER_PLOT_KEY).definition;
+ expect(objectDef.creatable).toEqual(mockObject.creatable);
+ });
+ });
+
+ describe("The scatter plot composition policy", () => {
+ it("allows composition for telemetry that contain at least 2 ranges", () => {
+ const parent = {
+ "composition": [],
+ "configuration": {
+ axes: {},
+ styles: {}
+ },
+ "name": "Some Scatter Plot",
+ "type": "telemetry.plot.scatter-plot",
+ "location": "mine",
+ "modified": 1631005183584,
+ "persisted": 1631005183502,
+ "identifier": {
+ "namespace": "",
+ "key": "b78e7e23-f2b8-4776-b1f0-3ff778f5c8a9"
+ }
+ };
+ const testTelemetryObject = {
+ identifier: {
+ namespace: "",
+ key: "test-object"
+ },
+ type: "test-object",
+ name: "Test Object",
+ telemetry: {
+ values: [{
+ key: "some-key",
+ name: "Some attribute",
+ hints: {
+ domain: 1
+ }
+ }, {
+ key: "some-other-key",
+ name: "Another attribute",
+ hints: {
+ range: 1
+ }
+ }, {
+ key: "some-other-key2",
+ name: "Another attribute2",
+ hints: {
+ range: 2
+ }
+ }]
+ }
+ };
+ const composition = openmct.composition.get(parent);
+ expect(() => {
+ composition.add(testTelemetryObject);
+ }).not.toThrow();
+ expect(parent.composition.length).toBe(1);
+ });
+
+ it("disallows composition for telemetry that don't contain at least 2 range hints", () => {
+ const parent = {
+ "composition": [],
+ "configuration": {
+ axes: {},
+ styles: {}
+ },
+ "name": "Some Scatter Plot",
+ "type": "telemetry.plot.scatter-plot",
+ "location": "mine",
+ "modified": 1631005183584,
+ "persisted": 1631005183502,
+ "identifier": {
+ "namespace": "",
+ "key": "b78e7e23-f2b8-4776-b1f0-3ff778f5c8a9"
+ }
+ };
+ const testTelemetryObject = {
+ identifier: {
+ namespace: "",
+ key: "test-object"
+ },
+ type: "test-object",
+ name: "Test Object",
+ telemetry: {
+ values: [{
+ key: "some-key",
+ name: "Some attribute",
+ hints: {
+ domain: 1
+ }
+ }, {
+ key: "some-other-key",
+ name: "Another attribute",
+ hints: {
+ range: 1
+ }
+ }]
+ }
+ };
+ const composition = openmct.composition.get(parent);
+ expect(() => {
+ composition.add(testTelemetryObject);
+ }).toThrow();
+ expect(parent.composition.length).toBe(0);
+ });
+ });
+ describe('the inspector view', () => {
+ let mockComposition;
+ let testDomainObject;
+ let selection;
+ let plotInspectorView;
+ let viewContainer;
+ let optionsElement;
+ beforeEach(async () => {
+ testDomainObject = {
+ identifier: {
+ namespace: "",
+ key: "test-object"
+ },
+ type: "test-object",
+ name: "Test Object",
+ telemetry: {
+ values: [{
+ key: "utc",
+ format: "utc",
+ name: "Time",
+ hints: {
+ domain: 1
+ }
+ }, {
+ key: "some-key",
+ name: "Some attribute",
+ hints: {
+ range: 1
+ }
+ }, {
+ key: "some-other-key",
+ name: "Another attribute",
+ hints: {
+ range: 2
+ }
+ }]
+ }
+ };
+
+ selection = [
+ [
+ {
+ context: {
+ item: {
+ id: "test-object",
+ identifier: {
+ key: "test-object",
+ namespace: ''
+ },
+ type: "telemetry.plot.scatter-plot",
+ configuration: {
+ axes: {},
+ styles: {
+ }
+ },
+ composition: [
+ {
+ key: '~Some~foo.scatter'
+ }
+ ]
+ }
+ }
+ }
+ ]
+ ];
+
+ mockComposition = new EventEmitter();
+ mockComposition.load = () => {
+ mockComposition.emit('add', testDomainObject);
+
+ return [testDomainObject];
+ };
+
+ spyOn(openmct.composition, 'get').and.returnValue(mockComposition);
+
+ viewContainer = document.createElement('div');
+ child.append(viewContainer);
+
+ const applicableViews = openmct.inspectorViews.get(selection);
+ plotInspectorView = applicableViews[0];
+ plotInspectorView.show(viewContainer);
+
+ await Vue.nextTick();
+ optionsElement = element.querySelector('.c-scatter-plot-options');
+ });
+
+ afterEach(() => {
+ plotInspectorView.destroy();
+ });
+
+ it('it renders the options', () => {
+ expect(optionsElement).toBeDefined();
+ });
+ });
+});
diff --git a/src/plugins/charts/scatter/scatterPlotConstants.js b/src/plugins/charts/scatter/scatterPlotConstants.js
new file mode 100644
index 000000000..e458be37c
--- /dev/null
+++ b/src/plugins/charts/scatter/scatterPlotConstants.js
@@ -0,0 +1,4 @@
+export const SCATTER_PLOT_VIEW = 'scatter-plot.view';
+export const SCATTER_PLOT_KEY = 'telemetry.plot.scatter-plot';
+export const SCATTER_PLOT_INSPECTOR_KEY = 'telemetry.plot.scatter-plot.inspector';
+export const TIME_STRIP_KEY = 'time-strip';
diff --git a/src/plugins/clearData/ClearDataAction.js b/src/plugins/clearData/ClearDataAction.js
index ce488e0dd..89e685bfa 100644
--- a/src/plugins/clearData/ClearDataAction.js
+++ b/src/plugins/clearData/ClearDataAction.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/clearData/plugin.js b/src/plugins/clearData/plugin.js
index 65279b080..a92dc55d3 100644
--- a/src/plugins/clearData/plugin.js
+++ b/src/plugins/clearData/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -48,7 +48,7 @@ define([
let indicator = {
element: component.$mount().$el,
- key: 'clear-data-indicator',
+ key: 'global-clear-indicator',
priority: openmct.priority.DEFAULT
};
diff --git a/src/plugins/clearData/pluginSpec.js b/src/plugins/clearData/pluginSpec.js
new file mode 100644
index 000000000..d1bcb195a
--- /dev/null
+++ b/src/plugins/clearData/pluginSpec.js
@@ -0,0 +1,232 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+import ClearDataPlugin from './plugin.js';
+import Vue from 'vue';
+import { createOpenMct, resetApplicationState, createMouseEvent } from 'utils/testing';
+
+describe('The Clear Data Plugin:', () => {
+ let clearDataPlugin;
+
+ describe('The clear data action:', () => {
+ let openmct;
+ let selection;
+ let mockObjectPath;
+ let clearDataAction;
+ let testViewObject;
+ beforeEach((done) => {
+ openmct = createOpenMct();
+
+ clearDataPlugin = new ClearDataPlugin(
+ ['table', 'telemetry.plot.overlay', 'telemetry.plot.stacked'],
+ {indicator: true}
+ );
+ openmct.install(clearDataPlugin);
+
+ clearDataAction = openmct.actions.getAction('clear-data-action');
+ testViewObject = [{
+ identifier: {
+ key: "foo-table",
+ namespace: ''
+ },
+ type: "table"
+ }];
+ openmct.router.path = testViewObject;
+ mockObjectPath = [
+ {
+ name: 'Mock Table',
+ type: 'table',
+ identifier: {
+ key: "foo-table",
+ namespace: ''
+ }
+ }
+ ];
+ selection = [
+ {
+ context: {
+ item: mockObjectPath[0]
+ }
+ }
+ ];
+
+ openmct.selection.select(selection);
+
+ openmct.on('start', done);
+ openmct.startHeadless();
+ });
+
+ afterEach(() => {
+ openmct.router.path = null;
+
+ return resetApplicationState(openmct);
+ });
+ it('is installed', () => {
+ expect(clearDataAction).toBeDefined();
+ });
+
+ it('is applicable on applicable objects', () => {
+ const gatheredActions = openmct.actions.getActionsCollection(mockObjectPath);
+ expect(gatheredActions.applicableActions['clear-data-action']).toBeDefined();
+ });
+
+ it('is not applicable on inapplicable objects', () => {
+ testViewObject = [{
+ identifier: {
+ key: "foo-widget",
+ namespace: ''
+ },
+ type: "widget"
+ }];
+ mockObjectPath = [
+ {
+ name: 'Mock Widget',
+ type: 'widget',
+ identifier: {
+ key: "foo-widget",
+ namespace: ''
+ }
+ }
+ ];
+ selection = [
+ {
+ context: {
+ item: mockObjectPath[0]
+ }
+ }
+ ];
+ openmct.selection.select(selection);
+ const gatheredActions = openmct.actions.getActionsCollection(mockObjectPath);
+ expect(gatheredActions.applicableActions['clear-data-action']).toBeUndefined();
+ });
+
+ it('is not applicable if object not in the selection path and not a layout', () => {
+ selection = [
+ {
+ context: {
+ item: {
+ name: 'Some Random Widget',
+ type: 'not-in-path-widget',
+ identifier: {
+ key: "something-else-widget",
+ namespace: ''
+ }
+ }
+ }
+ }
+ ];
+ openmct.selection.select(selection);
+ const gatheredActions = openmct.actions.getActionsCollection(mockObjectPath);
+ expect(gatheredActions.applicableActions['clear-data-action']).toBeUndefined();
+ });
+
+ it('is applicable if object not in the selection path and is a layout', () => {
+ selection = [
+ {
+ context: {
+ item: {
+ name: 'Some Random Widget',
+ type: 'not-in-path-widget',
+ identifier: {
+ key: "something-else-widget",
+ namespace: ''
+ }
+ }
+ }
+ }
+ ];
+
+ openmct.selection.select(selection);
+
+ testViewObject = [{
+ identifier: {
+ key: "foo-layout",
+ namespace: ''
+ },
+ type: "layout"
+ }];
+ openmct.router.path = testViewObject;
+ const gatheredActions = openmct.actions.getActionsCollection(mockObjectPath);
+ expect(gatheredActions.applicableActions['clear-data-action']).toBeDefined();
+ });
+
+ it('fires an event upon invocation', (done) => {
+ openmct.objectViews.on('clearData', (domainObject) => {
+ expect(domainObject).toEqual(testViewObject[0]);
+ done();
+ });
+ clearDataAction.invoke(testViewObject);
+ });
+ });
+
+ describe('The clear data indicator:', () => {
+ let openmct;
+ let appHolder;
+
+ beforeEach((done) => {
+ openmct = createOpenMct();
+
+ clearDataPlugin = new ClearDataPlugin([
+ 'table',
+ 'telemetry.plot.overlay',
+ 'telemetry.plot.stacked',
+ 'example.imagery'
+ ], {
+ indicator: true
+ });
+ openmct.install(clearDataPlugin);
+ appHolder = document.createElement('div');
+ document.body.appendChild(appHolder);
+ openmct.on('start', done);
+ openmct.start(appHolder);
+ });
+
+ it('installs', () => {
+ const globalClearIndicator = openmct.indicators.indicatorObjects
+ .find(indicator => indicator.key === 'global-clear-indicator').element;
+ expect(globalClearIndicator).toBeDefined();
+ });
+
+ it("renders its major elements", async () => {
+ await Vue.nextTick();
+ const indicatorClass = appHolder.querySelector('.c-indicator');
+ const iconClass = appHolder.querySelector('.icon-clear-data');
+ const indicatorLabel = appHolder.querySelector('.c-indicator__label');
+ const buttonElement = indicatorLabel.querySelector('button');
+ const hasMajorElements = Boolean(indicatorClass && iconClass && buttonElement);
+
+ expect(hasMajorElements).toBe(true);
+ expect(buttonElement.innerText).toEqual('Clear Data');
+ });
+
+ it("clicking the button fires the global clear", (done) => {
+ const indicatorLabel = appHolder.querySelector('.c-indicator__label');
+ const buttonElement = indicatorLabel.querySelector('button');
+ const clickEvent = createMouseEvent('click');
+ openmct.objectViews.on('clearData', () => {
+ // when we click the button, this event should fire
+ done();
+ });
+ buttonElement.dispatchEvent(clickEvent);
+ });
+ });
+});
diff --git a/src/plugins/clearData/test/ClearDataActionSpec.js b/src/plugins/clearData/test/ClearDataActionSpec.js
deleted file mode 100644
index 022d37d94..000000000
--- a/src/plugins/clearData/test/ClearDataActionSpec.js
+++ /dev/null
@@ -1,143 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-import ClearDataActionPlugin from '../plugin.js';
-import ClearDataAction from '../ClearDataAction.js';
-
-describe('When the Clear Data Plugin is installed,', () => {
- const mockObjectViews = jasmine.createSpyObj('objectViews', ['emit']);
- const mockIndicatorProvider = jasmine.createSpyObj('indicators', ['add']);
- const mockActionsProvider = jasmine.createSpyObj('actions', ['register']);
- const goodMockSelectionPath = [[{
- context: {
- item: {
- identifier: {
- key: 'apple',
- namespace: ''
- }
- }
- }
- }]];
-
- const openmct = {
- objectViews: mockObjectViews,
- indicators: mockIndicatorProvider,
- priority: {
- DEFAULT: 0
- },
- actions: mockActionsProvider,
- install: function (plugin) {
- plugin(this);
- },
- selection: {
- get: function () {
- return goodMockSelectionPath;
- }
- },
- objects: {
- areIdsEqual: function (obj1, obj2) {
- return true;
- }
- }
- };
-
- const mockObjectPath = [
- {
- name: 'mockObject1',
- type: 'apple'
- },
- {
- name: 'mockObject2',
- type: 'banana'
- }
- ];
-
- it('Global Clear Indicator is installed', () => {
- openmct.install(ClearDataActionPlugin(openmct, {indicator: true}));
-
- expect(mockIndicatorProvider.add).toHaveBeenCalled();
- });
-
- it('Clear Data context menu action is installed', () => {
- openmct.install(ClearDataActionPlugin(openmct, []));
-
- expect(mockActionsProvider.register).toHaveBeenCalled();
- });
-
- it('clear data action emits a clearData event when invoked', () => {
- const action = new ClearDataAction(openmct);
-
- action.invoke(mockObjectPath);
-
- expect(mockObjectViews.emit).toHaveBeenCalledWith('clearData', mockObjectPath[0]);
- });
-
- it('clears data on applicable objects', () => {
- let action = new ClearDataAction(openmct, ['apple']);
-
- const actionApplies = action.appliesTo(mockObjectPath);
-
- expect(actionApplies).toBe(true);
- });
-
- it('does not clear data on inapplicable objects', () => {
- let action = new ClearDataAction(openmct, ['pineapple']);
-
- const actionApplies = action.appliesTo(mockObjectPath);
-
- expect(actionApplies).toBe(false);
- });
-
- it('does not clear data if not in the selection path and not a layout', () => {
- openmct.objects = {
- areIdsEqual: function (obj1, obj2) {
- return false;
- }
- };
- openmct.router = {
- path: [{type: 'not-a-layout'}]
- };
-
- let action = new ClearDataAction(openmct, ['apple']);
-
- const actionApplies = action.appliesTo(mockObjectPath);
-
- expect(actionApplies).toBe(false);
- });
-
- it('does clear data if not in the selection path and is a layout', () => {
- openmct.objects = {
- areIdsEqual: function (obj1, obj2) {
- return false;
- }
- };
- openmct.router = {
- path: [{type: 'layout'}]
- };
-
- let action = new ClearDataAction(openmct, ['apple']);
-
- const actionApplies = action.appliesTo(mockObjectPath);
-
- expect(actionApplies).toBe(true);
- });
-});
diff --git a/src/plugins/clock/ClockViewProvider.js b/src/plugins/clock/ClockViewProvider.js
index 76f85f383..bcd8b75f8 100644
--- a/src/plugins/clock/ClockViewProvider.js
+++ b/src/plugins/clock/ClockViewProvider.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/clock/components/Clock.vue b/src/plugins/clock/components/Clock.vue
index 8a110dd60..c84af1b4b 100644
--- a/src/plugins/clock/components/Clock.vue
+++ b/src/plugins/clock/components/Clock.vue
@@ -1,5 +1,5 @@
<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
+ Open MCT, Copyright (c) 2014-2022, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
diff --git a/src/plugins/clock/components/ClockIndicator.vue b/src/plugins/clock/components/ClockIndicator.vue
index 1dade1c90..38094fa20 100644
--- a/src/plugins/clock/components/ClockIndicator.vue
+++ b/src/plugins/clock/components/ClockIndicator.vue
@@ -1,5 +1,5 @@
<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
+ Open MCT, Copyright (c) 2014-2022, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
diff --git a/src/plugins/clock/plugin.js b/src/plugins/clock/plugin.js
index 15424bb1c..ab477357b 100644
--- a/src/plugins/clock/plugin.js
+++ b/src/plugins/clock/plugin.js
@@ -1,6 +1,6 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -89,6 +89,7 @@ export default function ClockPlugin(options) {
"key": "timezone",
"name": "Timezone",
"control": "autocomplete",
+ "cssClass": "c-clock__timezone-selection c-menu--no-icon",
"options": momentTimezone.tz.names(),
property: [
'configuration',
diff --git a/src/plugins/clock/pluginSpec.js b/src/plugins/clock/pluginSpec.js
index 8befeea09..5447a669a 100644
--- a/src/plugins/clock/pluginSpec.js
+++ b/src/plugins/clock/pluginSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/condition/Condition.js b/src/plugins/condition/Condition.js
index 4882042e4..9896e9746 100644
--- a/src/plugins/condition/Condition.js
+++ b/src/plugins/condition/Condition.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -21,7 +21,7 @@
*****************************************************************************/
import EventEmitter from 'EventEmitter';
-import uuid from 'uuid';
+import { v4 as uuid } from 'uuid';
import TelemetryCriterion from "./criterion/TelemetryCriterion";
import { evaluateResults } from './utils/evaluator';
import { getLatestTimestamp } from './utils/time';
diff --git a/src/plugins/condition/ConditionManager.js b/src/plugins/condition/ConditionManager.js
index e497f89de..d04ef8d3c 100644
--- a/src/plugins/condition/ConditionManager.js
+++ b/src/plugins/condition/ConditionManager.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -22,7 +22,7 @@
import Condition from "./Condition";
import { getLatestTimestamp } from './utils/time';
-import uuid from "uuid";
+import { v4 as uuid } from 'uuid';
import EventEmitter from 'EventEmitter';
export default class ConditionManager extends EventEmitter {
@@ -300,8 +300,11 @@ export default class ConditionManager extends EventEmitter {
return this.compositionLoad.then(() => {
let latestTimestamp;
let conditionResults = {};
+ let nextLegOptions = {...options};
+ delete nextLegOptions.onPartialResponse;
+
const conditionRequests = this.conditions
- .map(condition => condition.requestLADConditionResult(options));
+ .map(condition => condition.requestLADConditionResult(nextLegOptions));
return Promise.all(conditionRequests)
.then((results) => {
diff --git a/src/plugins/condition/ConditionManagerSpec.js b/src/plugins/condition/ConditionManagerSpec.js
index 66c88b83d..13705917d 100644
--- a/src/plugins/condition/ConditionManagerSpec.js
+++ b/src/plugins/condition/ConditionManagerSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/condition/ConditionSetCompositionPolicy.js b/src/plugins/condition/ConditionSetCompositionPolicy.js
index d2b6b17c8..c328f4aa2 100644
--- a/src/plugins/condition/ConditionSetCompositionPolicy.js
+++ b/src/plugins/condition/ConditionSetCompositionPolicy.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/condition/ConditionSetCompositionPolicySpec.js b/src/plugins/condition/ConditionSetCompositionPolicySpec.js
index 7d1cda938..376927767 100644
--- a/src/plugins/condition/ConditionSetCompositionPolicySpec.js
+++ b/src/plugins/condition/ConditionSetCompositionPolicySpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/condition/ConditionSetMetadataProvider.js b/src/plugins/condition/ConditionSetMetadataProvider.js
index d46aae15a..0fd7ef72e 100644
--- a/src/plugins/condition/ConditionSetMetadataProvider.js
+++ b/src/plugins/condition/ConditionSetMetadataProvider.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/condition/ConditionSetTelemetryProvider.js b/src/plugins/condition/ConditionSetTelemetryProvider.js
index 55ace6e80..24fcb9e68 100644
--- a/src/plugins/condition/ConditionSetTelemetryProvider.js
+++ b/src/plugins/condition/ConditionSetTelemetryProvider.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/condition/ConditionSetViewProvider.js b/src/plugins/condition/ConditionSetViewProvider.js
index 6f58d3e06..535b0d7d5 100644
--- a/src/plugins/condition/ConditionSetViewProvider.js
+++ b/src/plugins/condition/ConditionSetViewProvider.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/condition/ConditionSpec.js b/src/plugins/condition/ConditionSpec.js
index 749c69939..8f9a35144 100644
--- a/src/plugins/condition/ConditionSpec.js
+++ b/src/plugins/condition/ConditionSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/condition/StyleRuleManager.js b/src/plugins/condition/StyleRuleManager.js
index a1ab1b6d4..18063b337 100644
--- a/src/plugins/condition/StyleRuleManager.js
+++ b/src/plugins/condition/StyleRuleManager.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -78,11 +78,13 @@ export default class StyleRuleManager extends EventEmitter {
this.openmct.objects.get(this.conditionSetIdentifier).then((conditionSetDomainObject) => {
this.openmct.telemetry.request(conditionSetDomainObject)
.then(output => {
- if (output && output.length) {
+ if (output && output.length && (this.conditionSetIdentifier && this.openmct.objects.areIdsEqual(conditionSetDomainObject.identifier, this.conditionSetIdentifier))) {
this.handleConditionSetResultUpdated(output[0]);
}
});
- this.stopProvidingTelemetry = this.openmct.telemetry.subscribe(conditionSetDomainObject, this.handleConditionSetResultUpdated.bind(this));
+ if (this.conditionSetIdentifier && this.openmct.objects.areIdsEqual(conditionSetDomainObject.identifier, this.conditionSetIdentifier)) {
+ this.stopProvidingTelemetry = this.openmct.telemetry.subscribe(conditionSetDomainObject, this.handleConditionSetResultUpdated.bind(this));
+ }
});
}
@@ -109,7 +111,7 @@ export default class StyleRuleManager extends EventEmitter {
if (!styleConfiguration || !styleConfiguration.conditionSetIdentifier) {
this.initialize(styleConfiguration || {});
this.applyStaticStyle();
- this.destroy();
+ this.destroy(true);
} else {
let isNewConditionSet = !this.conditionSetIdentifier
|| !this.openmct.objects.areIdsEqual(this.conditionSetIdentifier, styleConfiguration.conditionSetIdentifier);
@@ -180,15 +182,17 @@ export default class StyleRuleManager extends EventEmitter {
this.updateDomainObjectStyle();
}
- destroy() {
+ destroy(skipEventListeners) {
if (this.stopProvidingTelemetry) {
this.stopProvidingTelemetry();
delete this.stopProvidingTelemetry;
}
- this.openmct.time.off("bounds", this.refreshData);
- this.openmct.editor.off('isEditing', this.toggleSubscription);
+ if (!skipEventListeners) {
+ this.openmct.time.off("bounds", this.refreshData);
+ this.openmct.editor.off('isEditing', this.toggleSubscription);
+ }
this.conditionSetIdentifier = undefined;
}
diff --git a/src/plugins/condition/components/Condition.vue b/src/plugins/condition/components/Condition.vue
index 2d2dda3d1..7201b6b31 100644
--- a/src/plugins/condition/components/Condition.vue
+++ b/src/plugins/condition/components/Condition.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
-* Open MCT, Copyright (c) 2014-2021, United States Government
+* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -21,31 +21,35 @@
*****************************************************************************/
<template>
-<div class="c-condition-h"
- :class="{ 'is-drag-target': draggingOver }"
- @dragover.prevent
- @drop.prevent="dropCondition($event, conditionIndex)"
- @dragenter="dragEnter($event, conditionIndex)"
- @dragleave="dragLeave($event, conditionIndex)"
+<div
+ class="c-condition-h"
+ :class="{ 'is-drag-target': draggingOver }"
+ @dragover.prevent
+ @drop.prevent="dropCondition($event, conditionIndex)"
+ @dragenter="dragEnter($event, conditionIndex)"
+ @dragleave="dragLeave($event, conditionIndex)"
>
<div class="c-condition-h__drop-target"></div>
- <div v-if="isEditing"
- :class="{'is-current': condition.id === currentConditionId}"
- class="c-condition c-condition--edit"
+ <div
+ v-if="isEditing"
+ :class="{'is-current': condition.id === currentConditionId}"
+ class="c-condition c-condition--edit"
>
<!-- Edit view -->
<div class="c-condition__header">
- <span class="c-condition__drag-grippy c-grippy c-grippy--vertical-drag"
- title="Drag to reorder conditions"
- :class="[{ 'is-enabled': !condition.isDefault }, { 'hide-nice': condition.isDefault }]"
- :draggable="!condition.isDefault"
- @dragstart="dragStart"
- @dragend="dragEnd"
+ <span
+ class="c-condition__drag-grippy c-grippy c-grippy--vertical-drag"
+ title="Drag to reorder conditions"
+ :class="[{ 'is-enabled': !condition.isDefault }, { 'hide-nice': condition.isDefault }]"
+ :draggable="!condition.isDefault"
+ @dragstart="dragStart"
+ @dragend="dragEnd"
></span>
- <span class="c-condition__disclosure c-disclosure-triangle c-tree__item__view-control is-enabled"
- :class="{ 'c-disclosure-triangle--expanded': expanded }"
- @click="expanded = !expanded"
+ <span
+ class="c-condition__disclosure c-disclosure-triangle c-tree__item__view-control is-enabled"
+ :class="{ 'c-disclosure-triangle--expanded': expanded }"
+ @click="expanded = !expanded"
></span>
<span class="c-condition__name">{{ condition.configuration.name }}</span>
@@ -54,107 +58,123 @@
Define criteria
</template>
<span v-else>
- <condition-description :show-label="false"
- :condition="condition"
+ <condition-description
+ :show-label="false"
+ :condition="condition"
/>
</span>
</span>
<div class="c-condition__buttons">
- <button v-if="!condition.isDefault"
- class="c-click-icon c-condition__duplicate-button icon-duplicate"
- title="Duplicate this condition"
- @click="cloneCondition"
+ <button
+ v-if="!condition.isDefault"
+ class="c-click-icon c-condition__duplicate-button icon-duplicate"
+ title="Duplicate this condition"
+ @click="cloneCondition"
></button>
- <button v-if="!condition.isDefault"
- class="c-click-icon c-condition__delete-button icon-trash"
- title="Delete this condition"
- @click="removeCondition"
+ <button
+ v-if="!condition.isDefault"
+ class="c-click-icon c-condition__delete-button icon-trash"
+ title="Delete this condition"
+ @click="removeCondition"
></button>
</div>
</div>
- <div v-if="expanded"
- class="c-condition__definition c-cdef"
+ <div
+ v-if="expanded"
+ class="c-condition__definition c-cdef"
>
<span class="c-cdef__separator c-row-separator"></span>
<span class="c-cdef__label">Condition Name</span>
<span class="c-cdef__controls">
- <input v-model="condition.configuration.name"
- class="t-condition-input__name"
- type="text"
- @change="persist"
+ <input
+ v-model="condition.configuration.name"
+ class="t-condition-input__name"
+ type="text"
+ @change="persist"
>
</span>
<span class="c-cdef__label">Output</span>
<span class="c-cdef__controls">
<span class="c-cdef__control">
- <select v-model="selectedOutputSelection"
- @change="setOutputValue"
+ <select
+ v-model="selectedOutputSelection"
+ @change="setOutputValue"
>
- <option v-for="option in outputOptions"
- :key="option"
- :value="option"
+ <option
+ v-for="option in outputOptions"
+ :key="option"
+ :value="option"
>
{{ initCap(option) }}
</option>
</select>
</span>
<span class="c-cdef__control">
- <input v-if="selectedOutputSelection === outputOptions[2]"
- v-model="condition.configuration.output"
- class="t-condition-name-input"
- type="text"
- @change="persist"
+ <input
+ v-if="selectedOutputSelection === outputOptions[2]"
+ v-model="condition.configuration.output"
+ class="t-condition-name-input"
+ type="text"
+ @change="persist"
>
</span>
</span>
- <div v-if="!condition.isDefault"
- class="c-cdef__match-and-criteria"
+ <div
+ v-if="!condition.isDefault"
+ class="c-cdef__match-and-criteria"
>
<span class="c-cdef__separator c-row-separator"></span>
<span class="c-cdef__label">Match</span>
<span class="c-cdef__controls">
- <select v-model="condition.configuration.trigger"
- @change="persist"
+ <select
+ v-model="condition.configuration.trigger"
+ @change="persist"
>
- <option v-for="option in triggers"
- :key="option.value"
- :value="option.value"
+ <option
+ v-for="option in triggers"
+ :key="option.value"
+ :value="option.value"
> {{ option.label }}</option>
</select>
</span>
<template v-if="telemetry.length || condition.configuration.criteria.length">
- <div v-for="(criterion, index) in condition.configuration.criteria"
- :key="criterion.id"
- class="c-cdef__criteria"
+ <div
+ v-for="(criterion, index) in condition.configuration.criteria"
+ :key="criterion.id"
+ class="c-cdef__criteria"
>
- <Criterion :telemetry="telemetry"
- :criterion="criterion"
- :index="index"
- :trigger="condition.configuration.trigger"
- :is-default="condition.configuration.criteria.length === 1"
- @persist="persist"
+ <Criterion
+ :telemetry="telemetry"
+ :criterion="criterion"
+ :index="index"
+ :trigger="condition.configuration.trigger"
+ :is-default="condition.configuration.criteria.length === 1"
+ @persist="persist"
/>
<div class="c-cdef__criteria__buttons">
- <button class="c-click-icon c-cdef__criteria-duplicate-button icon-duplicate"
- title="Duplicate this criteria"
- @click="cloneCriterion(index)"
+ <button
+ class="c-click-icon c-cdef__criteria-duplicate-button icon-duplicate"
+ title="Duplicate this criteria"
+ @click="cloneCriterion(index)"
></button>
- <button v-if="!(condition.configuration.criteria.length === 1)"
- class="c-click-icon c-cdef__criteria-duplicate-button icon-trash"
- title="Delete this criteria"
- @click="removeCriterion(index)"
+ <button
+ v-if="!(condition.configuration.criteria.length === 1)"
+ class="c-click-icon c-cdef__criteria-duplicate-button icon-trash"
+ title="Delete this criteria"
+ @click="removeCriterion(index)"
></button>
</div>
</div>
</template>
<div class="c-cdef__separator c-row-separator"></div>
- <div class="c-cdef__controls"
- :disabled="!telemetry.length"
+ <div
+ class="c-cdef__controls"
+ :disabled="!telemetry.length"
>
<button
class="c-cdef__add-criteria-button c-button c-button--labeled icon-plus"
@@ -166,9 +186,10 @@
</div>
</div>
</div>
- <div v-else
- class="c-condition c-condition--browse"
- :class="{'is-current': condition.id === currentConditionId}"
+ <div
+ v-else
+ class="c-condition c-condition--browse"
+ :class="{'is-current': condition.id === currentConditionId}"
>
<!-- Browse view -->
<div class="c-condition__header">
@@ -180,8 +201,9 @@
</span>
</div>
<div class="c-condition__summary">
- <condition-description :show-label="false"
- :condition="condition"
+ <condition-description
+ :show-label="false"
+ :condition="condition"
/>
</div>
</div>
@@ -192,7 +214,7 @@
import Criterion from './Criterion.vue';
import ConditionDescription from "./ConditionDescription.vue";
import { TRIGGER, TRIGGER_LABEL } from "@/plugins/condition/utils/constants";
-import uuid from 'uuid';
+import { v4 as uuid } from 'uuid';
export default {
components: {
diff --git a/src/plugins/condition/components/ConditionCollection.vue b/src/plugins/condition/components/ConditionCollection.vue
index 63d7b9bef..9825cd094 100644
--- a/src/plugins/condition/components/ConditionCollection.vue
+++ b/src/plugins/condition/components/ConditionCollection.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -21,8 +21,9 @@
*****************************************************************************/
<template>
-<section id="conditionCollection"
- :class="{ 'is-expanded': expanded }"
+<section
+ id="conditionCollection"
+ :class="{ 'is-expanded': expanded }"
>
<div class="c-cs__header c-section__header">
<span
@@ -32,12 +33,14 @@
></span>
<div class="c-cs__header-label c-section__label">Conditions</div>
</div>
- <div v-if="expanded"
- class="c-cs__content"
+ <div
+ v-if="expanded"
+ class="c-cs__content"
>
- <div v-show="isEditing"
- class="hint"
- :class="{ 's-status-icon-warning-lo': !telemetryObjs.length }"
+ <div
+ v-show="isEditing"
+ class="hint"
+ :class="{ 's-status-icon-warning-lo': !telemetryObjs.length }"
>
<template v-if="!telemetryObjs.length">Drag telemetry into this Condition Set to configure Conditions and add criteria.</template>
<template v-else>The first condition to match is the one that is applied. Drag conditions to reorder.</template>
@@ -52,24 +55,26 @@
<span class="c-cs-button__label">Add Condition</span>
</button>
- <div class="c-cs__conditions-h"
- :class="{ 'is-active-dragging': isDragging }"
+ <div
+ class="c-cs__conditions-h"
+ :class="{ 'is-active-dragging': isDragging }"
>
- <Condition v-for="(condition, index) in conditionCollection"
- :key="condition.id"
- :condition="condition"
- :current-condition-id="currentConditionId"
- :condition-index="index"
- :telemetry="telemetryObjs"
- :is-editing="isEditing"
- :move-index="moveIndex"
- :is-dragging="isDragging"
- @updateCondition="updateCondition"
- @removeCondition="removeCondition"
- @cloneCondition="cloneCondition"
- @setMoveIndex="setMoveIndex"
- @dragComplete="dragComplete"
- @dropCondition="dropCondition"
+ <Condition
+ v-for="(condition, index) in conditionCollection"
+ :key="condition.id"
+ :condition="condition"
+ :current-condition-id="currentConditionId"
+ :condition-index="index"
+ :telemetry="telemetryObjs"
+ :is-editing="isEditing"
+ :move-index="moveIndex"
+ :is-dragging="isDragging"
+ @updateCondition="updateCondition"
+ @removeCondition="removeCondition"
+ @cloneCondition="cloneCondition"
+ @setMoveIndex="setMoveIndex"
+ @dragComplete="dragComplete"
+ @dropCondition="dropCondition"
/>
</div>
</div>
diff --git a/src/plugins/condition/components/ConditionDescription.vue b/src/plugins/condition/components/ConditionDescription.vue
index 55aafb2c0..e044f6a4d 100644
--- a/src/plugins/condition/components/ConditionDescription.vue
+++ b/src/plugins/condition/components/ConditionDescription.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
-* Open MCT, Copyright (c) 2014-2021, United States Government
+* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -22,18 +22,21 @@
<template>
<div class="c-style__condition-desc">
- <span v-if="showLabel && condition"
- class="c-style__condition-desc__name c-condition__name"
+ <span
+ v-if="showLabel && condition"
+ class="c-style__condition-desc__name c-condition__name"
>
{{ condition.configuration.name }}
</span>
- <span v-if="!condition.isDefault"
- class="c-style__condition-desc__text"
+ <span
+ v-if="!condition.isDefault"
+ class="c-style__condition-desc__text"
>
{{ description }}
</span>
- <span v-else
- class="c-style__condition-desc__text"
+ <span
+ v-else
+ class="c-style__condition-desc__text"
>
Match if no other condition is matched
</span>
diff --git a/src/plugins/condition/components/ConditionError.vue b/src/plugins/condition/components/ConditionError.vue
index b54ee4a94..280ef46c2 100644
--- a/src/plugins/condition/components/ConditionError.vue
+++ b/src/plugins/condition/components/ConditionError.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
-* Open MCT, Copyright (c) 2014-2021, United States Government
+* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -21,12 +21,14 @@
*****************************************************************************/
<template>
-<div v-if="conditionErrors.length"
- class="c-condition__errors"
+<div
+ v-if="conditionErrors.length"
+ class="c-condition__errors"
>
- <div v-for="(error, index) in conditionErrors"
- :key="index"
- class="u-alert u-alert--block u-alert--with-icon"
+ <div
+ v-for="(error, index) in conditionErrors"
+ :key="index"
+ class="u-alert u-alert--block u-alert--with-icon"
>{{ error.message.errorText }} {{ error.additionalInfo }}
</div>
</div>
diff --git a/src/plugins/condition/components/ConditionSet.vue b/src/plugins/condition/components/ConditionSet.vue
index f819a1823..eb4ce2c98 100644
--- a/src/plugins/condition/components/ConditionSet.vue
+++ b/src/plugins/condition/components/ConditionSet.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -36,18 +36,20 @@
</div>
</section>
<div class="c-cs__test-data-and-conditions-w">
- <TestData class="c-cs__test-data"
- :is-editing="isEditing"
- :test-data="testData"
- :telemetry="telemetryObjs"
- @updateTestData="updateTestData"
+ <TestData
+ class="c-cs__test-data"
+ :is-editing="isEditing"
+ :test-data="testData"
+ :telemetry="telemetryObjs"
+ @updateTestData="updateTestData"
/>
- <ConditionCollection class="c-cs__conditions"
- :is-editing="isEditing"
- :test-data="testData"
- @conditionSetResultUpdated="updateCurrentOutput"
- @updateDefaultOutput="updateDefaultOutput"
- @telemetryUpdated="updateTelemetry"
+ <ConditionCollection
+ class="c-cs__conditions"
+ :is-editing="isEditing"
+ :test-data="testData"
+ @conditionSetResultUpdated="updateCurrentOutput"
+ @updateDefaultOutput="updateDefaultOutput"
+ @telemetryUpdated="updateTelemetry"
/>
</div>
</div>
diff --git a/src/plugins/condition/components/Criterion.vue b/src/plugins/condition/components/Criterion.vue
index f89dbdef5..be1bde745 100644
--- a/src/plugins/condition/components/Criterion.vue
+++ b/src/plugins/condition/components/Criterion.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
-* Open MCT, Copyright (c) 2014-2021, United States Government
+* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -26,76 +26,89 @@
<span class="c-cdef__label">{{ setRowLabel }}</span>
<span class="c-cdef__controls">
<span class="c-cdef__control">
- <select ref="telemetrySelect"
- v-model="criterion.telemetry"
- @change="updateMetadataOptions"
+ <select
+ ref="telemetrySelect"
+ v-model="criterion.telemetry"
+ @change="updateMetadataOptions"
>
<option value="">- Select Telemetry -</option>
<option value="all">all telemetry</option>
<option value="any">any telemetry</option>
- <option v-for="telemetryOption in telemetry"
- :key="telemetryOption.identifier.key"
- :value="telemetryOption.identifier"
+ <option
+ v-for="telemetryOption in telemetry"
+ :key="telemetryOption.identifier.key"
+ :value="telemetryOption.identifier"
>
{{ telemetryOption.name }}
</option>
</select>
</span>
- <span v-if="criterion.telemetry"
- class="c-cdef__control"
+ <span
+ v-if="criterion.telemetry"
+ class="c-cdef__control"
>
- <select ref="metadataSelect"
- v-model="criterion.metadata"
- @change="updateOperations"
+ <select
+ ref="metadataSelect"
+ v-model="criterion.metadata"
+ @change="updateOperations"
>
<option value="">- Select Field -</option>
- <option v-for="option in telemetryMetadataOptions"
- :key="option.key"
- :value="option.key"
+ <option
+ v-for="option in telemetryMetadataOptions"
+ :key="option.key"
+ :value="option.key"
>
{{ option.name }}
</option>
<option value="dataReceived">any data received</option>
</select>
</span>
- <span v-if="criterion.telemetry && criterion.metadata"
- class="c-cdef__control"
+ <span
+ v-if="criterion.telemetry && criterion.metadata"
+ class="c-cdef__control"
>
- <select v-model="criterion.operation"
- @change="updateInputVisibilityAndValues"
+ <select
+ v-model="criterion.operation"
+ @change="updateInputVisibilityAndValues"
>
<option value="">- Select Comparison -</option>
- <option v-for="option in filteredOps"
- :key="option.name"
- :value="option.name"
+ <option
+ v-for="option in filteredOps"
+ :key="option.name"
+ :value="option.name"
>
{{ option.text }}
</option>
</select>
<template v-if="!enumerations.length">
- <span v-for="(item, inputIndex) in inputCount"
- :key="inputIndex"
- class="c-cdef__control__inputs"
+ <span
+ v-for="(item, inputIndex) in inputCount"
+ :key="inputIndex"
+ class="c-cdef__control__inputs"
>
- <input v-model="criterion.input[inputIndex]"
- class="c-cdef__control__input"
- :type="setInputType"
- @change="persist"
+ <input
+ v-model="criterion.input[inputIndex]"
+ class="c-cdef__control__input"
+ :type="setInputType"
+ @change="persist"
>
<span v-if="inputIndex < inputCount-1">and</span>
</span>
<span v-if="criterion.metadata === 'dataReceived'">seconds</span>
</template>
<span v-else>
- <span v-if="inputCount && criterion.operation"
- class="c-cdef__control"
+ <span
+ v-if="inputCount && criterion.operation"
+ class="c-cdef__control"
>
- <select v-model="criterion.input[0]"
- @change="persist"
+ <select
+ v-model="criterion.input[0]"
+ @change="persist"
>
- <option v-for="option in enumerations"
- :key="option.string"
- :value="option.value.toString()"
+ <option
+ v-for="option in enumerations"
+ :key="option.string"
+ :value="option.value.toString()"
>
{{ option.string }}
</option>
diff --git a/src/plugins/condition/components/TestData.vue b/src/plugins/condition/components/TestData.vue
index 060736d67..2668ce448 100644
--- a/src/plugins/condition/components/TestData.vue
+++ b/src/plugins/condition/components/TestData.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -21,9 +21,10 @@
*****************************************************************************/
<template>
-<section v-show="isEditing"
- id="test-data"
- :class="{ 'is-expanded': expanded }"
+<section
+ v-show="isEditing"
+ id="test-data"
+ :class="{ 'is-expanded': expanded }"
>
<div class="c-cs__header c-section__header">
<span
@@ -33,11 +34,13 @@
></span>
<div class="c-cs__header-label c-section__label">Test Data</div>
</div>
- <div v-if="expanded"
- class="c-cs__content"
+ <div
+ v-if="expanded"
+ class="c-cs__content"
>
- <div class="c-cs__test-data__controls c-cdef__controls"
- :disabled="!telemetry.length"
+ <div
+ class="c-cs__test-data__controls c-cdef__controls"
+ :disabled="!telemetry.length"
>
<label class="c-toggle-switch">
<input
@@ -50,59 +53,69 @@
</label>
</div>
<div class="c-cs-tests">
- <span v-for="(testInput, tIndex) in testInputs"
- :key="tIndex"
- class="c-test-datum c-cs-test"
+ <span
+ v-for="(testInput, tIndex) in testInputs"
+ :key="tIndex"
+ class="c-test-datum c-cs-test"
>
<span class="c-cs-test__label">Set</span>
<span class="c-cs-test__controls">
<span class="c-cdef__control">
- <select v-model="testInput.telemetry"
- @change="updateMetadata(testInput)"
+ <select
+ v-model="testInput.telemetry"
+ @change="updateMetadata(testInput)"
>
<option value="">- Select Telemetry -</option>
- <option v-for="(telemetryOption, index) in telemetry"
- :key="index"
- :value="telemetryOption.identifier"
+ <option
+ v-for="(telemetryOption, index) in telemetry"
+ :key="index"
+ :value="telemetryOption.identifier"
>
{{ telemetryOption.name }}
</option>
</select>
</span>
- <span v-if="testInput.telemetry"
- class="c-cdef__control"
+ <span
+ v-if="testInput.telemetry"
+ class="c-cdef__control"
>
- <select v-model="testInput.metadata"
- @change="updateTestData"
+ <select
+ v-model="testInput.metadata"
+ @change="updateTestData"
>
<option value="">- Select Field -</option>
- <option v-for="(option, index) in telemetryMetadataOptions[getId(testInput.telemetry)]"
- :key="index"
- :value="option.key"
+ <option
+ v-for="(option, index) in telemetryMetadataOptions[getId(testInput.telemetry)]"
+ :key="index"
+ :value="option.key"
>
{{ option.name }}
</option>
</select>
</span>
- <span v-if="testInput.metadata"
- class="c-cdef__control__inputs"
+ <span
+ v-if="testInput.metadata"
+ class="c-cdef__control__inputs"
>
- <input v-model="testInput.value"
- placeholder="Enter test input"
- type="text"
- class="c-cdef__control__input"
- @change="updateTestData"
+ <input
+ v-model="testInput.value"
+ placeholder="Enter test input"
+ type="text"
+ class="c-cdef__control__input"
+ @change="updateTestData"
>
</span>
</span>
<div class="c-cs-test__buttons">
- <button class="c-click-icon c-test-data__duplicate-button icon-duplicate"
- title="Duplicate this test datum"
- @click="addTestInput(testInput)"
+ <button
+ class="c-click-icon c-test-data__duplicate-button icon-duplicate"
+ title="Duplicate this test datum"
+ @click="addTestInput(testInput)"
></button>
- <button class="c-click-icon c-test-data__delete-button icon-trash"
- title="Delete this test datum"
- @click="removeTestInput(tIndex)"
+ <button
+ class="c-click-icon c-test-data__delete-button icon-trash"
+ title="Delete this test datum"
+ @click="removeTestInput(tIndex)"
></button>
</div>
</span>
diff --git a/src/plugins/condition/components/conditionals.scss b/src/plugins/condition/components/conditionals.scss
index f51ab904f..965577b89 100644
--- a/src/plugins/condition/components/conditionals.scss
+++ b/src/plugins/condition/components/conditionals.scss
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/condition/components/inspector/ConditionalStylesView.vue b/src/plugins/condition/components/inspector/ConditionalStylesView.vue
index 062ac9f35..e69de29bb 100644
--- a/src/plugins/condition/components/inspector/ConditionalStylesView.vue
+++ b/src/plugins/condition/components/inspector/ConditionalStylesView.vue
@@ -1,475 +0,0 @@
-/*****************************************************************************
-* Open MCT, Copyright (c) 2014-2021, United States Government
-* as represented by the Administrator of the National Aeronautics and Space
-* Administration. All rights reserved.
-*
-* Open MCT is licensed under the Apache License, Version 2.0 (the
-* "License"); you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-* http://www.apache.org/licenses/LICENSE-2.0.
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-* License for the specific language governing permissions and limitations
-* under the License.
-*
-* Open MCT includes source code licensed under additional open source
-* licenses. See the Open Source Licenses file (LICENSES.md) included with
-* this source code distribution or the Licensing information page available
-* at runtime from the About dialog for additional information.
-*****************************************************************************/
-
-<template>
-<div class="c-inspector__styles c-inspect-styles">
- <template v-if="!conditionSetDomainObject">
- <div class="c-inspect-styles__header">
- Object Style
- </div>
- <div class="c-inspect-styles__content">
- <div v-if="staticStyle"
- class="c-inspect-styles__style"
- >
- <StyleEditor class="c-inspect-styles__editor"
- :style-item="staticStyle"
- :is-editing="isEditing"
- @persist="updateStaticStyle"
- />
- </div>
- <button
- id="addConditionSet"
- class="c-button c-button--major c-toggle-styling-button labeled"
- @click="addConditionSet"
- >
- <span class="c-cs-button__label">Use Conditional Styling...</span>
- </button>
- </div>
- </template>
- <template v-else>
- <div class="c-inspect-styles__header">
- Conditional Object Styles
- </div>
- <div class="c-inspect-styles__content c-inspect-styles__condition-set">
- <a v-if="conditionSetDomainObject"
- class="c-object-label icon-conditional"
- @click="navigateOrPreview"
- >
- <span class="c-object-label__name">{{ conditionSetDomainObject.name }}</span>
- </a>
- <template v-if="isEditing">
- <button
- id="changeConditionSet"
- class="c-button labeled"
- @click="addConditionSet"
- >
- <span class="c-button__label">Change...</span>
- </button>
-
- <button class="c-click-icon icon-x"
- title="Remove conditional styles"
- @click="removeConditionSet"
- ></button>
- </template>
- </div>
-
- <div v-if="conditionsLoaded"
- class="c-inspect-styles__conditions"
- >
- <div v-for="(conditionStyle, index) in conditionalStyles"
- :key="index"
- class="c-inspect-styles__condition"
- :class="{'is-current': conditionStyle.conditionId === selectedConditionId}"
- @click="applySelectedConditionStyle(conditionStyle.conditionId)"
- >
- <condition-error :show-label="true"
- :condition="getCondition(conditionStyle.conditionId)"
- />
- <condition-description :show-label="true"
- :condition="getCondition(conditionStyle.conditionId)"
- />
- <StyleEditor class="c-inspect-styles__editor"
- :style-item="conditionStyle"
- :is-editing="isEditing"
- @persist="updateConditionalStyle"
- />
- </div>
- </div>
- </template>
-</div>
-</template>
-
-<script>
-
-import StyleEditor from "./StyleEditor.vue";
-import SelectorDialogTree from '@/ui/components/SelectorDialogTree.vue';
-import ConditionDescription from "@/plugins/condition/components/ConditionDescription.vue";
-import ConditionError from "@/plugins/condition/components/ConditionError.vue";
-import Vue from 'vue';
-import PreviewAction from "@/ui/preview/PreviewAction.js";
-import { getApplicableStylesForItem } from "@/plugins/condition/utils/styleUtils";
-import isEmpty from 'lodash/isEmpty';
-
-export default {
- name: 'ConditionalStylesView',
- components: {
- ConditionDescription,
- ConditionError,
- StyleEditor
- },
- inject: [
- 'openmct',
- 'selection'
- ],
- data() {
- return {
- conditionalStyles: [],
- staticStyle: undefined,
- conditionSetDomainObject: undefined,
- isEditing: this.openmct.editor.isEditing(),
- conditions: undefined,
- conditionsLoaded: false,
- navigateToPath: '',
- selectedConditionId: ''
- };
- },
- destroyed() {
- this.removeListeners();
- },
- mounted() {
- this.itemId = '';
- this.getDomainObjectFromSelection();
- this.previewAction = new PreviewAction(this.openmct);
- if (this.domainObject.configuration && this.domainObject.configuration.objectStyles) {
- let objectStyles = this.itemId ? this.domainObject.configuration.objectStyles[this.itemId] : this.domainObject.configuration.objectStyles;
- this.initializeStaticStyle(objectStyles);
- if (objectStyles && objectStyles.conditionSetIdentifier) {
- this.openmct.objects.get(objectStyles.conditionSetIdentifier).then(this.initialize);
- this.conditionalStyles = objectStyles.styles;
- }
- } else {
- this.initializeStaticStyle();
- }
-
- this.openmct.editor.on('isEditing', this.setEditState);
- },
- methods: {
- isItemType(type, item) {
- return item && (item.type === type);
- },
- getDomainObjectFromSelection() {
- let layoutItem;
- let domainObject;
-
- if (this.selection[0].length > 1) {
- //If there are more than 1 items in the this.selection[0] list, the first one could either be a sub domain object OR a layout drawing control.
- //The second item in the this.selection[0] list is the container object (usually a layout)
- layoutItem = this.selection[0][0].context.layoutItem;
- const item = this.selection[0][0].context.item;
- this.canHide = true;
- if (item
- && (!layoutItem || (this.isItemType('subobject-view', layoutItem)))) {
- domainObject = item;
- } else {
- domainObject = this.selection[0][1].context.item;
- if (layoutItem) {
- this.itemId = layoutItem.id;
- }
- }
- } else {
- domainObject = this.selection[0][0].context.item;
- }
-
- this.domainObject = domainObject;
- this.initialStyles = getApplicableStylesForItem(domainObject, layoutItem);
- this.$nextTick(() => {
- this.removeListeners();
- if (this.domainObject) {
- this.stopObserving = this.openmct.objects.observe(this.domainObject, '*', newDomainObject => this.domainObject = newDomainObject);
- this.stopObservingItems = this.openmct.objects.observe(this.domainObject, 'configuration.items', this.updateDomainObjectItemStyles);
- }
- });
- },
- removeListeners() {
- if (this.stopObserving) {
- this.stopObserving();
- }
-
- if (this.stopObservingItems) {
- this.stopObservingItems();
- }
-
- if (this.stopProvidingTelemetry) {
- this.stopProvidingTelemetry();
- delete this.stopProvidingTelemetry;
- }
- },
- initialize(conditionSetDomainObject) {
- //If there are new conditions in the conditionSet we need to set those styles to default
- this.conditionSetDomainObject = conditionSetDomainObject;
- this.enableConditionSetNav();
- this.initializeConditionalStyles();
- },
- setEditState(isEditing) {
- this.isEditing = isEditing;
- if (this.isEditing) {
- if (this.stopProvidingTelemetry) {
- this.stopProvidingTelemetry();
- delete this.stopProvidingTelemetry;
- }
- } else {
- this.subscribeToConditionSet();
- }
- },
- addConditionSet() {
- let conditionSetDomainObject;
- let self = this;
-
- function handleItemSelection({ item }) {
- if (item) {
- conditionSetDomainObject = item;
- }
- }
-
- function dismissDialog(overlay, initialize) {
- overlay.dismiss();
- if (initialize && conditionSetDomainObject) {
- self.conditionSetDomainObject = conditionSetDomainObject;
- self.conditionalStyles = [];
- self.initializeConditionalStyles();
- }
- }
-
- let vm = new Vue({
- components: { SelectorDialogTree },
- provide: {
- openmct: this.openmct
- },
- data() {
- return {
- handleItemSelection,
- title: 'Select Condition Set'
- };
- },
- template: '<selector-dialog-tree :title="title" @treeItemSelected="handleItemSelection"></selector-dialog-tree>'
- }).$mount();
-
- let overlay = this.openmct.overlays.overlay({
- element: vm.$el,
- size: 'small',
- buttons: [
- {
- label: 'OK',
- emphasis: 'true',
- callback: () => dismissDialog(overlay, true)
- },
- {
- label: 'Cancel',
- callback: () => dismissDialog(overlay, false)
- }
- ],
- onDestroy: () => vm.$destroy()
- });
- },
- enableConditionSetNav() {
- this.openmct.objects.getOriginalPath(this.conditionSetDomainObject.identifier).then(
- (objectPath) => {
- this.objectPath = objectPath;
- this.navigateToPath = '#/browse/' + this.openmct.objects.getRelativePath(this.objectPath);
- }
- );
- },
- navigateOrPreview(event) {
- // If editing, display condition set in Preview overlay; otherwise nav to it while browsing
- if (this.openmct.editor.isEditing()) {
- event.preventDefault();
- this.previewAction.invoke(this.objectPath);
- } else {
- this.openmct.router.navigate(this.navigateToPath);
- }
- },
- removeConditionSet() {
- this.conditionSetDomainObject = undefined;
- this.conditionalStyles = [];
- let domainObjectStyles = (this.domainObject.configuration && this.domainObject.configuration.objectStyles) || {};
- if (this.itemId) {
- domainObjectStyles[this.itemId].conditionSetIdentifier = undefined;
- domainObjectStyles[this.itemId].selectedConditionId = undefined;
- domainObjectStyles[this.itemId].defaultConditionId = undefined;
- delete domainObjectStyles[this.itemId].conditionSetIdentifier;
- domainObjectStyles[this.itemId].styles = undefined;
- delete domainObjectStyles[this.itemId].styles;
- if (isEmpty(domainObjectStyles[this.itemId])) {
- delete domainObjectStyles[this.itemId];
- }
- } else {
- domainObjectStyles.conditionSetIdentifier = undefined;
- domainObjectStyles.selectedConditionId = undefined;
- domainObjectStyles.defaultConditionId = undefined;
- delete domainObjectStyles.conditionSetIdentifier;
- domainObjectStyles.styles = undefined;
- delete domainObjectStyles.styles;
- }
-
- if (isEmpty(domainObjectStyles)) {
- domainObjectStyles = undefined;
- }
-
- this.persist(domainObjectStyles);
- if (this.stopProvidingTelemetry) {
- this.stopProvidingTelemetry();
- delete this.stopProvidingTelemetry;
- }
- },
- updateDomainObjectItemStyles(newItems) {
- //check that all items that have been styles still exist. Otherwise delete those styles
- let domainObjectStyles = (this.domainObject.configuration && this.domainObject.configuration.objectStyles) || {};
- let itemsToRemove = [];
- let keys = Object.keys(domainObjectStyles);
- //TODO: Need an easier way to find which properties are itemIds
- keys.forEach((key) => {
- const keyIsItemId = (key !== 'styles')
- && (key !== 'staticStyle')
- && (key !== 'defaultConditionId')
- && (key !== 'selectedConditionId')
- && (key !== 'conditionSetIdentifier');
- if (keyIsItemId) {
- if (!(newItems.find(item => item.id === key))) {
- itemsToRemove.push(key);
- }
- }
- });
- if (itemsToRemove.length) {
- this.removeItemStyles(itemsToRemove, domainObjectStyles);
- }
- },
- removeItemStyles(itemIds, domainObjectStyles) {
- itemIds.forEach(itemId => {
- if (domainObjectStyles[itemId]) {
- domainObjectStyles[itemId] = undefined;
- delete domainObjectStyles[this.itemId];
- }
- });
- if (isEmpty(domainObjectStyles)) {
- domainObjectStyles = undefined;
- }
-
- this.persist(domainObjectStyles);
- },
- initializeConditionalStyles() {
- if (!this.conditions) {
- this.conditions = {};
- }
-
- let conditionalStyles = [];
- this.conditionSetDomainObject.configuration.conditionCollection.forEach((conditionConfiguration, index) => {
- if (conditionConfiguration.isDefault) {
- this.selectedConditionId = conditionConfiguration.id;
- }
-
- this.conditions[conditionConfiguration.id] = conditionConfiguration;
- let foundStyle = this.findStyleByConditionId(conditionConfiguration.id);
- if (foundStyle) {
- foundStyle.style = Object.assign((this.canHide ? { isStyleInvisible: '' } : {}), this.initialStyles, foundStyle.style);
- conditionalStyles.push(foundStyle);
- } else {
- conditionalStyles.splice(index, 0, {
- conditionId: conditionConfiguration.id,
- style: Object.assign((this.canHide ? { isStyleInvisible: '' } : {}), this.initialStyles)
- });
- }
- });
- //we're doing this so that we remove styles for any conditions that have been removed from the condition set
- this.conditionalStyles = conditionalStyles;
- this.conditionsLoaded = true;
- this.persist(this.getDomainObjectConditionalStyle(this.selectedConditionId));
- if (!this.isEditing) {
- this.subscribeToConditionSet();
- }
- },
- subscribeToConditionSet() {
- if (this.stopProvidingTelemetry) {
- this.stopProvidingTelemetry();
- delete this.stopProvidingTelemetry;
- }
-
- if (this.conditionSetDomainObject) {
- this.openmct.telemetry.request(this.conditionSetDomainObject)
- .then(output => {
- if (output && output.length) {
- this.handleConditionSetResultUpdated(output[0]);
- }
- });
- this.stopProvidingTelemetry = this.openmct.telemetry.subscribe(this.conditionSetDomainObject, this.handleConditionSetResultUpdated.bind(this));
- }
- },
- handleConditionSetResultUpdated(resultData) {
- this.selectedConditionId = resultData ? resultData.conditionId : '';
- },
- initializeStaticStyle(objectStyles) {
- let staticStyle = objectStyles && objectStyles.staticStyle;
- if (staticStyle) {
- this.staticStyle = {
- style: Object.assign({}, this.initialStyles, staticStyle.style)
- };
- } else {
- this.staticStyle = {
- style: Object.assign({}, this.initialStyles)
- };
- }
- },
- findStyleByConditionId(id) {
- return this.conditionalStyles.find(conditionalStyle => conditionalStyle.conditionId === id);
- },
- updateStaticStyle(staticStyle) {
- this.staticStyle = staticStyle;
- this.persist(this.getDomainObjectConditionalStyle());
- },
- updateConditionalStyle(conditionStyle) {
- let found = this.findStyleByConditionId(conditionStyle.conditionId);
- if (found) {
- found.style = conditionStyle.style;
- this.selectedConditionId = found.conditionId;
- this.persist(this.getDomainObjectConditionalStyle());
- }
- },
- getDomainObjectConditionalStyle(defaultConditionId) {
- let objectStyle = {
- styles: this.conditionalStyles,
- staticStyle: this.staticStyle,
- selectedConditionId: this.selectedConditionId
- };
- if (defaultConditionId) {
- objectStyle.defaultConditionId = defaultConditionId;
- }
-
- if (this.conditionSetDomainObject) {
- objectStyle.conditionSetIdentifier = this.conditionSetDomainObject.identifier;
- }
-
- let domainObjectStyles = (this.domainObject.configuration && this.domainObject.configuration.objectStyles) || {};
-
- if (this.itemId) {
- domainObjectStyles[this.itemId] = objectStyle;
- } else {
- //we're deconstructing here to ensure that if an item within a domainObject already had a style we don't lose it
- domainObjectStyles = {
- ...domainObjectStyles,
- ...objectStyle
- };
- }
-
- return domainObjectStyles;
- },
- getCondition(id) {
- return this.conditions ? this.conditions[id] : {};
- },
- applySelectedConditionStyle(conditionId) {
- this.selectedConditionId = conditionId;
- this.persist(this.getDomainObjectConditionalStyle());
- },
- persist(style) {
- this.openmct.objects.mutate(this.domainObject, 'configuration.objectStyles', style);
- }
- }
-};
-</script>
diff --git a/src/plugins/condition/components/inspector/MultiSelectStylesView.vue b/src/plugins/condition/components/inspector/MultiSelectStylesView.vue
deleted file mode 100644
index d1628fe7d..000000000
--- a/src/plugins/condition/components/inspector/MultiSelectStylesView.vue
+++ /dev/null
@@ -1,280 +0,0 @@
-/*****************************************************************************
-* Open MCT, Copyright (c) 2014-2021, United States Government
-* as represented by the Administrator of the National Aeronautics and Space
-* Administration. All rights reserved.
-*
-* Open MCT is licensed under the Apache License, Version 2.0 (the
-* "License"); you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-* http://www.apache.org/licenses/LICENSE-2.0.
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-* License for the specific language governing permissions and limitations
-* under the License.
-*
-* Open MCT includes source code licensed under additional open source
-* licenses. See the Open Source Licenses file (LICENSES.md) included with
-* this source code distribution or the Licensing information page available
-* at runtime from the About dialog for additional information.
-*****************************************************************************/
-
-<template>
-<div class="c-inspector__styles c-inspect-styles">
- <div class="c-inspect-styles__header">
- Object Style
- </div>
- <div class="c-inspect-styles__content">
- <div v-if="isStaticAndConditionalStyles"
- class="c-inspect-styles__mixed-static-and-conditional u-alert u-alert--block u-alert--with-icon"
- >
- Your selection includes one or more items that use Conditional Styling. Applying a static style below will replace any Conditional Styling with the new choice.
- </div>
- <div v-if="staticStyle"
- class="c-inspect-styles__style"
- >
- <style-editor class="c-inspect-styles__editor"
- :style-item="staticStyle"
- :is-editing="isEditing"
- :mixed-styles="mixedStyles"
- @persist="updateStaticStyle"
- />
- </div>
- </div>
-</div>
-</template>
-
-<script>
-
-import StyleEditor from "./StyleEditor.vue";
-import PreviewAction from "@/ui/preview/PreviewAction.js";
-import { getApplicableStylesForItem, getConsolidatedStyleValues, getConditionalStyleForItem } from "@/plugins/condition/utils/styleUtils";
-import isEmpty from 'lodash/isEmpty';
-
-export default {
- name: 'MultiSelectStylesView',
- components: {
- StyleEditor
- },
- inject: [
- 'openmct',
- 'selection'
- ],
- data() {
- return {
- staticStyle: undefined,
- isEditing: this.openmct.editor.isEditing(),
- mixedStyles: [],
- isStaticAndConditionalStyles: false
- };
- },
- destroyed() {
- this.removeListeners();
- },
- mounted() {
- this.items = [];
- this.previewAction = new PreviewAction(this.openmct);
- this.getObjectsAndItemsFromSelection();
- this.initializeStaticStyle();
- this.openmct.editor.on('isEditing', this.setEditState);
- },
- methods: {
- isItemType(type, item) {
- return item && (item.type === type);
- },
- hasConditionalStyles(domainObject, id) {
- return getConditionalStyleForItem(domainObject, id) !== undefined;
- },
- getObjectsAndItemsFromSelection() {
- let domainObject;
- let subObjects = [];
-
- //multiple selection
- let itemInitialStyles = [];
- let itemStyle;
- this.selection.forEach((selectionItem) => {
- const item = selectionItem[0].context.item;
- const layoutItem = selectionItem[0].context.layoutItem;
- if (item && this.isItemType('subobject-view', layoutItem)) {
- subObjects.push(item);
- itemStyle = getApplicableStylesForItem(item);
- if (!this.isStaticAndConditionalStyles) {
- this.isStaticAndConditionalStyles = this.hasConditionalStyles(item);
- }
- } else {
- domainObject = selectionItem[1].context.item;
- itemStyle = getApplicableStylesForItem(domainObject, layoutItem || item);
- this.items.push({
- id: layoutItem.id,
- applicableStyles: itemStyle
- });
- if (!this.isStaticAndConditionalStyles) {
- this.isStaticAndConditionalStyles = this.hasConditionalStyles(domainObject, layoutItem.id);
- }
- }
-
- itemInitialStyles.push(itemStyle);
- });
- const {styles, mixedStyles} = getConsolidatedStyleValues(itemInitialStyles);
- this.initialStyles = styles;
- this.mixedStyles = mixedStyles;
-
- this.domainObject = domainObject;
- this.removeListeners();
- if (this.domainObject) {
- this.stopObserving = this.openmct.objects.observe(this.domainObject, '*', newDomainObject => this.domainObject = newDomainObject);
- this.stopObservingItems = this.openmct.objects.observe(this.domainObject, 'configuration.items', this.updateDomainObjectItemStyles);
- }
-
- subObjects.forEach(this.registerListener);
- },
- updateDomainObjectItemStyles(newItems) {
- //check that all items that have been styles still exist. Otherwise delete those styles
- let keys = Object.keys(this.domainObject.configuration.objectStyles || {});
- keys.forEach((key) => {
- if ((key !== 'styles')
- && (key !== 'staticStyle')
- && (key !== 'conditionSetIdentifier')) {
- if (!(newItems.find(item => item.id === key))) {
- this.removeItemStyles(key);
- }
- }
- });
- },
- registerListener(domainObject) {
- let id = this.openmct.objects.makeKeyString(domainObject.identifier);
-
- if (!this.domainObjectsById) {
- this.domainObjectsById = {};
- }
-
- if (!this.domainObjectsById[id]) {
- this.domainObjectsById[id] = domainObject;
- this.observeObject(domainObject, id);
- }
- },
- observeObject(domainObject, id) {
- let unobserveObject = this.openmct.objects.observe(domainObject, '*', function (newObject) {
- this.domainObjectsById[id] = JSON.parse(JSON.stringify(newObject));
- }.bind(this));
- this.unObserveObjects.push(unobserveObject);
- },
- removeListeners() {
- if (this.stopObserving) {
- this.stopObserving();
- }
-
- if (this.stopObservingItems) {
- this.stopObservingItems();
- }
-
- if (this.unObserveObjects) {
- this.unObserveObjects.forEach((unObserveObject) => {
- unObserveObject();
- });
- }
-
- this.unObserveObjects = [];
- },
- removeItemStyles(itemId) {
- let domainObjectStyles = (this.domainObject.configuration && this.domainObject.configuration.objectStyles) || {};
- if (itemId && domainObjectStyles[itemId]) {
- domainObjectStyles[itemId] = undefined;
- delete domainObjectStyles[this.itemId];
-
- if (isEmpty(domainObjectStyles)) {
- domainObjectStyles = undefined;
- }
-
- this.persist(this.domainObject, domainObjectStyles);
- }
- },
- removeConditionalStyles(domainObjectStyles, itemId) {
- if (itemId) {
- domainObjectStyles[itemId].conditionSetIdentifier = undefined;
- delete domainObjectStyles[itemId].conditionSetIdentifier;
- domainObjectStyles[itemId].styles = undefined;
- delete domainObjectStyles[itemId].styles;
- } else {
- domainObjectStyles.conditionSetIdentifier = undefined;
- delete domainObjectStyles.conditionSetIdentifier;
- domainObjectStyles.styles = undefined;
- delete domainObjectStyles.styles;
- }
- },
- setEditState(isEditing) {
- this.isEditing = isEditing;
- },
- initializeStaticStyle() {
- this.staticStyle = {
- style: Object.assign({}, this.initialStyles)
- };
- },
- updateStaticStyle(staticStyle, property) {
- //update the static style for each of the layoutItems as well as each sub object item
- this.staticStyle = staticStyle;
- this.persist(this.domainObject, this.getDomainObjectStyle(this.domainObject, property, this.items));
- if (this.domainObjectsById) {
- const keys = Object.keys(this.domainObjectsById);
- keys.forEach(key => {
- let domainObject = this.domainObjectsById[key];
- this.persist(domainObject, this.getDomainObjectStyle(domainObject, property));
- });
- }
-
- this.isStaticAndConditionalStyles = false;
- let foundIndex = this.mixedStyles.indexOf(property);
- if (foundIndex > -1) {
- this.mixedStyles.splice(foundIndex, 1);
- }
- },
- getDomainObjectStyle(domainObject, property, items) {
- let domainObjectStyles = (domainObject.configuration && domainObject.configuration.objectStyles) || {};
-
- if (items) {
- items.forEach(item => {
- let itemStaticStyle = {};
- if (domainObjectStyles[item.id] && domainObjectStyles[item.id].staticStyle) {
- itemStaticStyle = domainObjectStyles[item.id].staticStyle.style;
- }
-
- Object.keys(item.applicableStyles).forEach(key => {
- if (property === key) {
- itemStaticStyle[key] = this.staticStyle.style[key];
- }
- });
- if (this.isStaticAndConditionalStyles) {
- this.removeConditionalStyles(domainObjectStyles, item.id);
- }
-
- if (isEmpty(itemStaticStyle)) {
- itemStaticStyle = undefined;
- domainObjectStyles[item.id] = undefined;
- } else {
- domainObjectStyles[item.id] = Object.assign({}, { staticStyle: { style: itemStaticStyle } });
- }
- });
- } else {
- if (!domainObjectStyles.staticStyle) {
- domainObjectStyles.staticStyle = {
- style: {}
- };
- }
-
- if (this.isStaticAndConditionalStyles) {
- this.removeConditionalStyles(domainObjectStyles);
- }
-
- domainObjectStyles.staticStyle.style[property] = this.staticStyle.style[property];
- }
-
- return domainObjectStyles;
- },
-
- persist(domainObject, style) {
- this.openmct.objects.mutate(domainObject, 'configuration.objectStyles', style);
- }
- }
-};
-</script>
diff --git a/src/plugins/condition/components/inspector/StyleEditor.vue b/src/plugins/condition/components/inspector/StyleEditor.vue
index af1c29f1b..f5a107e75 100644
--- a/src/plugins/condition/components/inspector/StyleEditor.vue
+++ b/src/plugins/condition/components/inspector/StyleEditor.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
-* Open MCT, Copyright (c) 2014-2021, United States Government
+* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -23,52 +23,60 @@
<template>
<div class="c-style has-local-controls c-toolbar">
<div class="c-style__controls">
- <div :class="[
- { 'is-style-invisible': styleItem.style && styleItem.style.isStyleInvisible },
- { 'c-style-thumb--mixed': mixedStyles.indexOf('backgroundColor') > -1 }
- ]"
- :style="[styleItem.style.imageUrl ? { backgroundImage:'url(' + styleItem.style.imageUrl + ')'} : itemStyle ]"
- class="c-style-thumb"
+ <div
+ :class="[
+ { 'is-style-invisible': styleItem.style && styleItem.style.isStyleInvisible },
+ { 'c-style-thumb--mixed': mixedStyles.indexOf('backgroundColor') > -1 }
+ ]"
+ :style="[styleItem.style.imageUrl ? { backgroundImage:'url(' + styleItem.style.imageUrl + ')'} : itemStyle ]"
+ class="c-style-thumb"
>
- <span class="c-style-thumb__text"
- :class="{ 'hide-nice': !hasProperty(styleItem.style.color) }"
+ <span
+ class="c-style-thumb__text"
+ :class="{ 'hide-nice': !hasProperty(styleItem.style.color) }"
>
ABC
</span>
</div>
- <toolbar-color-picker v-if="hasProperty(styleItem.style.border)"
- class="c-style__toolbar-button--border-color u-menu-to--center"
- :options="borderColorOption"
- @change="updateStyleValue"
+ <toolbar-color-picker
+ v-if="hasProperty(styleItem.style.border)"
+ class="c-style__toolbar-button--border-color u-menu-to--center"
+ :options="borderColorOption"
+ @change="updateStyleValue"
/>
- <toolbar-color-picker v-if="hasProperty(styleItem.style.backgroundColor)"
- class="c-style__toolbar-button--background-color u-menu-to--center"
- :options="backgroundColorOption"
- @change="updateStyleValue"
+ <toolbar-color-picker
+ v-if="hasProperty(styleItem.style.backgroundColor)"
+ class="c-style__toolbar-button--background-color u-menu-to--center"
+ :options="backgroundColorOption"
+ @change="updateStyleValue"
/>
- <toolbar-color-picker v-if="hasProperty(styleItem.style.color)"
- class="c-style__toolbar-button--color u-menu-to--center"
- :options="colorOption"
- @change="updateStyleValue"
+ <toolbar-color-picker
+ v-if="hasProperty(styleItem.style.color)"
+ class="c-style__toolbar-button--color u-menu-to--center"
+ :options="colorOption"
+ @change="updateStyleValue"
/>
- <toolbar-button v-if="hasProperty(styleItem.style.imageUrl)"
- class="c-style__toolbar-button--image-url"
- :options="imageUrlOption"
- @change="updateStyleValue"
+ <toolbar-button
+ v-if="hasProperty(styleItem.style.imageUrl)"
+ class="c-style__toolbar-button--image-url"
+ :options="imageUrlOption"
+ @change="updateStyleValue"
/>
- <toolbar-toggle-button v-if="hasProperty(styleItem.style.isStyleInvisible)"
- class="c-style__toolbar-button--toggle-visible"
- :options="isStyleInvisibleOption"
- @change="updateStyleValue"
+ <toolbar-toggle-button
+ v-if="hasProperty(styleItem.style.isStyleInvisible)"
+ class="c-style__toolbar-button--toggle-visible"
+ :options="isStyleInvisibleOption"
+ @change="updateStyleValue"
/>
</div>
<!-- Save Styles -->
- <toolbar-button v-if="canSaveStyle"
- class="c-style__toolbar-button--save c-local-controls--show-on-hover c-icon-button c-icon-button--major"
- :options="saveOptions"
- @click="saveItemStyle()"
+ <toolbar-button
+ v-if="canSaveStyle"
+ class="c-style__toolbar-button--save c-local-controls--show-on-hover c-icon-button c-icon-button--major"
+ :options="saveOptions"
+ @click="saveItemStyle()"
/>
</div>
</template>
diff --git a/src/plugins/condition/components/inspector/StylesView.vue b/src/plugins/condition/components/inspector/StylesView.vue
index b7d1d695a..378776d5c 100644
--- a/src/plugins/condition/components/inspector/StylesView.vue
+++ b/src/plugins/condition/components/inspector/StylesView.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
-* Open MCT, Copyright (c) 2014-2021, United States Government
+* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -22,8 +22,9 @@
<template>
<div class="c-inspector__styles c-inspect-styles">
- <div v-if="isStaticAndConditionalStyles"
- class="c-inspect-styles__mixed-static-and-conditional u-alert u-alert--block u-alert--with-icon"
+ <div
+ v-if="isStaticAndConditionalStyles"
+ class="c-inspect-styles__mixed-static-and-conditional u-alert u-alert--block u-alert--with-icon"
>
Your selection includes one or more items that use Conditional Styling. Applying a static style below will replace any Conditional Styling with the new choice.
</div>
@@ -37,16 +38,18 @@
@set-font-property="setFontProperty"
/>
<div class="c-inspect-styles__content">
- <div v-if="staticStyle"
- class="c-inspect-styles__style"
+ <div
+ v-if="staticStyle"
+ class="c-inspect-styles__style"
>
- <StyleEditor class="c-inspect-styles__editor"
- :style-item="staticStyle"
- :is-editing="allowEditing"
- :mixed-styles="mixedStyles"
- :non-specific-font-properties="nonSpecificFontProperties"
- @persist="updateStaticStyle"
- @save-style="saveStyle"
+ <StyleEditor
+ class="c-inspect-styles__editor"
+ :style-item="staticStyle"
+ :is-editing="allowEditing"
+ :mixed-styles="mixedStyles"
+ :non-specific-font-properties="nonSpecificFontProperties"
+ @persist="updateStaticStyle"
+ @save-style="saveStyle"
/>
</div>
<button
@@ -63,10 +66,11 @@
<div class="c-inspect-styles__header">
Conditional Object Styles
</div>
- <div class="c-inspect-styles__content c-inspect-styles__condition-set">
- <a v-if="conditionSetDomainObject"
- class="c-object-label"
- @click="navigateOrPreview"
+ <div class="c-inspect-styles__content c-inspect-styles__condition-set c-inspect-styles__elem">
+ <a
+ v-if="conditionSetDomainObject"
+ class="c-object-label"
+ @click="navigateOrPreview"
>
<span class="c-object-label__type-icon icon-conditional"></span>
<span class="c-object-label__name">{{ conditionSetDomainObject.name }}</span>
@@ -80,40 +84,69 @@
<span class="c-button__label">Change...</span>
</button>
- <button class="c-click-icon icon-x"
- title="Remove conditional styles"
- @click="removeConditionSet"
+ <button
+ class="c-click-icon icon-x"
+ title="Remove conditional styles"
+ @click="removeConditionSet"
></button>
</template>
</div>
+ <div
+ v-if="isConditionWidget && allowEditing"
+ class="c-inspect-styles__elem c-inspect-styles__output-label-toggle"
+ >
+ <label class="c-toggle-switch">
+ <input
+ type="checkbox"
+ :checked="useConditionSetOutputAsLabel"
+ @change="updateConditionSetOutputLabel"
+ >
+ <span class="c-toggle-switch__slider"></span>
+ <span class="c-toggle-switch__label">Use Condition Set output as label</span>
+ </label>
+ </div>
+ <div
+ v-if="isConditionWidget && !allowEditing"
+ class="c-inspect-styles__elem"
+ >
+ <span class="c-toggle-switch__label">Condition Set output as label:
+ <span v-if="useConditionSetOutputAsLabel"> Yes</span><span v-else> No</span>
+ </span>
+ </div>
+
<FontStyleEditor
v-if="canStyleFont"
:font-style="consolidatedFontStyle"
@set-font-property="setFontProperty"
/>
- <div v-if="conditionsLoaded"
- class="c-inspect-styles__conditions"
+ <div
+ v-if="conditionsLoaded"
+ class="c-inspect-styles__conditions"
>
- <div v-for="(conditionStyle, index) in conditionalStyles"
- :key="index"
- class="c-inspect-styles__condition"
- :class="{'is-current': conditionStyle.conditionId === selectedConditionId}"
- @click="applySelectedConditionStyle(conditionStyle.conditionId)"
+ <div
+ v-for="(conditionStyle, index) in conditionalStyles"
+ :key="index"
+ class="c-inspect-styles__condition"
+ :class="{'is-current': conditionStyle.conditionId === selectedConditionId}"
+ @click="applySelectedConditionStyle(conditionStyle.conditionId)"
>
- <condition-error :show-label="true"
- :condition="getCondition(conditionStyle.conditionId)"
+ <condition-error
+ :show-label="true"
+ :condition="getCondition(conditionStyle.conditionId)"
/>
- <condition-description :show-label="true"
- :condition="getCondition(conditionStyle.conditionId)"
+ <condition-description
+ :show-label="true"
+ :condition="getCondition(conditionStyle.conditionId)"
/>
- <StyleEditor class="c-inspect-styles__editor"
- :style-item="conditionStyle"
- :non-specific-font-properties="nonSpecificFontProperties"
- :is-editing="allowEditing"
- @persist="updateConditionalStyle"
- @save-style="saveStyle"
+ <StyleEditor
+ class="c-inspect-styles__editor"
+ :style-item="conditionStyle"
+ :non-specific-font-properties="nonSpecificFontProperties"
+ :is-editing="allowEditing"
+ @persist="updateConditionalStyle"
+ @save-style="saveStyle"
/>
</div>
</div>
@@ -127,10 +160,8 @@ import FontStyleEditor from '@/ui/inspector/styles/FontStyleEditor.vue';
import StyleEditor from "./StyleEditor.vue";
import PreviewAction from "@/ui/preview/PreviewAction.js";
import { getApplicableStylesForItem, getConsolidatedStyleValues, getConditionSetIdentifierForItem } from "@/plugins/condition/utils/styleUtils";
-import SelectorDialogTree from '@/ui/components/SelectorDialogTree.vue';
import ConditionError from "@/plugins/condition/components/ConditionError.vue";
import ConditionDescription from "@/plugins/condition/components/ConditionDescription.vue";
-import Vue from 'vue';
const NON_SPECIFIC = '??';
const NON_STYLEABLE_CONTAINER_TYPES = [
@@ -172,7 +203,8 @@ export default {
selectedConditionId: '',
items: [],
domainObject: undefined,
- consolidatedFontStyle: {}
+ consolidatedFontStyle: {},
+ useConditionSetOutputAsLabel: false
};
},
computed: {
@@ -187,6 +219,11 @@ export default {
allowEditing() {
return this.isEditing && !this.locked;
},
+ isConditionWidget() {
+ const hasConditionWidgetObjects = this.domainObjectsById && Object.values(this.domainObjectsById).some((object) => object.type === 'conditionWidget');
+
+ return (hasConditionWidgetObjects || (this.domainObject && this.domainObject.type === 'conditionWidget'));
+ },
styleableFontItems() {
return this.selection.filter(selectionPath => {
const item = selectionPath[0].context.item;
@@ -205,28 +242,6 @@ export default {
return true;
});
},
- computedconsolidatedFontStyle() {
- let consolidatedFontStyle;
- const styles = [];
-
- this.styleableFontItems.forEach(styleable => {
- const fontStyle = this.getFontStyle(styleable[0]);
-
- styles.push(fontStyle);
- });
-
- if (styles.length) {
- const hasConsolidatedFontSize = styles.length && styles.every((fontStyle, i, arr) => fontStyle.fontSize === arr[0].fontSize);
- const hasConsolidatedFont = styles.length && styles.every((fontStyle, i, arr) => fontStyle.font === arr[0].font);
-
- consolidatedFontStyle = {
- fontSize: hasConsolidatedFontSize ? styles[0].fontSize : NON_SPECIFIC,
- font: hasConsolidatedFont ? styles[0].font : NON_SPECIFIC
- };
- }
-
- return consolidatedFontStyle;
- },
nonSpecificFontProperties() {
if (!this.consolidatedFontStyle) {
return [];
@@ -247,6 +262,8 @@ export default {
this.previewAction = new PreviewAction(this.openmct);
this.isMultipleSelection = this.selection.length > 1;
this.getObjectsAndItemsFromSelection();
+ this.useConditionSetOutputAsLabel = this.getConfigurationForLabel();
+
if (!this.isMultipleSelection) {
let objectStyles = this.getObjectStyles();
this.initializeStaticStyle(objectStyles);
@@ -264,6 +281,12 @@ export default {
this.stylesManager.on('styleSelected', this.applyStyleToSelection);
},
methods: {
+ getConfigurationForLabel() {
+ const childObjectUsesLabels = Object.values(this.domainObjectsById || {}).some((object) => object.configuration && object.configuration.useConditionSetOutputAsLabel);
+ const domainObjectUsesLabels = this.domainObject && this.domainObject.configuration && this.domainObject.configuration.useConditionSetOutputAsLabel;
+
+ return childObjectUsesLabels || domainObjectUsesLabels;
+ },
getObjectStyles() {
let objectStyles;
if (this.domainObjectsById) {
@@ -314,16 +337,7 @@ export default {
return item && (item.type === type);
},
canPersistObject(item) {
- // for now the only way to tell if an object can be persisted is if it is creatable.
- let creatable = false;
- if (item) {
- const type = this.openmct.types.get(item.type);
- if (type && type.definition) {
- creatable = (type.definition.creatable !== undefined && (type.definition.creatable === 'true' || type.definition.creatable === true));
- }
- }
-
- return creatable;
+ return this.openmct.objects.isPersistable(item.identifier);
},
hasConditionalStyle(domainObject, layoutItem) {
const id = layoutItem ? layoutItem.id : undefined;
@@ -487,13 +501,14 @@ export default {
this.conditions[conditionConfiguration.id] = conditionConfiguration;
let foundStyle = this.findStyleByConditionId(conditionConfiguration.id);
+ let output = { output: conditionConfiguration.configuration.output };
if (foundStyle) {
- foundStyle.style = Object.assign((this.canHide ? { isStyleInvisible: '' } : {}), this.initialStyles, foundStyle.style);
+ foundStyle.style = Object.assign((this.canHide ? { isStyleInvisible: '' } : {}), this.initialStyles, foundStyle.style, output);
conditionalStyles.push(foundStyle);
} else {
conditionalStyles.splice(index, 0, {
conditionId: conditionConfiguration.id,
- style: Object.assign((this.canHide ? { isStyleInvisible: '' } : {}), this.initialStyles)
+ style: Object.assign((this.canHide ? { isStyleInvisible: '' } : {}), this.initialStyles, output)
});
}
});
@@ -537,53 +552,28 @@ export default {
return this.conditions ? this.conditions[id] : {};
},
addConditionSet() {
- let conditionSetDomainObject;
- let self = this;
- function handleItemSelection({ item }) {
- if (item) {
- conditionSetDomainObject = item;
- }
- }
-
- function dismissDialog(overlay, initialize) {
- overlay.dismiss();
-
- if (initialize && conditionSetDomainObject) {
- self.conditionSetDomainObject = conditionSetDomainObject;
- self.conditionalStyles = [];
- self.initializeConditionalStyles();
- }
- }
+ const conditionWidgetParent = this.openmct.router.path[1];
+ const formStructure = {
+ title: 'Select Condition Set',
+ sections: [{
+ name: 'Location',
+ cssClass: 'grows',
+ rows: [{
+ key: 'location',
+ name: 'Condition Set',
+ cssClass: 'grows',
+ control: 'locator',
+ required: true,
+ parent: conditionWidgetParent,
+ validate: data => data.value[0].type === 'conditionSet'
+ }]
+ }]
+ };
- let vm = new Vue({
- components: { SelectorDialogTree },
- provide: {
- openmct: this.openmct
- },
- data() {
- return {
- handleItemSelection,
- title: 'Select Condition Set'
- };
- },
- template: '<SelectorDialogTree :title="title" @treeItemSelected="handleItemSelection"></SelectorDialogTree>'
- }).$mount();
-
- let overlay = this.openmct.overlays.overlay({
- element: vm.$el,
- size: 'small',
- buttons: [
- {
- label: 'OK',
- emphasis: 'true',
- callback: () => dismissDialog(overlay, true)
- },
- {
- label: 'Cancel',
- callback: () => dismissDialog(overlay, false)
- }
- ],
- onDestroy: () => vm.$destroy()
+ this.openmct.forms.showForm(formStructure).then(data => {
+ this.conditionSetDomainObject = data.location[0];
+ this.conditionalStyles = [];
+ this.initializeConditionalStyles();
});
},
removeConditionSet() {
@@ -715,6 +705,12 @@ export default {
} else {
objectStyle.styles.forEach((conditionalStyle, index) => {
let style = {};
+ if (domainObject.configuration.useConditionSetOutputAsLabel) {
+ style.output = conditionalStyle.style.output;
+ } else {
+ style.output = '';
+ }
+
Object.keys(item.applicableStyles).concat(['isStyleInvisible']).forEach(key => {
style[key] = conditionalStyle.style[key];
});
@@ -731,10 +727,21 @@ export default {
}
});
} else {
- domainObjectStyles = {
- ...domainObjectStyles,
- ...objectStyle
- };
+ if (domainObject.configuration.useConditionSetOutputAsLabel !== true) {
+ let objectConditionStyle = JSON.parse(JSON.stringify(objectStyle));
+ objectConditionStyle.styles.forEach((conditionalStyle) => {
+ conditionalStyle.style.output = '';
+ });
+ domainObjectStyles = {
+ ...domainObjectStyles,
+ ...objectConditionStyle
+ };
+ } else {
+ domainObjectStyles = {
+ ...domainObjectStyles,
+ ...objectStyle
+ };
+ }
}
return domainObjectStyles;
@@ -743,6 +750,17 @@ export default {
this.selectedConditionId = conditionId;
this.getAndPersistStyles();
},
+ persistLabelConfiguration() {
+ if (this.domainObjectsById) {
+ Object.values(this.domainObjectsById).forEach((object) => {
+ this.openmct.objects.mutate(object, 'configuration.useConditionSetOutputAsLabel', this.useConditionSetOutputAsLabel);
+ });
+ } else {
+ this.openmct.objects.mutate(this.domainObject, 'configuration.useConditionSetOutputAsLabel', this.useConditionSetOutputAsLabel);
+ }
+
+ this.getAndPersistStyles();
+ },
persist(domainObject, style) {
this.openmct.objects.mutate(domainObject, 'configuration.objectStyles', style);
},
@@ -863,6 +881,10 @@ export default {
const layoutItemType = selectionPath[0].context.layoutItem && selectionPath[0].context.layoutItem.type;
return layoutItemType && layoutItemType !== 'subobject-view';
+ },
+ updateConditionSetOutputLabel(event) {
+ this.useConditionSetOutputAsLabel = event.target.checked === true;
+ this.persistLabelConfiguration();
}
}
};
diff --git a/src/plugins/condition/components/inspector/conditional-styles.scss b/src/plugins/condition/components/inspector/conditional-styles.scss
index 9679bdddd..297664053 100644
--- a/src/plugins/condition/components/inspector/conditional-styles.scss
+++ b/src/plugins/condition/components/inspector/conditional-styles.scss
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -39,12 +39,15 @@
flex-direction: column;
}
+ &__elem {
+ border-bottom: 1px solid $colorInteriorBorder;
+ padding-bottom: $interiorMargin;
+ }
+
&__condition-set {
align-items: baseline;
- border-bottom: 1px solid $colorInteriorBorder;
display: flex;
flex-direction: row;
- padding-bottom: $interiorMargin;
.c-object-label {
flex: 1 1 auto;
diff --git a/src/plugins/condition/criterion/AllTelemetryCriterion.js b/src/plugins/condition/criterion/AllTelemetryCriterion.js
index f447caa76..5ef202e28 100644
--- a/src/plugins/condition/criterion/AllTelemetryCriterion.js
+++ b/src/plugins/condition/criterion/AllTelemetryCriterion.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/condition/criterion/TelemetryCriterion.js b/src/plugins/condition/criterion/TelemetryCriterion.js
index 1a391ad47..e343b9d59 100644
--- a/src/plugins/condition/criterion/TelemetryCriterion.js
+++ b/src/plugins/condition/criterion/TelemetryCriterion.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/condition/criterion/TelemetryCriterionSpec.js b/src/plugins/condition/criterion/TelemetryCriterionSpec.js
index 7fbfa2f0a..fd69ac432 100644
--- a/src/plugins/condition/criterion/TelemetryCriterionSpec.js
+++ b/src/plugins/condition/criterion/TelemetryCriterionSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/condition/plugin.js b/src/plugins/condition/plugin.js
index 70f244abb..df56d35df 100644
--- a/src/plugins/condition/plugin.js
+++ b/src/plugins/condition/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -23,7 +23,7 @@ import ConditionSetViewProvider from './ConditionSetViewProvider.js';
import ConditionSetCompositionPolicy from "./ConditionSetCompositionPolicy";
import ConditionSetMetadataProvider from './ConditionSetMetadataProvider';
import ConditionSetTelemetryProvider from './ConditionSetTelemetryProvider';
-import uuid from "uuid";
+import { v4 as uuid } from 'uuid';
export default function ConditionPlugin() {
diff --git a/src/plugins/condition/pluginSpec.js b/src/plugins/condition/pluginSpec.js
index e148872ce..b8bca44cb 100644
--- a/src/plugins/condition/pluginSpec.js
+++ b/src/plugins/condition/pluginSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -133,6 +133,168 @@ describe('the plugin', function () {
});
});
+ describe('the condition set usage for condition widgets', () => {
+ let conditionWidgetItem;
+ let selection;
+ let component;
+ let styleViewComponentObject;
+ const conditionSetDomainObject = {
+ "configuration": {
+ "conditionTestData": [
+ {
+ "telemetry": "",
+ "metadata": "",
+ "input": ""
+ }
+ ],
+ "conditionCollection": [
+ {
+ "id": "39584410-cbf9-499e-96dc-76f27e69885d",
+ "configuration": {
+ "name": "Unnamed Condition",
+ "output": "Sine > 0",
+ "trigger": "all",
+ "criteria": [
+ {
+ "id": "85fbb2f7-7595-42bd-9767-a932266c5225",
+ "telemetry": {
+ "namespace": "",
+ "key": "be0ba97f-b510-4f40-a18d-4ff121d5ea1a"
+ },
+ "operation": "greaterThan",
+ "input": [
+ "0"
+ ],
+ "metadata": "sin"
+ },
+ {
+ "id": "35400132-63b0-425c-ac30-8197df7d5862",
+ "telemetry": "any",
+ "operation": "enumValueIs",
+ "input": [
+ "0"
+ ],
+ "metadata": "state"
+ }
+ ]
+ },
+ "summary": "Match if all criteria are met: Sine Wave Generator Sine > 0 and any telemetry State is OFF "
+ },
+ {
+ "isDefault": true,
+ "id": "2532d90a-e0d6-4935-b546-3123522da2de",
+ "configuration": {
+ "name": "Default",
+ "output": "Default",
+ "trigger": "all",
+ "criteria": [
+ ]
+ },
+ "summary": ""
+ }
+ ]
+ },
+ "composition": [
+ {
+ "namespace": "",
+ "key": "be0ba97f-b510-4f40-a18d-4ff121d5ea1a"
+ },
+ {
+ "namespace": "",
+ "key": "077ffa67-e78f-4e99-80e0-522ac33a3888"
+ }
+ ],
+ "telemetry": {
+ },
+ "name": "Condition Set",
+ "type": "conditionSet",
+ "identifier": {
+ "namespace": "",
+ "key": "863012c1-f6ca-4ab0-aed7-fd43d5e4cd12"
+ }
+
+ };
+
+ beforeEach(() => {
+ conditionWidgetItem = {
+ "label": "Condition Widget",
+ "conditionalLabel": "",
+ "configuration": {
+ },
+ "name": "Condition Widget",
+ "type": "conditionWidget",
+ "identifier": {
+ "namespace": "",
+ "key": "c5e636c1-6771-4c9c-b933-8665cab189b3"
+ }
+ };
+ selection = [
+ [{
+ context: {
+ "item": conditionWidgetItem,
+ "supportsMultiSelect": false
+ }
+ }]
+ ];
+ let viewContainer = document.createElement('div');
+ child.append(viewContainer);
+ component = new Vue({
+ el: viewContainer,
+ components: {
+ StylesView
+ },
+ provide: {
+ openmct: openmct,
+ selection: selection,
+ stylesManager
+ },
+ template: '<styles-view/>'
+ });
+
+ return Vue.nextTick().then(() => {
+ styleViewComponentObject = component.$root.$children[0];
+ styleViewComponentObject.setEditState(true);
+ });
+ });
+
+ afterEach(() => {
+ component.$destroy();
+ });
+
+ it('does not include the output label when the flag is disabled', () => {
+ styleViewComponentObject.conditionSetDomainObject = conditionSetDomainObject;
+ styleViewComponentObject.conditionalStyles = [];
+ styleViewComponentObject.initializeConditionalStyles();
+ expect(styleViewComponentObject.conditionalStyles.length).toBe(2);
+
+ return Vue.nextTick().then(() => {
+ const hasNoOutput = styleViewComponentObject.domainObject.configuration.objectStyles.styles.every((style) => {
+ return style.style.output === '' || style.style.output === undefined;
+ });
+
+ expect(hasNoOutput).toBeTrue();
+ });
+ });
+
+ it('includes the output label when the flag is enabled', () => {
+ styleViewComponentObject.conditionSetDomainObject = conditionSetDomainObject;
+ styleViewComponentObject.conditionalStyles = [];
+ styleViewComponentObject.initializeConditionalStyles();
+ expect(styleViewComponentObject.conditionalStyles.length).toBe(2);
+
+ styleViewComponentObject.useConditionSetOutputAsLabel = true;
+ styleViewComponentObject.persistLabelConfiguration();
+
+ return Vue.nextTick().then(() => {
+ const outputs = styleViewComponentObject.domainObject.configuration.objectStyles.styles.map((style) => {
+ return style.style.output;
+ });
+ expect(outputs.join(',')).toEqual('Sine > 0,Default');
+ });
+ });
+
+ });
+
describe('the condition set usage for multiple display layout items', () => {
let displayLayoutItem;
let lineLayoutItem;
@@ -449,6 +611,10 @@ describe('the plugin', function () {
const applicableStyles = getApplicableStylesForItem(styleViewComponentObject.domainObject, item);
const applicableStylesKeys = Object.keys(applicableStyles).concat(['isStyleInvisible']);
Object.keys(foundStyle.style).forEach((key) => {
+ if (key === 'output') {
+ return;
+ }
+
expect(applicableStylesKeys.indexOf(key)).toBeGreaterThan(-1);
expect(foundStyle.style[key]).toEqual(conditionalStyle.style[key]);
});
diff --git a/src/plugins/condition/utils/constants.js b/src/plugins/condition/utils/constants.js
index 8f4cb5d64..faeb7c6a7 100644
--- a/src/plugins/condition/utils/constants.js
+++ b/src/plugins/condition/utils/constants.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/condition/utils/evaluator.js b/src/plugins/condition/utils/evaluator.js
index 34914358f..3fd332536 100644
--- a/src/plugins/condition/utils/evaluator.js
+++ b/src/plugins/condition/utils/evaluator.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/condition/utils/evaluatorSpec.js b/src/plugins/condition/utils/evaluatorSpec.js
index c3bf8d8a7..66f13a9f7 100644
--- a/src/plugins/condition/utils/evaluatorSpec.js
+++ b/src/plugins/condition/utils/evaluatorSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/condition/utils/operations.js b/src/plugins/condition/utils/operations.js
index 947c7e477..515e17293 100644
--- a/src/plugins/condition/utils/operations.js
+++ b/src/plugins/condition/utils/operations.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/condition/utils/operationsSpec.js b/src/plugins/condition/utils/operationsSpec.js
index 033635857..f6fe6f536 100644
--- a/src/plugins/condition/utils/operationsSpec.js
+++ b/src/plugins/condition/utils/operationsSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/condition/utils/styleUtils.js b/src/plugins/condition/utils/styleUtils.js
index 98d2adaf2..ff55b4eae 100644
--- a/src/plugins/condition/utils/styleUtils.js
+++ b/src/plugins/condition/utils/styleUtils.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/condition/utils/time.js b/src/plugins/condition/utils/time.js
index 296c0ccd4..2c2871851 100644
--- a/src/plugins/condition/utils/time.js
+++ b/src/plugins/condition/utils/time.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/condition/utils/timeSpec.js b/src/plugins/condition/utils/timeSpec.js
index 3435f2c46..043ac2ce1 100644
--- a/src/plugins/condition/utils/timeSpec.js
+++ b/src/plugins/condition/utils/timeSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/conditionWidget/ConditionWidgetViewProvider.js b/src/plugins/conditionWidget/ConditionWidgetViewProvider.js
index 3b1a2c4a7..3408e212e 100644
--- a/src/plugins/conditionWidget/ConditionWidgetViewProvider.js
+++ b/src/plugins/conditionWidget/ConditionWidgetViewProvider.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/conditionWidget/components/ConditionWidget.vue b/src/plugins/conditionWidget/components/ConditionWidget.vue
index bc37fb736..ef865b1c6 100644
--- a/src/plugins/conditionWidget/components/ConditionWidget.vue
+++ b/src/plugins/conditionWidget/components/ConditionWidget.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
-* Open MCT, Copyright (c) 2014-2021, United States Government
+* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -21,12 +21,13 @@
*****************************************************************************/
<template>
-<component :is="urlDefined ? 'a' : 'span'"
- class="c-condition-widget u-style-receiver js-style-receiver"
- :href="url"
+<component
+ :is="urlDefined ? 'a' : 'span'"
+ class="c-condition-widget u-style-receiver js-style-receiver"
+ :href="url"
>
<div class="c-condition-widget__label">
- {{ internalDomainObject.label }}
+ {{ label }}
</div>
</component>
</template>
@@ -38,28 +39,112 @@ export default {
inject: ['openmct', 'domainObject'],
data: function () {
return {
- internalDomainObject: this.domainObject
+ conditionalLabel: '',
+ conditionSetIdentifier: null,
+ domainObjectLabel: '',
+ url: null,
+ urlDefined: false,
+ useConditionSetOutputAsLabel: false
};
},
computed: {
- urlDefined() {
- return this.internalDomainObject.url && this.internalDomainObject.url.length > 0;
- },
- url() {
- return this.urlDefined ? sanitizeUrl(this.internalDomainObject.url) : null;
+ label() {
+ return this.useConditionSetOutputAsLabel
+ ? this.conditionalLabel
+ : this.domainObjectLabel
+ ;
+ }
+ },
+ watch: {
+ conditionSetIdentifier: {
+ handler(newValue, oldValue) {
+ if (!oldValue || !newValue || !this.openmct.objects.areIdsEqual(newValue, oldValue)) {
+ return;
+ }
+
+ this.listenToConditionSetChanges();
+ },
+ deep: true
}
},
mounted() {
- this.unlisten = this.openmct.objects.observe(this.internalDomainObject, '*', this.updateInternalDomainObject);
+ this.unlisten = this.openmct.objects.observe(this.domainObject, '*', this.updateDomainObject);
+
+ if (this.domainObject) {
+ this.updateDomainObject(this.domainObject);
+ this.listenToConditionSetChanges();
+ }
},
beforeDestroy() {
+ this.conditionSetIdentifier = null;
+
if (this.unlisten) {
this.unlisten();
}
+
+ this.stopListeningToConditionSetChanges();
},
methods: {
- updateInternalDomainObject(domainObject) {
- this.internalDomainObject = domainObject;
+ async listenToConditionSetChanges() {
+ if (!this.conditionSetIdentifier) {
+ return;
+ }
+
+ const conditionSetDomainObject = await this.openmct.objects.get(this.conditionSetIdentifier);
+ this.stopListeningToConditionSetChanges();
+
+ if (!conditionSetDomainObject) {
+ this.openmct.notifications.alert('Unable to find condition set');
+ }
+
+ this.telemetryCollection = this.openmct.telemetry.requestCollection(conditionSetDomainObject, {
+ size: 1,
+ strategy: 'latest'
+ });
+
+ this.telemetryCollection.on('add', this.updateConditionLabel, this);
+ this.telemetryCollection.load();
+ },
+ stopListeningToConditionSetChanges() {
+ if (this.telemetryCollection) {
+ this.telemetryCollection.off('add', this.updateConditionLabel, this);
+ this.telemetryCollection.destroy();
+ this.telemetryCollection = null;
+ }
+ },
+ updateConditionLabel([latestDatum]) {
+ if (!this.conditionSetIdentifier) {
+ this.stopListeningToConditionSetChanges();
+
+ return;
+ }
+
+ this.conditionalLabel = latestDatum.output || '';
+ },
+ updateDomainObject(domainObject) {
+ if (this.domainObjectLabel !== domainObject.label) {
+ this.domainObjectLabel = domainObject.label;
+ }
+
+ const urlDefined = domainObject.url && domainObject.url.length > 0;
+ if (this.urlDefined !== urlDefined) {
+ this.urlDefined = urlDefined;
+ }
+
+ const url = this.urlDefined ? sanitizeUrl(domainObject.url) : null;
+ if (this.url !== url) {
+ this.url = url;
+ }
+
+ const conditionSetIdentifier = domainObject.configuration?.objectStyles?.conditionSetIdentifier;
+ if (conditionSetIdentifier && this.conditionSetIdentifier !== conditionSetIdentifier) {
+ this.conditionSetIdentifier = conditionSetIdentifier;
+ }
+
+ const useConditionSetOutputAsLabel = this.conditionSetIdentifier && domainObject.configuration.useConditionSetOutputAsLabel;
+ if (this.useConditionSetOutputAsLabel !== useConditionSetOutputAsLabel) {
+ this.useConditionSetOutputAsLabel = useConditionSetOutputAsLabel;
+ }
}
}
};
diff --git a/src/plugins/conditionWidget/components/condition-widget.scss b/src/plugins/conditionWidget/components/condition-widget.scss
index 8f9adec74..7c06ff989 100644
--- a/src/plugins/conditionWidget/components/condition-widget.scss
+++ b/src/plugins/conditionWidget/components/condition-widget.scss
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/conditionWidget/plugin.js b/src/plugins/conditionWidget/plugin.js
index 9b9440aed..39f1230d2 100644
--- a/src/plugins/conditionWidget/plugin.js
+++ b/src/plugins/conditionWidget/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -27,12 +27,15 @@ export default function plugin() {
openmct.objectViews.addProvider(new ConditionWidgetViewProvider(openmct));
openmct.types.addType('conditionWidget', {
+ key: 'conditionWidget',
name: "Condition Widget",
description: "A button that can be used on its own, or dynamically styled with a Condition Set.",
creatable: true,
cssClass: 'icon-condition-widget',
initialize(domainObject) {
+ domainObject.configuration = {};
domainObject.label = 'Condition Widget';
+ domainObject.conditionalLabel = '';
},
form: [
{
diff --git a/src/plugins/conditionWidget/pluginSpec.js b/src/plugins/conditionWidget/pluginSpec.js
new file mode 100644
index 000000000..033516f13
--- /dev/null
+++ b/src/plugins/conditionWidget/pluginSpec.js
@@ -0,0 +1,195 @@
+import { createOpenMct, resetApplicationState } from "utils/testing";
+import ConditionWidgetPlugin from "./plugin";
+import Vue from 'vue';
+
+describe('the plugin', function () {
+ const CONDITION_WIDGET_KEY = 'conditionWidget';
+ let objectDef;
+ let element;
+ let child;
+ let openmct;
+ let mockConditionObjectDefinition;
+ let mockConditionObject;
+ let mockConditionObjectPath;
+
+ beforeEach((done) => {
+ mockConditionObjectPath = [
+ {
+ name: 'mock folder',
+ type: 'fake-folder',
+ identifier: {
+ key: 'mock-folder',
+ namespace: ''
+ }
+ },
+ {
+ name: 'mock parent folder',
+ type: 'conditionWidget',
+ identifier: {
+ key: 'mock-parent-folder',
+ namespace: ''
+ }
+ }
+ ];
+
+ mockConditionObjectDefinition = {
+ name: 'Condition Widget',
+ key: 'conditionWidget',
+ creatable: true
+ };
+
+ mockConditionObject = {
+ "conditionWidget": {
+ "identifier": {
+ "namespace": "",
+ "key": "condition-widget-object"
+ },
+ "url": "https://nasa.github.io/openmct/",
+ "label": "Foo Widget",
+ "type": "conditionWidget",
+ "composition": []
+ },
+ "telemetry": {
+ "identifier": {
+ "namespace": "",
+ "key": "telemetry-object"
+ },
+ "type": "test-telemetry-object",
+ "name": "Test Telemetry Object",
+ "telemetry": {
+ "values": [
+ {
+ "key": "name",
+ "name": "Name",
+ "format": "string"
+ },
+ {
+ "key": "utc",
+ "name": "Time",
+ "format": "utc",
+ "hints": {
+ "domain": 1
+ }
+ },
+ {
+ "name": "Some attribute 1",
+ "key": "some-key-1",
+ "hints": {
+ "range": 1
+ }
+ },
+ {
+ "name": "Some attribute 2",
+ "key": "some-key-2"
+ }
+ ]
+ }
+ }
+ };
+
+ const timeSystem = {
+ timeSystemKey: 'utc',
+ bounds: {
+ start: 1597160002854,
+ end: 1597181232854
+ }
+ };
+
+ openmct = createOpenMct(timeSystem);
+ openmct.install(new ConditionWidgetPlugin());
+
+ objectDef = openmct.types.get('conditionWidget').definition;
+
+ element = document.createElement('div');
+ element.style.width = '640px';
+ element.style.height = '480px';
+ child = document.createElement('div');
+ child.style.width = '640px';
+ child.style.height = '480px';
+ element.appendChild(child);
+
+ openmct.on('start', done);
+ openmct.startHeadless();
+ });
+
+ afterEach(() => {
+ return resetApplicationState(openmct);
+ });
+
+ it('defines a conditionWidget object type with the correct key', () => {
+ expect(objectDef.key).toEqual(mockConditionObjectDefinition.key);
+ });
+
+ describe('the conditionWidget object', () => {
+ it('is creatable', () => {
+ expect(objectDef.creatable).toEqual(mockConditionObjectDefinition.creatable);
+ });
+ });
+
+ describe('the view', () => {
+ let conditionWidgetView;
+ let testViewObject;
+
+ beforeEach(() => {
+ testViewObject = {
+ id: "test-object",
+ identifier: {
+ key: "test-object",
+ namespace: ''
+ },
+ type: "conditionWidget"
+ };
+
+ const applicableViews = openmct.objectViews.get(testViewObject, mockConditionObjectPath);
+ conditionWidgetView = applicableViews.find((viewProvider) => viewProvider.key === 'conditionWidget');
+ let view = conditionWidgetView.view(testViewObject, element);
+ view.show(child, true);
+
+ return Vue.nextTick();
+ });
+
+ it('provides a view', () => {
+ expect(conditionWidgetView).toBeDefined();
+ });
+ });
+
+ it("should have a view provider for condition widget objects", () => {
+ const applicableViews = openmct.objectViews.get(mockConditionObject[CONDITION_WIDGET_KEY], []);
+
+ const conditionWidgetViewProvider = applicableViews.find(
+ (viewProvider) => viewProvider.key === CONDITION_WIDGET_KEY
+ );
+
+ expect(applicableViews.length).toEqual(1);
+ expect(conditionWidgetViewProvider).toBeDefined();
+ });
+
+ it("should render a view with a URL and label", async () => {
+ const urlParent = document.createElement('div');
+ const urlChild = document.createElement('div');
+ urlParent.appendChild(urlChild);
+
+ const applicableViews = openmct.objectViews.get(mockConditionObject[CONDITION_WIDGET_KEY], []);
+
+ const conditionWidgetViewProvider = applicableViews.find(
+ (viewProvider) => viewProvider.key === CONDITION_WIDGET_KEY
+ );
+
+ const conditionWidgetView = conditionWidgetViewProvider.view(mockConditionObject[CONDITION_WIDGET_KEY], [mockConditionObject[CONDITION_WIDGET_KEY]]);
+ conditionWidgetView.show(urlChild);
+
+ await Vue.nextTick();
+
+ const domainUrl = mockConditionObject[CONDITION_WIDGET_KEY].url;
+ expect(urlParent.innerHTML).toContain(`<a href="${domainUrl}"`);
+
+ const conditionWidgetRender = urlParent.querySelector('.c-condition-widget');
+ expect(conditionWidgetRender).toBeDefined();
+ expect(conditionWidgetRender.innerHTML).toContain('<div class="c-condition-widget__label">');
+
+ const conditionWidgetLabel = conditionWidgetRender.querySelector('.c-condition-widget__label');
+ expect(conditionWidgetLabel).toBeDefined();
+ const domainLabel = mockConditionObject[CONDITION_WIDGET_KEY].label;
+ expect(conditionWidgetLabel.textContent).toContain(domainLabel);
+ });
+});
diff --git a/src/plugins/defaultRootName/plugin.js b/src/plugins/defaultRootName/plugin.js
index a8edf2eeb..5dfed337f 100644
--- a/src/plugins/defaultRootName/plugin.js
+++ b/src/plugins/defaultRootName/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2018, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/defaultRootName/pluginSpec.js b/src/plugins/defaultRootName/pluginSpec.js
index 2fb7e4f64..84a7a2678 100644
--- a/src/plugins/defaultRootName/pluginSpec.js
+++ b/src/plugins/defaultRootName/pluginSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/displayLayout/AlphanumericFormatViewProvider.js b/src/plugins/displayLayout/AlphanumericFormatViewProvider.js
index b96f272c3..c4b43d2f5 100644
--- a/src/plugins/displayLayout/AlphanumericFormatViewProvider.js
+++ b/src/plugins/displayLayout/AlphanumericFormatViewProvider.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/displayLayout/DisplayLayoutToolbar.js b/src/plugins/displayLayout/DisplayLayoutToolbar.js
index fdf69f600..5bcf3ee45 100644
--- a/src/plugins/displayLayout/DisplayLayoutToolbar.js
+++ b/src/plugins/displayLayout/DisplayLayoutToolbar.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -211,13 +211,15 @@ define(['lodash'], function (_) {
options: [
{
value: false,
- icon: 'icon-frame-show',
- title: "Frame visible"
+ icon: 'icon-frame-hide',
+ title: "Frame visible",
+ label: 'Hide frame'
},
{
value: true,
- icon: 'icon-frame-hide',
- title: "Frame hidden"
+ icon: 'icon-frame-show',
+ title: "Frame hidden",
+ label: 'Show frame'
}
]
};
@@ -401,6 +403,7 @@ define(['lodash'], function (_) {
},
icon: "icon-pencil",
title: "Edit text properties",
+ label: "Edit text",
dialog: DIALOG_FORM.text
};
}
@@ -514,12 +517,14 @@ define(['lodash'], function (_) {
{
value: true,
icon: 'icon-eye-open',
- title: "Show units"
+ title: "Show units",
+ label: "Show units"
},
{
value: false,
icon: 'icon-eye-disabled',
- title: "Hide units"
+ title: "Hide units",
+ label: "Hide units"
}
]
};
@@ -562,6 +567,7 @@ define(['lodash'], function (_) {
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);
@@ -662,9 +668,9 @@ define(['lodash'], function (_) {
'display-mode': [],
'telemetry-value': [],
'style': [],
+ 'unit-toggle': [],
'position': [],
'duplicate': [],
- 'unit-toggle': [],
'remove': [],
'toggle-grid': []
};
@@ -689,6 +695,7 @@ define(['lodash'], function (_) {
if (toolbar.position.length === 0) {
toolbar.position = [
getStackOrder(selectedParent, selectionPath),
+ getSeparator(),
getXInput(selectedParent, selectedObjects),
getYInput(selectedParent, selectedObjects),
getHeightInput(selectedParent, selectedObjects),
@@ -712,9 +719,17 @@ define(['lodash'], function (_) {
toolbar['telemetry-value'] = [getTelemetryValueMenu(selectionPath, 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),
@@ -729,17 +744,11 @@ define(['lodash'], function (_) {
if (toolbar.viewSwitcher.length === 0) {
toolbar.viewSwitcher = [getViewSwitcherMenu(selectedParent, selectionPath, selectedObjects)];
}
-
- if (toolbar['unit-toggle'].length === 0) {
- let toggleUnitsButton = getToggleUnitsButton(selectedParent, selectedObjects);
- if (toggleUnitsButton) {
- toolbar['unit-toggle'] = [toggleUnitsButton];
- }
- }
} 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),
@@ -758,6 +767,7 @@ define(['lodash'], function (_) {
if (toolbar.position.length === 0) {
toolbar.position = [
getStackOrder(selectedParent, selectionPath),
+ getSeparator(),
getXInput(selectedParent, selectedObjects),
getYInput(selectedParent, selectedObjects),
getHeightInput(selectedParent, selectedObjects),
@@ -772,6 +782,7 @@ define(['lodash'], function (_) {
if (toolbar.position.length === 0) {
toolbar.position = [
getStackOrder(selectedParent, selectionPath),
+ getSeparator(),
getXInput(selectedParent, selectedObjects),
getYInput(selectedParent, selectedObjects),
getHeightInput(selectedParent, selectedObjects),
@@ -786,6 +797,7 @@ define(['lodash'], function (_) {
if (toolbar.position.length === 0) {
toolbar.position = [
getStackOrder(selectedParent, selectionPath),
+ getSeparator(),
getXInput(selectedParent, selectedObjects),
getYInput(selectedParent, selectedObjects),
getX2Input(selectedParent, selectedObjects),
diff --git a/src/plugins/displayLayout/DisplayLayoutType.js b/src/plugins/displayLayout/DisplayLayoutType.js
index 7a3ce8770..b79d019bc 100644
--- a/src/plugins/displayLayout/DisplayLayoutType.js
+++ b/src/plugins/displayLayout/DisplayLayoutType.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/displayLayout/LayoutDrag.js b/src/plugins/displayLayout/LayoutDrag.js
index ab8a9640d..21f9fab4d 100644
--- a/src/plugins/displayLayout/LayoutDrag.js
+++ b/src/plugins/displayLayout/LayoutDrag.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/displayLayout/components/AlphanumericFormat.vue b/src/plugins/displayLayout/components/AlphanumericFormat.vue
index f6b592ce9..0751caccb 100644
--- a/src/plugins/displayLayout/components/AlphanumericFormat.vue
+++ b/src/plugins/displayLayout/components/AlphanumericFormat.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/displayLayout/components/BoxView.vue b/src/plugins/displayLayout/components/BoxView.vue
index 577ce8b09..7cc46a65d 100644
--- a/src/plugins/displayLayout/components/BoxView.vue
+++ b/src/plugins/displayLayout/components/BoxView.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/displayLayout/components/DisplayLayout.vue b/src/plugins/displayLayout/components/DisplayLayout.vue
index a7b21e7f0..bc29b615a 100644
--- a/src/plugins/displayLayout/components/DisplayLayout.vue
+++ b/src/plugins/displayLayout/components/DisplayLayout.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -73,7 +73,7 @@
</template>
<script>
-import uuid from 'uuid';
+import { v4 as uuid } from 'uuid';
import SubobjectView from './SubobjectView.vue';
import TelemetryView from './TelemetryView.vue';
import BoxView from './BoxView.vue';
@@ -166,7 +166,7 @@ export default {
},
computed: {
gridSize() {
- return this.domainObject.configuration.layoutGrid;
+ return this.domainObject.configuration.layoutGrid.map(Number);
},
layoutItems() {
return this.domainObject.configuration.items;
@@ -209,18 +209,7 @@ export default {
},
layoutItems: {
handler(value) {
- let wMax = this.$el.clientWidth / this.gridSize[0];
- let hMax = this.$el.clientHeight / this.gridSize[1];
- value.forEach(item => {
- if (item.x + item.width > wMax) {
- wMax = item.x + item.width + 2;
- }
-
- if (item.y + item.height > hMax) {
- hMax = item.y + item.height + 2;
- }
- });
- this.gridDimensions = [wMax * this.gridSize[0], hMax * this.gridSize[1]];
+ this.updateGrid();
},
deep: true
}
@@ -233,6 +222,8 @@ export default {
this.composition.on('remove', this.removeChild);
this.composition.load();
this.gridDimensions = [this.$el.offsetWidth, this.$el.scrollHeight];
+
+ this.watchDisplayResize();
},
destroyed: function () {
this.openmct.selection.off('change', this.setSelection);
@@ -240,6 +231,25 @@ export default {
this.composition.off('remove', this.removeChild);
},
methods: {
+ updateGrid() {
+ let wMax = this.$el.clientWidth / this.gridSize[0];
+ let hMax = this.$el.clientHeight / this.gridSize[1];
+ this.layoutItems.forEach(item => {
+ if (item.x + item.width > wMax) {
+ wMax = item.x + item.width + 2;
+ }
+
+ if (item.y + item.height > hMax) {
+ hMax = item.y + item.height + 2;
+ }
+ });
+ this.gridDimensions = [wMax * this.gridSize[0], hMax * this.gridSize[1]];
+ },
+ watchDisplayResize() {
+ const resizeObserver = new ResizeObserver(() => this.updateGrid());
+
+ resizeObserver.observe(this.$el);
+ },
addElement(itemType, element) {
this.addItem(itemType + '-view', element);
},
@@ -404,8 +414,12 @@ export default {
}
},
containsObject(identifier) {
- return _.get(this.domainObject, 'composition')
- .some(childId => this.openmct.objects.areIdsEqual(childId, identifier));
+ if ('composition' in this.domainObject) {
+ return this.domainObject.composition
+ .some(childId => this.openmct.objects.areIdsEqual(childId, identifier));
+ }
+
+ return false;
},
handleDragOver($event) {
if (this.domainObject.locked) {
@@ -494,7 +508,7 @@ export default {
}
},
removeFromComposition(keyString) {
- let composition = _.get(this.domainObject, 'composition');
+ let composition = this.domainObject.composition ? this.domainObject.composition : [];
composition = composition.filter(identifier => {
return this.openmct.objects.makeKeyString(identifier) !== keyString;
});
diff --git a/src/plugins/displayLayout/components/EditMarquee.vue b/src/plugins/displayLayout/components/EditMarquee.vue
index d20a10ff4..3c2be6335 100644
--- a/src/plugins/displayLayout/components/EditMarquee.vue
+++ b/src/plugins/displayLayout/components/EditMarquee.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/displayLayout/components/EllipseView.vue b/src/plugins/displayLayout/components/EllipseView.vue
index 5e7f404b9..934b79704 100644
--- a/src/plugins/displayLayout/components/EllipseView.vue
+++ b/src/plugins/displayLayout/components/EllipseView.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
-* Open MCT, Copyright (c) 2014-2021, United States Government
+* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/displayLayout/components/ImageView.vue b/src/plugins/displayLayout/components/ImageView.vue
index cf6c4efe4..bc0c970f4 100644
--- a/src/plugins/displayLayout/components/ImageView.vue
+++ b/src/plugins/displayLayout/components/ImageView.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/displayLayout/components/LayoutFrame.vue b/src/plugins/displayLayout/components/LayoutFrame.vue
index 5d01cd907..c81fb80f7 100644
--- a/src/plugins/displayLayout/components/LayoutFrame.vue
+++ b/src/plugins/displayLayout/components/LayoutFrame.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -25,8 +25,7 @@
class="l-layout__frame c-frame"
:class="{
'no-frame': !item.hasFrame,
- 'u-inspectable': inspectable,
- 'is-in-small-container': size.width < 600 || size.height < 600
+ 'u-inspectable': inspectable
}"
:style="style"
>
diff --git a/src/plugins/displayLayout/components/LineView.vue b/src/plugins/displayLayout/components/LineView.vue
index 1f8ccba2c..7b856a2f4 100644
--- a/src/plugins/displayLayout/components/LineView.vue
+++ b/src/plugins/displayLayout/components/LineView.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/displayLayout/components/SubobjectView.vue b/src/plugins/displayLayout/components/SubobjectView.vue
index 3644dbd76..8e58f89dd 100644
--- a/src/plugins/displayLayout/components/SubobjectView.vue
+++ b/src/plugins/displayLayout/components/SubobjectView.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/displayLayout/components/TelemetryView.vue b/src/plugins/displayLayout/components/TelemetryView.vue
index 3e2626a06..3c5e5eba2 100644
--- a/src/plugins/displayLayout/components/TelemetryView.vue
+++ b/src/plugins/displayLayout/components/TelemetryView.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -37,8 +37,9 @@
:data-font="item.font"
@contextmenu.prevent="showContextMenu"
>
- <div class="is-status__indicator"
- :title="`This item is ${status}`"
+ <div
+ class="is-status__indicator"
+ :title="`This item is ${status}`"
></div>
<div
v-if="showLabel"
@@ -90,7 +91,7 @@ export default {
width: DEFAULT_TELEMETRY_DIMENSIONS[0],
height: DEFAULT_TELEMETRY_DIMENSIONS[1],
displayMode: 'all',
- value: metadata.getDefaultDisplayValue(),
+ value: metadata.getDefaultDisplayValue()?.key,
stroke: "",
fill: "",
color: "",
@@ -151,7 +152,7 @@ export default {
},
unit() {
let value = this.item.value;
- let unit = this.metadata.value(value).unit;
+ let unit = this.metadata ? this.metadata.value(value).unit : '';
return unit;
},
@@ -221,20 +222,20 @@ export default {
.then(this.setObject);
}
- this.openmct.time.on("bounds", this.refreshData);
-
this.status = this.openmct.status.get(this.item.identifier);
this.removeStatusListener = this.openmct.status.observe(this.item.identifier, this.setStatus);
},
beforeDestroy() {
- this.removeSubscription();
this.removeStatusListener();
if (this.removeSelectable) {
this.removeSelectable();
}
- this.openmct.time.off("bounds", this.refreshData);
+ this.telemetryCollection.off('add', this.setLatestValues);
+ this.telemetryCollection.off('clear', this.refreshData);
+
+ this.telemetryCollection.destroy();
if (this.mutablePromise) {
this.mutablePromise.then(() => {
@@ -252,34 +253,9 @@ export default {
return `At ${timeFormatter.format(this.datum)} ${this.domainObject.name} had a value of ${this.telemetryValue}${unit}`;
},
- requestHistoricalData() {
- let bounds = this.openmct.time.bounds();
- let options = {
- start: bounds.start,
- end: bounds.end,
- size: 1,
- strategy: 'latest'
- };
- this.openmct.telemetry.request(this.domainObject, options)
- .then(data => {
- if (data.length > 0) {
- this.latestDatum = data[data.length - 1];
- this.updateView();
- }
- });
- },
- subscribeToObject() {
- this.subscription = this.openmct.telemetry.subscribe(this.domainObject, function (datum) {
- const key = this.openmct.time.timeSystem().key;
- const datumTimeStamp = datum[key];
- if (this.openmct.time.clock() !== undefined
- || (datumTimeStamp
- && (this.openmct.time.bounds().end >= datumTimeStamp))
- ) {
- this.latestDatum = datum;
- this.updateView();
- }
- }.bind(this));
+ setLatestValues(data) {
+ this.latestDatum = data[data.length - 1];
+ this.updateView();
},
updateView() {
if (!this.updatingView) {
@@ -290,17 +266,10 @@ export default {
});
}
},
- removeSubscription() {
- if (this.subscription) {
- this.subscription();
- this.subscription = undefined;
- }
- },
refreshData(bounds, isTick) {
if (!isTick) {
this.latestDatum = undefined;
this.updateView();
- this.requestHistoricalData(this.domainObject);
}
},
setObject(domainObject) {
@@ -311,11 +280,16 @@ export default {
this.limitEvaluator = this.openmct.telemetry.limitEvaluator(this.domainObject);
this.formats = this.openmct.telemetry.getFormatMap(this.metadata);
- const valueMetadata = this.metadata.value(this.item.value);
+ const valueMetadata = this.metadata ? this.metadata.value(this.item.value) : {};
this.customStringformatter = this.openmct.telemetry.customStringFormatter(valueMetadata, this.item.format);
- this.requestHistoricalData();
- this.subscribeToObject();
+ this.telemetryCollection = this.openmct.telemetry.requestCollection(this.domainObject, {
+ size: 1,
+ strategy: 'latest'
+ });
+ this.telemetryCollection.on('add', this.setLatestValues);
+ this.telemetryCollection.on('clear', this.refreshData);
+ this.telemetryCollection.load();
this.currentObjectPath = this.objectPath.slice();
this.currentObjectPath.unshift(this.domainObject);
diff --git a/src/plugins/displayLayout/components/TextView.vue b/src/plugins/displayLayout/components/TextView.vue
index b3e683c20..d227059ef 100644
--- a/src/plugins/displayLayout/components/TextView.vue
+++ b/src/plugins/displayLayout/components/TextView.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/displayLayout/components/layout-frame.scss b/src/plugins/displayLayout/components/layout-frame.scss
index 63f2299ec..656a535c8 100644
--- a/src/plugins/displayLayout/components/layout-frame.scss
+++ b/src/plugins/displayLayout/components/layout-frame.scss
@@ -9,10 +9,6 @@
> *:first-child {
flex: 1 1 auto;
}
-
- &.is-in-small-container {
- //background: rgba(blue, 0.1);
- }
}
.c-frame__move-bar {
@@ -32,7 +28,6 @@
&[s-selected] {
// All frames selected while editing
- border: $editFrameSelectedBorder;
box-shadow: $editFrameSelectedShdw;
.c-frame__move-bar {
diff --git a/src/plugins/displayLayout/components/telemetry-view.scss b/src/plugins/displayLayout/components/telemetry-view.scss
index 0dbfc75ac..8b73d118a 100644
--- a/src/plugins/displayLayout/components/telemetry-view.scss
+++ b/src/plugins/displayLayout/components/telemetry-view.scss
@@ -17,14 +17,14 @@
}
}
- > * + * {
- margin-left: $interiorMargin;
- }
-
&__value {
@include isLimit();
}
+ &__label {
+ margin-right: $interiorMargin;
+ }
+
.c-frame & {
@include abs();
border: 1px solid transparent;
diff --git a/src/plugins/displayLayout/mixins/objectStyles-mixin.js b/src/plugins/displayLayout/mixins/objectStyles-mixin.js
index 76323f81b..e2f1bfdbf 100644
--- a/src/plugins/displayLayout/mixins/objectStyles-mixin.js
+++ b/src/plugins/displayLayout/mixins/objectStyles-mixin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -38,10 +38,14 @@ export default {
this.objectStyle = this.getObjectStyleForItem(this.parentDomainObject.configuration.objectStyles);
this.initObjectStyles();
},
- destroyed() {
+ beforeDestroy() {
if (this.stopListeningObjectStyles) {
this.stopListeningObjectStyles();
}
+
+ if (this.styleRuleManager) {
+ this.styleRuleManager.destroy();
+ }
},
methods: {
getObjectStyleForItem(objectStyle) {
diff --git a/src/plugins/displayLayout/plugin.js b/src/plugins/displayLayout/plugin.js
index 0c148ed80..1d949e32c 100644
--- a/src/plugins/displayLayout/plugin.js
+++ b/src/plugins/displayLayout/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/displayLayout/pluginSpec.js b/src/plugins/displayLayout/pluginSpec.js
index 43ef4932f..8e6a56e5f 100644
--- a/src/plugins/displayLayout/pluginSpec.js
+++ b/src/plugins/displayLayout/pluginSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -41,7 +41,7 @@ describe('the plugin', function () {
element.appendChild(child);
openmct.on('start', done);
- openmct.startHeadless();
+ openmct.start(child);
});
afterEach(() => {
@@ -88,6 +88,35 @@ describe('the plugin', function () {
expect(displayLayoutViewProvider).toBeDefined();
});
+ it('renders a display layout view without errors', () => {
+ const testViewObject = {
+ identifier: {
+ namespace: 'test-namespace',
+ key: 'test-key'
+ },
+ type: 'layout',
+ configuration: {
+ items: [],
+ layoutGrid: [10, 10]
+ },
+ composition: []
+ };
+
+ const applicableViews = openmct.objectViews.get(testViewObject, []);
+ let displayLayoutViewProvider = applicableViews.find((viewProvider) => viewProvider.key === 'layout.view');
+ let view = displayLayoutViewProvider.view(testViewObject);
+ let error;
+
+ try {
+ view.show(child, false);
+ } catch (e) {
+ error = e;
+ }
+
+ expect(error).toBeUndefined();
+
+ });
+
describe('the alpha numeric format view', () => {
let displayLayoutItem;
let telemetryItem;
@@ -351,7 +380,7 @@ describe('the plugin', function () {
it('provides controls including separators', () => {
const displayLayoutToolbar = openmct.toolbars.get(selection);
- expect(displayLayoutToolbar.length).toBe(7);
+ expect(displayLayoutToolbar.length).toBe(8);
});
});
});
diff --git a/src/plugins/duplicate/DuplicateAction.js b/src/plugins/duplicate/DuplicateAction.js
index cc19511f2..7181c990c 100644
--- a/src/plugins/duplicate/DuplicateAction.js
+++ b/src/plugins/duplicate/DuplicateAction.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -97,13 +97,16 @@ export default class DuplicateAction {
validate(currentParent) {
return (data) => {
- const parentCandidatePath = data.value;
- const parentCandidate = parentCandidatePath[0];
+ const parentCandidate = data.value[0];
let currentParentKeystring = this.openmct.objects.makeKeyString(currentParent.identifier);
let parentCandidateKeystring = this.openmct.objects.makeKeyString(parentCandidate.identifier);
let objectKeystring = this.openmct.objects.makeKeyString(this.object.identifier);
+ if (!this.openmct.objects.isPersistable(parentCandidate.identifier)) {
+ return false;
+ }
+
if (!parentCandidateKeystring || !currentParentKeystring) {
return false;
}
@@ -122,13 +125,14 @@ export default class DuplicateAction {
}
appliesTo(objectPath) {
- let parent = objectPath[1];
- let parentType = parent && this.openmct.types.get(parent.type);
- let child = objectPath[0];
- let childType = child && this.openmct.types.get(child.type);
- let locked = child.locked ? child.locked : parent && parent.locked;
-
- if (locked) {
+ const parent = objectPath[1];
+ const parentType = parent && this.openmct.types.get(parent.type);
+ const child = objectPath[0];
+ const childType = child && this.openmct.types.get(child.type);
+ const locked = child.locked ? child.locked : parent && parent.locked;
+ const isPersistable = this.openmct.objects.isPersistable(child.identifier);
+
+ if (locked || !isPersistable) {
return false;
}
diff --git a/src/plugins/duplicate/DuplicateTask.js b/src/plugins/duplicate/DuplicateTask.js
index 9374a880e..4c9ca0a10 100644
--- a/src/plugins/duplicate/DuplicateTask.js
+++ b/src/plugins/duplicate/DuplicateTask.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -20,7 +20,7 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
-import uuid from 'uuid';
+import { v4 as uuid } from 'uuid';
/**
* This class encapsulates the process of duplicating/copying a domain object
diff --git a/src/plugins/duplicate/plugin.js b/src/plugins/duplicate/plugin.js
index 0f07cc579..7b1f17504 100644
--- a/src/plugins/duplicate/plugin.js
+++ b/src/plugins/duplicate/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2019, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/duplicate/pluginSpec.js b/src/plugins/duplicate/pluginSpec.js
index 8195d92ea..00daf111f 100644
--- a/src/plugins/duplicate/pluginSpec.js
+++ b/src/plugins/duplicate/pluginSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/exportAsJSONAction/ExportAsJSONAction.js b/src/plugins/exportAsJSONAction/ExportAsJSONAction.js
index 5ab4aadf5..506c34745 100644
--- a/src/plugins/exportAsJSONAction/ExportAsJSONAction.js
+++ b/src/plugins/exportAsJSONAction/ExportAsJSONAction.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -22,7 +22,7 @@
import JSONExporter from '/src/exporters/JSONExporter.js';
import _ from 'lodash';
-import uuid from "uuid";
+import { v4 as uuid } from 'uuid';
export default class ExportAsJSONAction {
constructor(openmct) {
@@ -52,7 +52,7 @@ export default class ExportAsJSONAction {
appliesTo(objectPath) {
let domainObject = objectPath[0];
- return this._isCreatable(domainObject);
+ return this._isCreatableAndPersistable(domainObject);
}
/**
*
@@ -80,10 +80,11 @@ export default class ExportAsJSONAction {
* @param {object} domainObject
* @returns {boolean}
*/
- _isCreatable(domainObject) {
+ _isCreatableAndPersistable(domainObject) {
const type = this.openmct.types.get(domainObject.type);
+ const isPersistable = this.openmct.objects.isPersistable(domainObject.identifier);
- return type && type.definition.creatable;
+ return type && type.definition.creatable && isPersistable;
}
/**
* @private
@@ -127,6 +128,30 @@ export default class ExportAsJSONAction {
return copyOfChild;
}
+
+ /**
+ * @private
+ * @param {object} child
+ * @param {object} parent
+ * @returns {object}
+ */
+ _rewriteLinkForReference(child, parent) {
+ const childId = this._getId(child);
+ this.externalIdentifiers.push(childId);
+ const copyOfChild = JSON.parse(JSON.stringify(child));
+
+ copyOfChild.identifier.key = uuid();
+ const newIdString = this._getId(copyOfChild);
+ const parentId = this._getId(parent);
+
+ this.idMap[childId] = newIdString;
+ copyOfChild.location = null;
+ parent.configuration.objectStyles.conditionSetIdentifier = copyOfChild.identifier;
+ this.tree[newIdString] = copyOfChild;
+ this.tree[parentId].configuration.objectStyles.conditionSetIdentifier = copyOfChild.identifier;
+
+ return copyOfChild;
+ }
/**
* @private
*/
@@ -158,23 +183,27 @@ export default class ExportAsJSONAction {
"rootId": this._getId(this.root)
};
}
+
/**
* @private
* @param {object} parent
*/
_write(parent) {
this.calls++;
+ //conditional object styles are not saved on the composition, so we need to check for them
+ let childObjectReferenceId = parent.configuration?.objectStyles?.conditionSetIdentifier;
+
const composition = this.openmct.composition.get(parent);
if (composition !== undefined) {
composition.load()
.then((children) => {
children.forEach((child, index) => {
- // Only export if object is creatable
- if (this._isCreatable(child)) {
- // Prevents infinite export of self-contained objs
+ // Only export if object is creatable
+ if (this._isCreatableAndPersistable(child)) {
+ // Prevents infinite export of self-contained objs
if (!Object.prototype.hasOwnProperty.call(this.tree, this._getId(child))) {
- // If object is a link to something absent from
- // tree, generate new id and treat as new object
+ // If object is a link to something absent from
+ // tree, generate new id and treat as new object
if (this._isLinkedObject(child, parent)) {
child = this._rewriteLink(child, parent);
} else {
@@ -185,18 +214,41 @@ export default class ExportAsJSONAction {
}
}
});
- this.calls--;
- if (this.calls === 0) {
- this._rewriteReferences();
- this._saveAs(this._wrapTree());
+ this._decrementCallsAndSave();
+ });
+ } else if (!childObjectReferenceId) {
+ this._decrementCallsAndSave();
+ }
+
+ if (childObjectReferenceId) {
+ this.openmct.objects.get(childObjectReferenceId)
+ .then((child) => {
+ // Only export if object is creatable
+ if (this._isCreatableAndPersistable(child)) {
+ // Prevents infinite export of self-contained objs
+ if (!Object.prototype.hasOwnProperty.call(this.tree, this._getId(child))) {
+ // If object is a link to something absent from
+ // tree, generate new id and treat as new object
+ if (this._isLinkedObject(child, parent)) {
+ child = this._rewriteLinkForReference(child, parent);
+ } else {
+ this.tree[this._getId(child)] = child;
+ }
+
+ this._write(child);
+ }
}
+
+ this._decrementCallsAndSave();
});
- } else {
- this.calls--;
- if (this.calls === 0) {
- this._rewriteReferences();
- this._saveAs(this._wrapTree());
- }
+ }
+ }
+
+ _decrementCallsAndSave() {
+ this.calls--;
+ if (this.calls === 0) {
+ this._rewriteReferences();
+ this._saveAs(this._wrapTree());
}
}
}
diff --git a/src/plugins/exportAsJSONAction/ExportAsJSONActionSpec.js b/src/plugins/exportAsJSONAction/ExportAsJSONActionSpec.js
index 549943070..7ecb3b963 100644
--- a/src/plugins/exportAsJSONAction/ExportAsJSONActionSpec.js
+++ b/src/plugins/exportAsJSONAction/ExportAsJSONActionSpec.js
@@ -27,6 +27,10 @@ describe('Export as JSON plugin', () => {
it('ExportAsJSONAction applies to folder', () => {
domainObject = {
+ identifier: {
+ key: 'export-testing',
+ namespace: ''
+ },
composition: [],
location: 'mine',
modified: 1640115501237,
@@ -40,6 +44,10 @@ describe('Export as JSON plugin', () => {
it('ExportAsJSONAction applies to telemetry.plot.overlay', () => {
domainObject = {
+ identifier: {
+ key: 'export-testing',
+ namespace: ''
+ },
composition: [],
location: 'mine',
modified: 1640115501237,
@@ -53,6 +61,10 @@ describe('Export as JSON plugin', () => {
it('ExportAsJSONAction applies to telemetry.plot.stacked', () => {
domainObject = {
+ identifier: {
+ key: 'export-testing',
+ namespace: ''
+ },
composition: [],
location: 'mine',
modified: 1640115501237,
@@ -64,16 +76,24 @@ describe('Export as JSON plugin', () => {
expect(exportAsJSONAction.appliesTo([domainObject])).toEqual(true);
});
- it('ExportAsJSONAction applies does not applies to non-creatable objects', () => {
+ it('ExportAsJSONAction does not applie to non-persistable objects', () => {
domainObject = {
+ identifier: {
+ key: 'export-testing',
+ namespace: ''
+ },
composition: [],
location: 'mine',
modified: 1640115501237,
name: 'Non Editable Folder',
persisted: 1640115501237,
- type: 'noneditable.folder'
+ type: 'folder'
};
+ spyOn(openmct.objects, 'getProvider').and.callFake(() => {
+ return { get: () => domainObject };
+ });
+
expect(exportAsJSONAction.appliesTo([domainObject])).toEqual(false);
});
@@ -302,4 +322,57 @@ describe('Export as JSON plugin', () => {
exportAsJSONAction.invoke([parent]);
});
+
+ it('ExportAsJSONAction exports object references from tree', (done) => {
+ const parent = {
+ composition: [],
+ configuration: {
+ objectStyles: {
+ conditionSetIdentifier: {
+ key: 'child',
+ namespace: ''
+ }
+ }
+ },
+ identifier: {
+ key: 'parent',
+ namespace: ''
+ },
+ name: 'Parent',
+ type: 'folder',
+ modified: 1503598129176,
+ location: 'mine',
+ persisted: 1503598129176
+ };
+
+ const child = {
+ composition: [],
+ identifier: {
+ key: 'child',
+ namespace: ''
+ },
+ name: 'Child',
+ type: 'folder',
+ modified: 1503598132428,
+ location: null,
+ persisted: 1503598132428
+ };
+
+ spyOn(openmct.objects, 'get').and.callFake(object => {
+ return Promise.resolve(child);
+ });
+
+ spyOn(exportAsJSONAction, '_saveAs').and.callFake(completedTree => {
+ expect(Object.keys(completedTree).length).toBe(2);
+ const conditionSetId = Object.keys(completedTree.openmct)[1];
+ expect(Object.prototype.hasOwnProperty.call(completedTree, 'openmct')).toBeTruthy();
+ expect(Object.prototype.hasOwnProperty.call(completedTree, 'rootId')).toBeTruthy();
+ expect(Object.prototype.hasOwnProperty.call(completedTree.openmct, 'parent')).toBeTruthy();
+ expect(completedTree.openmct[conditionSetId].name).toBe('Child');
+
+ done();
+ });
+
+ exportAsJSONAction.invoke([parent]);
+ });
});
diff --git a/src/plugins/exportAsJSONAction/plugin.js b/src/plugins/exportAsJSONAction/plugin.js
index c3c107add..72fbd6046 100644
--- a/src/plugins/exportAsJSONAction/plugin.js
+++ b/src/plugins/exportAsJSONAction/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/faultManagement/FaultManagementInspector.vue b/src/plugins/faultManagement/FaultManagementInspector.vue
new file mode 100644
index 000000000..7059a4ff7
--- /dev/null
+++ b/src/plugins/faultManagement/FaultManagementInspector.vue
@@ -0,0 +1,129 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+<template>
+<div
+ v-if="isShowDetails"
+ class="c-inspector__properties c-inspect-properties"
+>
+ <div class="c-inspect-properties__header">Fault Details</div>
+ <ul
+ class="c-inspect-properties__section"
+ >
+ <DetailText :detail="sourceDetails" />
+ <DetailText :detail="occuredDetails" />
+ <DetailText :detail="criticalityDetails" />
+ <DetailText :detail="descriptionDetails" />
+ </ul>
+
+ <div class="c-inspect-properties__header">Telemetry</div>
+ <ul
+ class="c-inspect-properties__section"
+ >
+ <DetailText :detail="systemDetails" />
+ <DetailText :detail="tripValueDetails" />
+ <DetailText :detail="currentValueDetails" />
+ </ul>
+</div>
+</template>
+
+<script>
+import DetailText from '@/ui/inspector/details/DetailText.vue';
+
+export default {
+ name: 'FaultManagementInspector',
+ components: {
+ DetailText
+ },
+ inject: ['openmct'],
+ data() {
+ return {
+ isShowDetails: false
+ };
+ },
+ computed: {
+ criticalityDetails() {
+ return {
+ name: 'Criticality',
+ value: this.selectedFault?.severity
+ };
+ },
+ currentValueDetails() {
+ return {
+ name: 'Live value',
+ value: this.selectedFault?.currentValueInfo?.value
+ };
+ },
+ descriptionDetails() {
+ return {
+ name: 'Description',
+ value: this.selectedFault?.shortDescription
+ };
+ },
+ occuredDetails() {
+ return {
+ name: 'Occured',
+ value: this.selectedFault?.triggerTime
+ };
+ },
+ sourceDetails() {
+ return {
+ name: 'Source',
+ value: this.selectedFault?.name
+ };
+ },
+ systemDetails() {
+ return {
+ name: 'System',
+ value: this.selectedFault?.namespace
+ };
+ },
+ tripValueDetails() {
+ return {
+ name: 'Trip Value',
+ value: this.selectedFault?.triggerValueInfo?.value
+ };
+ }
+ },
+ mounted() {
+ this.updateSelectedFaults();
+ },
+ methods: {
+ updateSelectedFaults() {
+ const selection = this.openmct.selection.get();
+ this.isShowDetails = false;
+
+ if (selection.length === 0 || selection[0].length < 2) {
+ return;
+ }
+
+ const selectedFaults = selection[0][1].context.selectedFaults;
+ if (selectedFaults.length !== 1) {
+ return;
+ }
+
+ this.isShowDetails = true;
+ this.selectedFault = selectedFaults[0];
+ }
+ }
+};
+</script>
diff --git a/src/plugins/faultManagement/FaultManagementInspectorViewProvider.js b/src/plugins/faultManagement/FaultManagementInspectorViewProvider.js
new file mode 100644
index 000000000..b4500496f
--- /dev/null
+++ b/src/plugins/faultManagement/FaultManagementInspectorViewProvider.js
@@ -0,0 +1,71 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+import FaultManagementInspector from './FaultManagementInspector.vue';
+
+import Vue from 'vue';
+
+import { FAULT_MANAGEMENT_INSPECTOR, FAULT_MANAGEMENT_TYPE } from './constants';
+
+export default function FaultManagementInspectorViewProvider(openmct) {
+ return {
+ openmct: openmct,
+ key: FAULT_MANAGEMENT_INSPECTOR,
+ name: 'FAULT_MANAGEMENT_TYPE',
+ canView: (selection) => {
+ if (selection.length !== 1 || selection[0].length === 0) {
+ return false;
+ }
+
+ let object = selection[0][0].context.item;
+
+ return object && object.type === FAULT_MANAGEMENT_TYPE;
+ },
+ view: (selection) => {
+ let component;
+
+ return {
+ show: function (element) {
+ component = new Vue({
+ el: element,
+ components: {
+ FaultManagementInspector
+ },
+ provide: {
+ openmct
+ },
+ template: '<FaultManagementInspector></FaultManagementInspector>'
+ });
+ },
+ destroy: function () {
+ if (component) {
+ component.$destroy();
+ component = undefined;
+ }
+ }
+ };
+ },
+ priority: () => {
+ return 1;
+ }
+ };
+}
diff --git a/src/plugins/faultManagement/FaultManagementListHeader.vue b/src/plugins/faultManagement/FaultManagementListHeader.vue
new file mode 100644
index 000000000..11c3bd628
--- /dev/null
+++ b/src/plugins/faultManagement/FaultManagementListHeader.vue
@@ -0,0 +1,105 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+<template>
+<div class="c-fault-mgmt-item-header c-fault-mgmt__list-header c-fault-mgmt__list">
+ <div class="c-fault-mgmt-item-header c-fault-mgmt__checkbox">
+ <input
+ type="checkbox"
+ :checked="isSelectAll"
+ @input="selectAll"
+ >
+ </div>
+ <div class="c-fault-mgmt-item-header c-fault-mgmt__list-header-results c-fault-mgmt__list-severity">
+ {{ totalFaultsCount }} Results
+ </div>
+ <div class="c-fault-mgmt__list-header-content">
+ <div class="c-fault-mgmt__list-content-right">
+ <div class="c-fault-mgmt-item-header c-fault-mgmt__list-header-tripVal">Trip Value</div>
+ <div class="c-fault-mgmt-item-header c-fault-mgmt__list-header-liveVal">Live Value</div>
+ <div class="c-fault-mgmt-item-header c-fault-mgmt__list-header-trigTime">Trigger Time</div>
+ </div>
+ </div>
+ <div class=" c-fault-mgmt-item-header c-fault-mgmt__list-header-action-wrapper">
+ <div class="c-fault-mgmt__list-header-sortButton c-fault-mgmt__list-action-button">
+ <SelectField
+ class="c-fault-mgmt-viewButton"
+ title="Sort By"
+ :model="model"
+ @onChange="onChange"
+ />
+ </div>
+ </div>
+</div>
+</template>
+
+<script>
+import SelectField from '@/api/forms/components/controls/SelectField.vue';
+
+import { SORT_ITEMS } from './constants';
+
+export default {
+ components: {
+ SelectField
+ },
+ inject: ['openmct', 'domainObject'],
+ props: {
+ selectedFaults: {
+ type: Array,
+ default() {
+ return [];
+ }
+ },
+ totalFaultsCount: {
+ type: Number,
+ default() {
+ return 0;
+ }
+ }
+ },
+ data() {
+ return {
+ model: {}
+ };
+ },
+ computed: {
+ isSelectAll() {
+ return this.totalFaultsCount > 0 && this.selectedFaults.length === this.totalFaultsCount;
+ }
+ },
+ beforeMount() {
+ const options = Object.values(SORT_ITEMS);
+ this.model = {
+ options,
+ value: options[0].value
+ };
+ },
+ methods: {
+ onChange(data) {
+ this.$emit('sortChanged', data);
+ },
+ selectAll(e) {
+ this.$emit('selectAll', e.target.checked);
+ }
+ }
+};
+</script>
diff --git a/src/plugins/faultManagement/FaultManagementListItem.vue b/src/plugins/faultManagement/FaultManagementListItem.vue
new file mode 100644
index 000000000..2a2f6bf85
--- /dev/null
+++ b/src/plugins/faultManagement/FaultManagementListItem.vue
@@ -0,0 +1,223 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+<template>
+<div
+ class="c-fault-mgmt__list data-selectable"
+ :class="classesFromState"
+>
+ <div class="c-fault-mgmt-item c-fault-mgmt__list-checkbox">
+ <input
+ type="checkbox"
+ :checked="isSelected"
+ @input="toggleSelected"
+ >
+ </div>
+ <div class="c-fault-mgmt-item">
+ <div
+ class="c-fault-mgmt__list-severity"
+ :title="fault.severity"
+ :class="[
+ 'is-severity-' + severity
+ ]"
+ >
+ </div>
+ </div>
+ <div class="c-fault-mgmt-item c-fault-mgmt__list-content">
+ <div class="c-fault-mgmt-item c-fault-mgmt__list-pathname">
+ <div class="c-fault-mgmt__list-path">{{ fault.namespace }}</div>
+ <div class="c-fault-mgmt__list-faultname">{{ fault.name }}</div>
+ </div>
+ <div class="c-fault-mgmt__list-content-right">
+ <div class="c-fault-mgmt-item c-fault-mgmt__list-trigVal">
+ <div
+ class="c-fault-mgmt-item__value"
+ :class="tripValueClassname"
+ title="Trip Value"
+ >{{ fault.triggerValueInfo.value }}</div>
+ </div>
+ <div class="c-fault-mgmt-item c-fault-mgmt__list-curVal">
+ <div
+ class="c-fault-mgmt-item__value"
+ :class="liveValueClassname"
+ title="Live Value"
+ >{{ fault.currentValueInfo.value }}</div>
+ </div>
+ <div class="c-fault-mgmt-item c-fault-mgmt__list-trigTime">
+ <div
+ class="c-fault-mgmt-item__value"
+ title="Last Trigger Time"
+ >{{ fault.triggerTime }}
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="c-fault-mgmt-item c-fault-mgmt__list-action-wrapper">
+ <button
+ class="c-fault-mgmt__list-action-button l-browse-bar__actions c-icon-button icon-3-dots"
+ title="Disposition Actions"
+ @click="showActionMenu"
+ ></button>
+ </div>
+</div>
+</template>
+<script>
+
+const RANGE_CONDITION_CLASS = {
+ 'LOW': 'is-limit--lwr',
+ 'HIGH': 'is-limit--upr'
+};
+
+const SEVERITY_CLASS = {
+ 'CRITICAL': 'is-limit--red',
+ 'WARNING': 'is-limit--yellow',
+ 'WATCH': 'is-limit--cyan'
+};
+
+export default {
+ inject: ['openmct', 'domainObject'],
+ props: {
+ fault: {
+ type: Object,
+ required: true
+ },
+ isSelected: {
+ type: Boolean,
+ default: () => {
+ return false;
+ }
+ }
+ },
+ computed: {
+ classesFromState() {
+ const exclusiveStates = [
+ {
+ className: 'is-shelved',
+ test: () => this.fault.shelved
+ },
+ {
+ className: 'is-unacknowledged',
+ test: () => !this.fault.acknowledged && !this.fault.shelved
+ },
+ {
+ className: 'is-acknowledged',
+ test: () => this.fault.acknowledged && !this.fault.shelved
+ }
+ ];
+
+ const classes = [];
+
+ if (this.isSelected) {
+ classes.push('is-selected');
+ }
+
+ const matchingState = exclusiveStates.find(stateDefinition => stateDefinition.test());
+
+ if (matchingState !== undefined) {
+ classes.push(matchingState.className);
+ }
+
+ return classes;
+ },
+ liveValueClassname() {
+ const currentValueInfo = this.fault?.currentValueInfo;
+ if (!currentValueInfo || currentValueInfo.monitoringResult === 'IN_LIMITS') {
+ return '';
+ }
+
+ let classname = RANGE_CONDITION_CLASS[currentValueInfo.rangeCondition] || '';
+ classname += ' ';
+ classname += SEVERITY_CLASS[currentValueInfo.monitoringResult] || '';
+
+ return classname.trim();
+ },
+ name() {
+ return `${this.fault?.name}/${this.fault?.namespace}`;
+ },
+ severity() {
+ return this.fault?.severity?.toLowerCase();
+ },
+ triggerTime() {
+ return this.fault?.triggerTime;
+ },
+ triggerValue() {
+ return this.fault?.triggerValueInfo?.value;
+ },
+ tripValueClassname() {
+ const triggerValueInfo = this.fault?.triggerValueInfo;
+ if (!triggerValueInfo || triggerValueInfo.monitoringResult === 'IN_LIMITS') {
+ return '';
+ }
+
+ let classname = RANGE_CONDITION_CLASS[triggerValueInfo.rangeCondition] || '';
+ classname += ' ';
+ classname += SEVERITY_CLASS[triggerValueInfo.monitoringResult] || '';
+
+ return classname.trim();
+ }
+ },
+ methods: {
+ showActionMenu(event) {
+ event.stopPropagation();
+
+ const menuItems = [
+ {
+ cssClass: 'icon-check',
+ isDisabled: this.fault.acknowledged,
+ name: 'Acknowledge',
+ description: '',
+ onItemClicked: (e) => {
+ this.$emit('acknowledgeSelected', [this.fault]);
+ }
+ },
+ {
+ cssClass: 'icon-timer',
+ name: 'Shelve',
+ description: '',
+ onItemClicked: () => {
+ this.$emit('shelveSelected', [this.fault], { shelved: true });
+ }
+ },
+ {
+ cssClass: 'icon-timer',
+ isDisabled: Boolean(!this.fault.shelved),
+ name: 'Unshelve',
+ description: '',
+ onItemClicked: () => {
+ this.$emit('shelveSelected', [this.fault], { shelved: false });
+ }
+ }
+ ];
+
+ this.openmct.menus.showMenu(event.x, event.y, menuItems);
+ },
+ toggleSelected(event) {
+ const faultData = {
+ fault: this.fault,
+ selected: event.target.checked
+ };
+
+ this.$emit('toggleSelected', faultData);
+ }
+ }
+};
+</script>
diff --git a/src/plugins/faultManagement/FaultManagementListView.vue b/src/plugins/faultManagement/FaultManagementListView.vue
new file mode 100644
index 000000000..f07dc839a
--- /dev/null
+++ b/src/plugins/faultManagement/FaultManagementListView.vue
@@ -0,0 +1,307 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+<template>
+<div class="c-faults-list-view">
+ <FaultManagementSearch
+ :search-term="searchTerm"
+ @filterChanged="updateFilter"
+ @updateSearchTerm="updateSearchTerm"
+ />
+
+ <FaultManagementToolbar
+ v-if="showToolbar"
+ :selected-faults="selectedFaults"
+ @acknowledgeSelected="toggleAcknowledgeSelected"
+ @shelveSelected="toggleShelveSelected"
+ />
+
+ <div class="c-faults-list-view-header-item-container-wrapper">
+ <div class="c-faults-list-view-header-item-container">
+ <FaultManagementListHeader
+ class="header"
+ :selected-faults="Object.values(selectedFaults)"
+ :total-faults-count="filteredFaultsList.length"
+ @selectAll="selectAll"
+ @sortChanged="sortChanged"
+ />
+
+ <div class="c-faults-list-view-item-body">
+ <template v-if="filteredFaultsList.length > 0">
+ <FaultManagementListItem
+ v-for="fault of filteredFaultsList"
+ :key="fault.id"
+ :fault="fault"
+ :is-selected="isSelected(fault)"
+ @toggleSelected="toggleSelected"
+ @acknowledgeSelected="toggleAcknowledgeSelected"
+ @shelveSelected="toggleShelveSelected"
+ />
+ </template>
+ </div>
+ </div>
+ </div>
+</div>
+</template>
+
+<script>
+import FaultManagementListHeader from './FaultManagementListHeader.vue';
+import FaultManagementListItem from './FaultManagementListItem.vue';
+import FaultManagementSearch from './FaultManagementSearch.vue';
+import FaultManagementToolbar from './FaultManagementToolbar.vue';
+
+import { FAULT_MANAGEMENT_SHELVE_DURATIONS_IN_MS, FILTER_ITEMS, SORT_ITEMS } from './constants';
+
+export default {
+ components: {
+ FaultManagementListHeader,
+ FaultManagementListItem,
+ FaultManagementSearch,
+ FaultManagementToolbar
+ },
+ inject: ['openmct', 'domainObject'],
+ props: {
+ faultsList: {
+ type: Array,
+ default: () => []
+ }
+ },
+ data() {
+ return {
+ filterIndex: 0,
+ searchTerm: '',
+ selectedFaults: {},
+ sortBy: Object.values(SORT_ITEMS)[0].value
+ };
+ },
+ computed: {
+ filteredFaultsList() {
+ const filterName = FILTER_ITEMS[this.filterIndex];
+ let list = this.faultsList;
+
+ // Exclude shelved alarms from all views except the Shelved view
+ if (filterName !== 'Shelved') {
+ list = list.filter(fault => fault.shelved !== true);
+ }
+
+ if (filterName === 'Acknowledged') {
+ list = list.filter(fault => fault.acknowledged);
+ } else if (filterName === 'Unacknowledged') {
+ list = list.filter(fault => !fault.acknowledged);
+ } else if (filterName === 'Shelved') {
+ list = list.filter(fault => fault.shelved);
+ }
+
+ if (this.searchTerm.length > 0) {
+ list = list.filter(this.filterUsingSearchTerm);
+ }
+
+ list.sort(SORT_ITEMS[this.sortBy].sortFunction);
+
+ return list;
+ },
+ showToolbar() {
+ return this.openmct.faults.supportsActions();
+ }
+ },
+ methods: {
+ filterUsingSearchTerm(fault) {
+ if (fault?.id?.toString().toLowerCase().includes(this.searchTerm)) {
+ return true;
+ }
+
+ if (fault?.triggerValueInfo?.toString().toLowerCase().includes(this.searchTerm)) {
+ return true;
+ }
+
+ if (fault?.currentValueInfo?.toString().toLowerCase().includes(this.searchTerm)) {
+ return true;
+ }
+
+ if (fault?.triggerTime.toString().toLowerCase().includes(this.searchTerm)) {
+ return true;
+ }
+
+ if (fault?.severity.toString().toLowerCase().includes(this.searchTerm)) {
+ return true;
+ }
+
+ return false;
+ },
+ isSelected(fault) {
+ return Boolean(this.selectedFaults[fault.id]);
+ },
+ selectAll(toggle = false) {
+ this.faultsList.forEach(fault => {
+ const faultData = {
+ fault,
+ selected: toggle
+ };
+ this.toggleSelected(faultData);
+ });
+ },
+ sortChanged(sort) {
+ this.sortBy = sort.value;
+ },
+ toggleSelected({ fault, selected = false}) {
+ if (selected) {
+ this.$set(this.selectedFaults, fault.id, fault);
+ } else {
+ this.$delete(this.selectedFaults, fault.id);
+ }
+
+ const selectedFaults = Object.values(this.selectedFaults);
+ this.openmct.selection.select(
+ [
+ {
+ element: this.$el,
+ context: {
+ item: this.openmct.router.path[0]
+ }
+ },
+ {
+ element: this.$el,
+ context: {
+ selectedFaults
+ }
+ }
+ ],
+ false);
+ },
+ toggleAcknowledgeSelected(faults = Object.values(this.selectedFaults)) {
+ let title = '';
+ if (faults.length > 1) {
+ title = `Acknowledge ${faults.length} selected faults`;
+ } else {
+ title = `Acknowledge fault: ${faults[0].name}`;
+ }
+
+ const formStructure = {
+ title,
+ sections: [
+ {
+ rows: [
+ {
+ key: 'comment',
+ control: 'textarea',
+ name: 'Optional comment',
+ pattern: '\\S+',
+ required: false,
+ cssClass: 'l-input-lg',
+ value: ''
+ }
+ ]
+ }
+ ],
+ buttons: {
+ submit: {
+ label: 'Acknowledge'
+ }
+ }
+ };
+
+ this.openmct.forms.showForm(formStructure)
+ .then(data => {
+ Object.values(faults)
+ .forEach(selectedFault => {
+ this.openmct.faults.acknowledgeFault(selectedFault, data);
+ });
+ });
+
+ this.selectedFaults = {};
+ },
+ async toggleShelveSelected(faults = Object.values(this.selectedFaults), shelveData = {}) {
+ const { shelved = true } = shelveData;
+ if (shelved) {
+ let title = faults.length > 1
+ ? `Shelve ${faults.length} selected faults`
+ : `Shelve fault: ${faults[0].name}`
+ ;
+
+ const formStructure = {
+ title,
+ sections: [
+ {
+ rows: [
+ {
+ key: 'comment',
+ control: 'textarea',
+ name: 'Optional comment',
+ pattern: '\\S+',
+ required: false,
+ cssClass: 'l-input-lg',
+ value: ''
+ },
+ {
+ key: 'shelveDuration',
+ control: 'select',
+ name: 'Shelve duration',
+ options: FAULT_MANAGEMENT_SHELVE_DURATIONS_IN_MS,
+ required: false,
+ cssClass: 'l-input-lg',
+ value: FAULT_MANAGEMENT_SHELVE_DURATIONS_IN_MS[0].value
+ }
+ ]
+ }
+ ],
+ buttons: {
+ submit: {
+ label: 'Shelve'
+ }
+ }
+ };
+
+ let data;
+ try {
+ data = await this.openmct.forms.showForm(formStructure);
+ } catch (e) {
+ return;
+ }
+
+ shelveData.comment = data.comment || '';
+ shelveData.shelveDuration = data.shelveDuration !== undefined
+ ? data.shelveDuration
+ : FAULT_MANAGEMENT_SHELVE_DURATIONS_IN_MS[0].value;
+ } else {
+ shelveData = {
+ shelved: false
+ };
+ }
+
+ Object.values(faults)
+ .forEach(selectedFault => {
+ this.openmct.faults.shelveFault(selectedFault, shelveData);
+ });
+
+ this.selectedFaults = {};
+ },
+ updateFilter(filter) {
+ this.selectAll();
+
+ this.filterIndex = filter.model.options.findIndex(option => option.value === filter.value);
+ },
+ updateSearchTerm(term = '') {
+ this.searchTerm = term.toLowerCase();
+ }
+ }
+};
+</script>
diff --git a/example/notifications/src/DialogLaunchIndicator.js b/src/plugins/faultManagement/FaultManagementObjectProvider.js
index a2b0ec2f1..9565c27c1 100644
--- a/example/notifications/src/DialogLaunchIndicator.js
+++ b/src/plugins/faultManagement/FaultManagementObjectProvider.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -20,36 +20,37 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
-define(
- [],
- function () {
- "use strict";
+import { FAULT_MANAGEMENT_TYPE, FAULT_MANAGEMENT_VIEW, FAULT_MANAGEMENT_NAMESPACE } from './constants';
- /**
- * A tool for manually invoking dialogs. When included this
- * indicator will allow for dialogs of different types to be
- * launched for demonstration and testing purposes.
- * @constructor
- */
+export default class FaultManagementObjectProvider {
+ constructor(openmct) {
+ this.openmct = openmct;
+ this.namespace = FAULT_MANAGEMENT_NAMESPACE;
+ this.key = FAULT_MANAGEMENT_VIEW;
+ this.objects = {};
- function DialogLaunchIndicator() {
-
- }
-
- DialogLaunchIndicator.template = 'dialogLaunchTemplate';
+ this.createFaultManagementRootObject();
+ }
- DialogLaunchIndicator.prototype.getGlyphClass = function () {
- return 'ok';
+ createFaultManagementRootObject() {
+ this.rootObject = {
+ identifier: {
+ key: this.key,
+ namespace: this.namespace
+ },
+ name: 'Fault Management',
+ type: FAULT_MANAGEMENT_TYPE,
+ location: 'ROOT'
};
- DialogLaunchIndicator.prototype.getText = function () {
- return "Launch test dialog";
- };
+ this.openmct.objects.addRoot(this.rootObject.identifier);
+ }
- DialogLaunchIndicator.prototype.getDescription = function () {
- return "Launch test dialog";
- };
+ get(identifier) {
+ if (identifier.key === FAULT_MANAGEMENT_VIEW) {
+ return Promise.resolve(this.rootObject);
+ }
- return DialogLaunchIndicator;
+ return Promise.reject();
}
-);
+}
diff --git a/src/plugins/faultManagement/FaultManagementPlugin.js b/src/plugins/faultManagement/FaultManagementPlugin.js
new file mode 100644
index 000000000..93dda8f5b
--- /dev/null
+++ b/src/plugins/faultManagement/FaultManagementPlugin.js
@@ -0,0 +1,42 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+import FaultManagementViewProvider from './FaultManagementViewProvider';
+import FaultManagementObjectProvider from './FaultManagementObjectProvider';
+import FaultManagementInspectorViewProvider from './FaultManagementInspectorViewProvider';
+
+import { FAULT_MANAGEMENT_TYPE, FAULT_MANAGEMENT_NAMESPACE } from './constants';
+
+export default function FaultManagementPlugin() {
+ return function (openmct) {
+ openmct.types.addType(FAULT_MANAGEMENT_TYPE, {
+ name: 'Fault Management',
+ creatable: false,
+ description: 'Fault Management View',
+ cssClass: 'icon-bell'
+ });
+
+ openmct.objectViews.addProvider(new FaultManagementViewProvider(openmct));
+ openmct.inspectorViews.addProvider(new FaultManagementInspectorViewProvider(openmct));
+ openmct.objects.addProvider(FAULT_MANAGEMENT_NAMESPACE, new FaultManagementObjectProvider(openmct));
+ };
+}
diff --git a/src/plugins/faultManagement/FaultManagementSearch.vue b/src/plugins/faultManagement/FaultManagementSearch.vue
new file mode 100644
index 000000000..bfd2060ff
--- /dev/null
+++ b/src/plugins/faultManagement/FaultManagementSearch.vue
@@ -0,0 +1,90 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+<template>
+<div class="c-fault-mgmt__search-row">
+ <Search
+ class="c-fault-mgmt-search"
+ :value="searchTerm"
+ @input="updateSearchTerm"
+ @clear="updateSearchTerm"
+ />
+
+ <SelectField
+ class="c-fault-mgmt-viewButton"
+ title="View Filter"
+ :model="model"
+ @onChange="onChange"
+ />
+</div>
+</template>
+
+<script>
+import SelectField from '@/api/forms/components/controls/SelectField.vue';
+import Search from '@/ui/components/search.vue';
+
+import { FILTER_ITEMS } from './constants';
+
+export default {
+ components: {
+ SelectField,
+ Search
+ },
+ inject: ['openmct', 'domainObject'],
+ props: {
+ searchTerm: {
+ type: String,
+ default: ''
+ }
+ },
+ data() {
+ return {
+ items: []
+ };
+ },
+ computed: {
+ model() {
+ return {
+ options: this.items,
+ value: this.items[0] ? this.items[0].value : FILTER_ITEMS[0].toLowerCase()
+ };
+ }
+ },
+ mounted() {
+ this.items = FILTER_ITEMS
+ .map(item => {
+ return {
+ name: item,
+ value: item.toLowerCase()
+ };
+ });
+ },
+ methods: {
+ onChange(data) {
+ this.$emit('filterChanged', data);
+ },
+ updateSearchTerm(searchTerm) {
+ this.$emit('updateSearchTerm', searchTerm);
+ }
+ }
+};
+</script>
diff --git a/src/plugins/faultManagement/FaultManagementToolbar.vue b/src/plugins/faultManagement/FaultManagementToolbar.vue
new file mode 100644
index 000000000..6134a449b
--- /dev/null
+++ b/src/plugins/faultManagement/FaultManagementToolbar.vue
@@ -0,0 +1,102 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+<template>
+<div class="c-fault-mgmt__toolbar">
+ <button
+ class="c-icon-button icon-check"
+ title="Acknowledge selected faults"
+ :disabled="disableAcknowledge"
+ @click="acknowledgeSelected"
+ >
+ <div
+ title="Acknowledge selected faults"
+ class="c-icon-button__label"
+ >
+ Acknowledge
+ </div>
+ </button>
+
+ <button
+ class="c-icon-button icon-timer"
+ title="Shelve selected faults"
+ :disabled="disableShelve"
+ @click="shelveSelected"
+ >
+ <div
+ title="Shelve selected items"
+ class="c-icon-button__label"
+ >
+ Shelve
+ </div>
+ </button>
+</div>
+</template>
+
+<script>
+export default {
+ inject: ['openmct', 'domainObject'],
+ props: {
+ selectedFaults: {
+ type: Object,
+ default() {
+ return {};
+ }
+ }
+ },
+ data() {
+ return {
+ disableAcknowledge: true,
+ disableShelve: true
+ };
+ },
+ watch: {
+ selectedFaults(newSelectedFaults) {
+ const selectedfaults = Object.values(newSelectedFaults);
+
+ let disableAcknowledge = true;
+ let disableShelve = true;
+
+ selectedfaults.forEach(fault => {
+ if (!fault.shelved) {
+ disableShelve = false;
+ }
+
+ if (!fault.acknowledged) {
+ disableAcknowledge = false;
+ }
+ });
+
+ this.disableAcknowledge = disableAcknowledge;
+ this.disableShelve = disableShelve;
+ }
+ },
+ methods: {
+ acknowledgeSelected() {
+ this.$emit('acknowledgeSelected');
+ },
+ shelveSelected() {
+ this.$emit('shelveSelected');
+ }
+ }
+};
+</script>
diff --git a/src/plugins/faultManagement/FaultManagementView.vue b/src/plugins/faultManagement/FaultManagementView.vue
new file mode 100644
index 000000000..71ba7cfe7
--- /dev/null
+++ b/src/plugins/faultManagement/FaultManagementView.vue
@@ -0,0 +1,76 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+<template>
+<FaultManagementListView
+ :faults-list="faultsList"
+/>
+</template>
+
+<script>
+
+import FaultManagementListView from './FaultManagementListView.vue';
+import { FAULT_MANAGEMENT_ALARMS, FAULT_MANAGEMENT_GLOBAL_ALARMS } from './constants';
+
+export default {
+ components: {
+ FaultManagementListView
+ },
+ inject: ['openmct', 'domainObject'],
+ data() {
+ return {
+ faultsList: []
+ };
+ },
+ mounted() {
+ this.updateFaultList();
+
+ this.unsubscribe = this.openmct.faults
+ .subscribe(this.domainObject, this.updateFault);
+ },
+ beforeDestroy() {
+ if (this.unsubscribe) {
+ this.unsubscribe();
+ }
+ },
+ methods: {
+ updateFault({ fault, type }) {
+ if (type === FAULT_MANAGEMENT_GLOBAL_ALARMS) {
+ this.updateFaultList();
+ } else if (type === FAULT_MANAGEMENT_ALARMS) {
+ this.faultsList.forEach((faultValue, i) => {
+ if (fault.id === faultValue.id) {
+ this.$set(this.faultsList, i, fault);
+ }
+ });
+ }
+ },
+ updateFaultList() {
+ this.openmct.faults
+ .request(this.domainObject)
+ .then(faultsData => {
+ this.faultsList = faultsData.map(fd => fd.fault);
+ });
+ }
+ }
+};
+</script>
diff --git a/src/plugins/faultManagement/FaultManagementViewProvider.js b/src/plugins/faultManagement/FaultManagementViewProvider.js
new file mode 100644
index 000000000..9576cfd97
--- /dev/null
+++ b/src/plugins/faultManagement/FaultManagementViewProvider.js
@@ -0,0 +1,69 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+import FaultManagementView from './FaultManagementView.vue';
+import { FAULT_MANAGEMENT_TYPE, FAULT_MANAGEMENT_VIEW } from './constants';
+import Vue from 'vue';
+
+export default class FaultManagementViewProvider {
+ constructor(openmct) {
+ this.openmct = openmct;
+ this.key = FAULT_MANAGEMENT_VIEW;
+ }
+
+ canView(domainObject) {
+ return domainObject.type === FAULT_MANAGEMENT_TYPE;
+ }
+
+ canEdit(domainObject) {
+ return false;
+ }
+
+ view(domainObject) {
+ let component;
+ const openmct = this.openmct;
+
+ return {
+ show: (element) => {
+ component = new Vue({
+ el: element,
+ components: {
+ FaultManagementView
+ },
+ provide: {
+ openmct,
+ domainObject
+ },
+ template: '<FaultManagementView></FaultManagementView>'
+ });
+ },
+ destroy: () => {
+ if (!component) {
+ return;
+ }
+
+ component.$destroy();
+ component = undefined;
+ }
+ };
+ }
+}
diff --git a/src/plugins/faultManagement/constants.js b/src/plugins/faultManagement/constants.js
new file mode 100644
index 000000000..9f0be44a5
--- /dev/null
+++ b/src/plugins/faultManagement/constants.js
@@ -0,0 +1,122 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+const FAULT_SEVERITY = {
+ 'CRITICAL': {
+ name: 'CRITICAL',
+ value: 'critical',
+ priority: 0
+ },
+ 'WARNING': {
+ name: 'WARNING',
+ value: 'warning',
+ priority: 1
+ },
+ 'WATCH': {
+ name: 'WATCH',
+ value: 'watch',
+ priority: 2
+ }
+};
+
+export const FAULT_MANAGEMENT_TYPE = 'faultManagement';
+export const FAULT_MANAGEMENT_INSPECTOR = 'faultManagementInspector';
+export const FAULT_MANAGEMENT_ALARMS = 'alarms';
+export const FAULT_MANAGEMENT_GLOBAL_ALARMS = 'global-alarm-status';
+export const FAULT_MANAGEMENT_SHELVE_DURATIONS_IN_MS = [
+ {
+ name: '5 Minutes',
+ value: 300000
+ },
+ {
+ name: '10 Minutes',
+ value: 600000
+ },
+ {
+ name: '15 Minutes',
+ value: 900000
+ },
+ {
+ name: 'Indefinite',
+ value: 0
+ }
+];
+export const FAULT_MANAGEMENT_VIEW = 'faultManagement.view';
+export const FAULT_MANAGEMENT_NAMESPACE = 'faults.taxonomy';
+export const FILTER_ITEMS = [
+ 'Standard View',
+ 'Acknowledged',
+ 'Unacknowledged',
+ 'Shelved'
+];
+export const SORT_ITEMS = {
+ 'newest-first': {
+ name: 'Newest First',
+ value: 'newest-first',
+ sortFunction: (a, b) => {
+ if (b.triggerTime > a.triggerTime) {
+ return 1;
+ }
+
+ if (a.triggerTime > b.triggerTime) {
+ return -1;
+ }
+
+ return 0;
+ }
+ },
+ 'oldest-first': {
+ name: 'Oldest First',
+ value: 'oldest-first',
+ sortFunction: (a, b) => {
+ if (a.triggerTime > b.triggerTime) {
+ return 1;
+ }
+
+ if (a.triggerTime < b.triggerTime) {
+ return -1;
+ }
+
+ return 0;
+ }
+ },
+ 'severity': {
+ name: 'Severity',
+ value: 'severity',
+ sortFunction: (a, b) => {
+ const diff = FAULT_SEVERITY[a.severity].priority - FAULT_SEVERITY[b.severity].priority;
+ if (diff !== 0) {
+ return diff;
+ }
+
+ if (b.triggerTime > a.triggerTime) {
+ return 1;
+ }
+
+ if (a.triggerTime > b.triggerTime) {
+ return -1;
+ }
+
+ return 0;
+ }
+ }
+};
diff --git a/src/plugins/faultManagement/fault-manager.scss b/src/plugins/faultManagement/fault-manager.scss
new file mode 100644
index 000000000..e1c97443d
--- /dev/null
+++ b/src/plugins/faultManagement/fault-manager.scss
@@ -0,0 +1,268 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+$colorFaultItemFg: $colorBodyFg;
+$colorFaultItemFgEmphasis: $colorBodyFgEm;
+$colorFaultItemBg: pullForward($colorBodyBg, 5%);
+
+/*********************************************** SEARCH */
+.c-fault-mgmt__search-row {
+ display: flex;
+ align-items: center;
+ flex: 0 0 auto;
+ > * + * {
+ margin-left: 10px;
+ float: right;
+ }
+}
+
+.c-fault-mgmt-search {
+ width: 95%;
+}
+
+/*********************************************** TOOLBAR */
+.c-fault-mgmt__toolbar {
+ display: flex;
+ justify-content: center;
+ flex: 0 0 auto;
+ > * + * {
+ margin-left: $interiorMargin;
+ }
+}
+
+/*********************************************** LIST VIEW */
+.c-faults-list-view {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+
+ > * + * {
+ margin-top: $interiorMargin;
+ }
+}
+
+.c-faults-list-view-header-item-container {
+ display: grid;
+ width: 100%;
+ grid-template-columns: max-content max-content repeat(5,minmax(max-content, 20%)) max-content;
+ grid-row-gap: $interiorMargin;
+
+ &-wrapper {
+ flex: 1 1 auto;
+ padding-right: $interiorMargin; // Fend of from scrollbar
+ overflow-y: auto;
+ }
+
+ .--width-less-than-600 & {
+ grid-template-columns: max-content max-content 1fr 1fr max-content;
+ }
+}
+
+.c-faults-list-view-item-body {
+ display: contents;
+}
+
+/*********************************************** LIST */
+.c-fault-mgmt__list {
+ display: contents;
+ color: $colorFaultItemFg;
+
+ &-checkbox{
+ border-top-left-radius: 4px;
+ border-bottom-left-radius: 4px;
+ }
+
+ &-severity {
+ font-size: 2em;
+
+ &.is-severity-critical {
+ @include glyphBefore($glyph-icon-alert-triangle);
+ color: $colorStatusError;
+ }
+
+ &.is-severity-warning {
+ @include glyphBefore($glyph-icon-alert-rect);
+ color: $colorStatusAlert;
+ }
+
+ &.is-severity-watch {
+ @include glyphBefore($glyph-icon-info);
+ color: $colorCommand;
+ }
+ }
+
+ &-content {
+ display: contents;
+
+ .--width-less-than-600 & {
+ display: flex;
+ flex-wrap: wrap;
+ grid-column: span 2;
+ }
+ }
+
+ &-pathname {
+ padding-right: $interiorMarginLg;
+ overflow-wrap: anywhere;
+ min-width: 100px;
+
+ }
+ &-path {
+ font-size: .85em;
+ margin-left: $interiorMargin;
+ }
+
+ &-faultname{
+ font-size: 1.3em;
+ margin-left: $interiorMargin;
+ }
+
+ &-content-right {
+ display: contents;
+ }
+
+ &-trigTime {
+ grid-column: 6 / span 2;
+ }
+
+ &-action-wrapper {
+ text-align: right;
+ flex: 0 0 auto;
+ align-items: stretch;
+ }
+
+ &-action-button {
+ flex: 0 0 auto;
+ margin-left: auto;
+ text-align: right;
+ }
+
+ // STATES
+ &.is-unacknowledged {
+ color: $colorFaultItemFgEmphasis;
+ .c-fault-mgmt__list-severity {
+ @include pulse($animName: severityAnim, $dur: 200ms);
+ }
+ }
+
+ &.is-acknowledged,
+ &.is-shelved {
+ .c-fault-mgmt__list-severity {
+ &:before {
+ opacity: 60%;
+ //font-size: 1.5em;
+ }
+
+ &:after {
+ color: $colorFaultItemFgEmphasis;
+ display: block;
+ font-family: symbolsfont;
+ position: absolute;
+ //text-shadow: black 0 0 2px;
+ right: -3px;
+ bottom: -3px;
+ transform-origin: right bottom;
+ transform: scale(0.6);
+ }
+ }
+ }
+
+ &.is-shelved {
+ .c-fault-mgmt__list-pathname {
+ font-style: italic;
+ }
+ }
+
+ &.is-acknowledged .c-fault-mgmt__list-severity:after {
+ content: $glyph-icon-check;
+ }
+
+ &.is-shelved .c-fault-mgmt__list-severity:after {
+ content: $glyph-icon-timer;
+ }
+}
+
+/*********************************************** LIST HEADER */
+.c-fault-mgmt__list-header {
+ display: contents;
+ border-radius: $controlCr;
+ align-items: center;
+
+ * {
+ margin: 0px;
+ border-radius: 0px;
+ }
+
+ .--width-less-than-600 & {
+ .c-fault-mgmt__list-content-right {
+ display:none;
+ }
+ }
+
+ &-content {
+ display: contents;
+ }
+
+ &-results {
+ grid-column: 2 / span 2;
+ font-size: 1em;
+ height: auto;
+ }
+
+ &-action-wrapper {
+ grid-column: 7 / span 2;
+
+ .--width-less-than-600 & {
+ grid-column: 4 / span 2;
+ }
+ }
+}
+
+/*********************************************** GRID ITEM */
+.c-fault-mgmt-item {
+ $p: $interiorMargin;
+ padding: $p;
+ background: $colorFaultItemBg;
+ white-space: nowrap;
+
+ &-header {
+ $c: $colorBodyBg;
+ background: $c;
+ border-bottom: 5px solid $c; // Creates illusion of "space" beneath header
+ min-height: 30px; // Needed to align cells
+ padding: $p;
+ position: sticky;
+ top: 0;
+ z-index: 2;
+ }
+
+ &__value {
+ @include isLimit();
+ background: rgba($colorBodyFg, 0.1);
+ padding: $p;
+ border-radius: $controlCr;
+ display: inline-flex;
+ }
+
+ .is-selected & {
+ background: $colorSelectedBg;
+ }
+}
diff --git a/src/adapter/policies/LegacyCompositionPolicyAdapter.js b/src/plugins/faultManagement/pluginSpec.js
index 1325c7d90..07ad9664c 100644
--- a/src/adapter/policies/LegacyCompositionPolicyAdapter.js
+++ b/src/plugins/faultManagement/pluginSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -20,23 +20,33 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
-export default function legacyCompositionPolicyAdapter(openmct) {
- const instantiate = openmct.$injector.get('instantiate');
- const policyService = openmct.$injector.get('policyService');
+import {
+ createOpenMct,
+ resetApplicationState
+} from '../../utils/testing';
+import { FAULT_MANAGEMENT_TYPE } from './constants';
- openmct.composition.addPolicy((parent, child) => {
+describe("The Fault Management Plugin", () => {
+ let openmct;
- let parentId = openmct.objects.makeKeyString(parent.identifier);
- let childId = openmct.objects.makeKeyString(child.identifier);
+ beforeEach(() => {
+ openmct = createOpenMct();
+ });
+
+ afterEach(() => {
+ return resetApplicationState(openmct);
+ });
+
+ it('is not installed by default', () => {
+ let typeDef = openmct.types.get(FAULT_MANAGEMENT_TYPE).definition;
+
+ expect(typeDef.name).toBe('Unknown Type');
+ });
- let legacyParent = instantiate(parent, parentId);
- let legacyChild = instantiate(child, childId);
- let result = policyService.allow(
- 'composition',
- legacyParent,
- legacyChild
- );
+ it('can be installed', () => {
+ openmct.install(openmct.plugins.FaultManagement());
+ let typeDef = openmct.types.get(FAULT_MANAGEMENT_TYPE).definition;
- return result;
+ expect(typeDef.name).toBe('Fault Management');
});
-}
+});
diff --git a/src/plugins/filters/FiltersInspectorViewProvider.js b/src/plugins/filters/FiltersInspectorViewProvider.js
index f6fcd7de4..d726e9e37 100644
--- a/src/plugins/filters/FiltersInspectorViewProvider.js
+++ b/src/plugins/filters/FiltersInspectorViewProvider.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/filters/plugin.js b/src/plugins/filters/plugin.js
index 29cfd83a9..78d70c1cd 100644
--- a/src/plugins/filters/plugin.js
+++ b/src/plugins/filters/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/flexibleLayout/components/container.vue b/src/plugins/flexibleLayout/components/container.vue
index ff51918f1..b12e9aba0 100644
--- a/src/plugins/flexibleLayout/components/container.vue
+++ b/src/plugins/flexibleLayout/components/container.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -57,7 +57,7 @@
/>
<drop-hint
- :key="i"
+ :key="'hint-' + i"
class="c-fl-frame__drop-hint"
:index="i"
:allow-drop="allowDrop"
@@ -66,7 +66,7 @@
<resize-handle
v-if="(i !== frames.length - 1)"
- :key="i"
+ :key="'handle-' + i"
:index="i"
:orientation="rowsLayout ? 'horizontal' : 'vertical'"
:is-editing="isEditing"
diff --git a/src/plugins/flexibleLayout/components/dropHint.vue b/src/plugins/flexibleLayout/components/dropHint.vue
index 87858975c..5ddccd490 100644
--- a/src/plugins/flexibleLayout/components/dropHint.vue
+++ b/src/plugins/flexibleLayout/components/dropHint.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/flexibleLayout/components/flexible-layout.scss b/src/plugins/flexibleLayout/components/flexible-layout.scss
index ac44eb3d3..6fe96a446 100644
--- a/src/plugins/flexibleLayout/components/flexible-layout.scss
+++ b/src/plugins/flexibleLayout/components/flexible-layout.scss
@@ -141,6 +141,10 @@
}
}
}
+
+ [s-selected].c-fl-frame__drag-wrapper {
+ border: $editFrameSelectedBorder;
+ }
}
/****** THEIR FRAMES */
diff --git a/src/plugins/flexibleLayout/components/flexibleLayout.vue b/src/plugins/flexibleLayout/components/flexibleLayout.vue
index 72e21ae48..503db9a1f 100644
--- a/src/plugins/flexibleLayout/components/flexibleLayout.vue
+++ b/src/plugins/flexibleLayout/components/flexibleLayout.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -43,7 +43,7 @@
<template v-for="(container, index) in containers">
<drop-hint
v-if="index === 0 && containers.length > 1"
- :key="index"
+ :key="`hint-top-${container.id}`"
class="c-fl-frame__drop-hint"
:index="-1"
:allow-drop="allowContainerDrop"
@@ -51,7 +51,7 @@
/>
<container-component
- :key="container.id"
+ :key="`component-${container.id}`"
class="c-fl__container"
:index="index"
:container="container"
@@ -66,7 +66,7 @@
<resize-handle
v-if="index !== (containers.length - 1)"
- :key="index"
+ :key="`handle-${container.id}`"
:index="index"
:orientation="rowsLayout ? 'vertical' : 'horizontal'"
:is-editing="isEditing"
@@ -77,7 +77,7 @@
<drop-hint
v-if="containers.length > 1"
- :key="index"
+ :key="`hint-bottom-${container.id}`"
class="c-fl-frame__drop-hint"
:index="index"
:allow-drop="allowContainerDrop"
diff --git a/src/plugins/flexibleLayout/components/frame.vue b/src/plugins/flexibleLayout/components/frame.vue
index 4ab902405..70e6802a6 100644
--- a/src/plugins/flexibleLayout/components/frame.vue
+++ b/src/plugins/flexibleLayout/components/frame.vue
@@ -1,6 +1,6 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -97,7 +97,14 @@ export default {
},
mounted() {
if (this.frame.domainObjectIdentifier) {
- this.openmct.objects.get(this.frame.domainObjectIdentifier).then((object) => {
+ let domainObjectPromise;
+ if (this.openmct.objects.supportsMutation(this.frame.domainObjectIdentifier)) {
+ domainObjectPromise = this.openmct.objects.getMutable(this.frame.domainObjectIdentifier);
+ } else {
+ domainObjectPromise = this.openmct.objects.get(this.frame.domainObjectIdentifier);
+ }
+
+ domainObjectPromise.then((object) => {
this.setDomainObject(object);
});
}
@@ -105,6 +112,10 @@ export default {
this.dragGhost = document.getElementById('js-fl-drag-ghost');
},
beforeDestroy() {
+ if (this.domainObject.isMutable) {
+ this.openmct.objects.destroyMutable(this.domainObject);
+ }
+
if (this.unsubscribeSelection) {
this.unsubscribeSelection();
}
diff --git a/src/plugins/flexibleLayout/components/resizeHandle.vue b/src/plugins/flexibleLayout/components/resizeHandle.vue
index 4ab7a3999..198a815da 100644
--- a/src/plugins/flexibleLayout/components/resizeHandle.vue
+++ b/src/plugins/flexibleLayout/components/resizeHandle.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/flexibleLayout/flexibleLayoutViewProvider.js b/src/plugins/flexibleLayout/flexibleLayoutViewProvider.js
index d9bb15560..1139d7574 100644
--- a/src/plugins/flexibleLayout/flexibleLayoutViewProvider.js
+++ b/src/plugins/flexibleLayout/flexibleLayoutViewProvider.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/flexibleLayout/plugin.js b/src/plugins/flexibleLayout/plugin.js
index 6c2e0e5ec..7d66529ba 100644
--- a/src/plugins/flexibleLayout/plugin.js
+++ b/src/plugins/flexibleLayout/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/flexibleLayout/pluginSpec.js b/src/plugins/flexibleLayout/pluginSpec.js
index 6b5c19ee7..470fb6e8b 100644
--- a/src/plugins/flexibleLayout/pluginSpec.js
+++ b/src/plugins/flexibleLayout/pluginSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -22,6 +22,7 @@
import { createOpenMct, resetApplicationState } from 'utils/testing';
import FlexibleLayout from './plugin';
+import Vue from 'vue';
describe('the plugin', function () {
let element;
@@ -61,7 +62,7 @@ describe('the plugin', function () {
element.appendChild(child);
openmct.on('start', done);
- openmct.startHeadless();
+ openmct.start(child);
});
afterEach(() => {
@@ -83,6 +84,16 @@ describe('the plugin', function () {
it('provides a view', () => {
expect(flexibleLayoutViewProvider).toBeDefined();
});
+
+ it('renders a view', async () => {
+ const flexibleView = flexibleLayoutViewProvider.view(testViewObject, []);
+ flexibleView.show(child, false);
+
+ await Vue.nextTick();
+ const flexTitle = child.querySelector('.l-browse-bar .c-object-label__name');
+
+ expect(flexTitle).not.toBeNull();
+ });
});
describe('the toolbar', () => {
diff --git a/src/plugins/flexibleLayout/toolbarProvider.js b/src/plugins/flexibleLayout/toolbarProvider.js
index 27d00ac1e..49fdff416 100644
--- a/src/plugins/flexibleLayout/toolbarProvider.js
+++ b/src/plugins/flexibleLayout/toolbarProvider.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/flexibleLayout/utils/container.js b/src/plugins/flexibleLayout/utils/container.js
index 9ee51b225..a26bf08ad 100644
--- a/src/plugins/flexibleLayout/utils/container.js
+++ b/src/plugins/flexibleLayout/utils/container.js
@@ -1,4 +1,4 @@
-import uuid from 'uuid';
+import { v4 as uuid } from 'uuid';
class Container {
constructor(size) {
diff --git a/src/plugins/flexibleLayout/utils/frame.js b/src/plugins/flexibleLayout/utils/frame.js
index a444a20ea..767464419 100644
--- a/src/plugins/flexibleLayout/utils/frame.js
+++ b/src/plugins/flexibleLayout/utils/frame.js
@@ -1,4 +1,4 @@
-import uuid from 'uuid';
+import { v4 as uuid } from 'uuid';
class Frame {
constructor(domainObjectIdentifier, size) {
diff --git a/src/plugins/folderView/FolderGridView.js b/src/plugins/folderView/FolderGridView.js
index 75ee447b9..02042cce7 100644
--- a/src/plugins/folderView/FolderGridView.js
+++ b/src/plugins/folderView/FolderGridView.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/folderView/FolderListView.js b/src/plugins/folderView/FolderListView.js
index b4d9bd5d2..ca01bcb35 100644
--- a/src/plugins/folderView/FolderListView.js
+++ b/src/plugins/folderView/FolderListView.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/folderView/components/GridItem.vue b/src/plugins/folderView/components/GridItem.vue
index 47649d942..b8ffa4e87 100644
--- a/src/plugins/folderView/components/GridItem.vue
+++ b/src/plugins/folderView/components/GridItem.vue
@@ -26,8 +26,9 @@
</div>
</div>
<div class="c-grid-item__controls">
- <div class="is-status__indicator"
- :title="`This item is ${status}`"
+ <div
+ class="is-status__indicator"
+ :title="`This item is ${status}`"
></div>
<div
class="icon-people"
diff --git a/src/plugins/folderView/components/ListItem.vue b/src/plugins/folderView/components/ListItem.vue
index c1b013090..41f47b019 100644
--- a/src/plugins/folderView/components/ListItem.vue
+++ b/src/plugins/folderView/components/ListItem.vue
@@ -17,8 +17,9 @@
class="c-object-label__type-icon c-list-item__name__type-icon"
:class="item.type.cssClass"
>
- <span class="is-status__indicator"
- :title="`This item is ${status}`"
+ <span
+ class="is-status__indicator"
+ :title="`This item is ${status}`"
></span>
</div>
<div class="c-object-label__name c-list-item__name__name">{{ item.model.name }}</div>
diff --git a/src/plugins/folderView/components/ListView.vue b/src/plugins/folderView/components/ListView.vue
index 3a1b22dd8..1811b9170 100644
--- a/src/plugins/folderView/components/ListView.vue
+++ b/src/plugins/folderView/components/ListView.vue
@@ -1,5 +1,5 @@
<template>
-<div class="c-table c-table--sortable c-list-view">
+<div class="c-table c-table--sortable c-list-view c-list-view--sticky-header c-list-view--selectable">
<table class="c-table__body">
<thead class="c-table__header">
<tr>
diff --git a/src/plugins/folderView/components/list-view.scss b/src/plugins/folderView/components/list-view.scss
deleted file mode 100644
index 8b73cf3c7..000000000
--- a/src/plugins/folderView/components/list-view.scss
+++ /dev/null
@@ -1,32 +0,0 @@
-/******************************* LIST VIEW */
-.c-list-view {
- overflow-x: auto !important;
- overflow-y: auto;
-
- tbody tr {
- background: $colorListItemBg;
- transition: $transOut;
- }
-
- body.desktop & {
- tbody tr {
- cursor: pointer;
-
- &:hover {
- background: $colorListItemBgHov;
- filter: $filterHov;
- transition: $transIn;
- }
- }
- }
-
- td {
- $p: floor($interiorMargin * 1.5);
- @include ellipsize();
- line-height: 120%; // Needed for icon alignment
- max-width: 0;
- padding-top: $p;
- padding-bottom: $p;
- width: 25%;
- }
-}
diff --git a/src/plugins/folderView/constants.js b/src/plugins/folderView/constants.js
index 99c1427e1..8a047ad1f 100644
--- a/src/plugins/folderView/constants.js
+++ b/src/plugins/folderView/constants.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/folderView/plugin.js b/src/plugins/folderView/plugin.js
index 6d19ec5dd..5dede3452 100644
--- a/src/plugins/folderView/plugin.js
+++ b/src/plugins/folderView/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/folderView/pluginSpec.js b/src/plugins/folderView/pluginSpec.js
index d35aed446..6b0100648 100644
--- a/src/plugins/folderView/pluginSpec.js
+++ b/src/plugins/folderView/pluginSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/formActions/CreateAction.js b/src/plugins/formActions/CreateAction.js
index 6504ad452..6491331d1 100644
--- a/src/plugins/formActions/CreateAction.js
+++ b/src/plugins/formActions/CreateAction.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -23,7 +23,7 @@
import PropertiesAction from './PropertiesAction';
import CreateWizard from './CreateWizard';
-import uuid from 'uuid';
+import { v4 as uuid } from 'uuid';
export default class CreateAction extends PropertiesAction {
constructor(openmct, type, parentDomainObject) {
diff --git a/src/plugins/formActions/CreateActionSpec.js b/src/plugins/formActions/CreateActionSpec.js
new file mode 100644
index 000000000..2071da471
--- /dev/null
+++ b/src/plugins/formActions/CreateActionSpec.js
@@ -0,0 +1,128 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+import CreateAction from './CreateAction';
+
+import {
+ createOpenMct,
+ resetApplicationState
+} from 'utils/testing';
+
+import { debounce } from 'lodash';
+
+let parentObject;
+let parentObjectPath;
+let unObserve;
+
+describe("The create action plugin", () => {
+ let openmct;
+
+ const TYPES = [
+ 'clock',
+ 'conditionWidget',
+ 'conditionWidget',
+ 'example.imagery',
+ 'example.state-generator',
+ 'flexible-layout',
+ 'folder',
+ 'generator',
+ 'hyperlink',
+ 'LadTable',
+ 'LadTableSet',
+ 'layout',
+ 'mmgis',
+ 'notebook',
+ 'plan',
+ 'table',
+ 'tabs',
+ 'telemetry-mean',
+ 'telemetry.plot.bar-graph',
+ 'telemetry.plot.overlay',
+ 'telemetry.plot.stacked',
+ 'time-strip',
+ 'timer',
+ 'webpage'
+ ];
+
+ beforeEach((done) => {
+ openmct = createOpenMct();
+
+ openmct.on('start', done);
+ openmct.startHeadless();
+ });
+
+ afterEach(() => {
+ return resetApplicationState(openmct);
+ });
+
+ describe('creates new objects for a', () => {
+ beforeEach(() => {
+ parentObject = {
+ name: 'mock folder',
+ type: 'folder',
+ identifier: {
+ key: 'mock-folder',
+ namespace: ''
+ },
+ composition: []
+ };
+ parentObjectPath = [parentObject];
+
+ spyOn(openmct.objects, 'save');
+ openmct.objects.save.and.callThrough();
+ spyOn(openmct.forms, 'showForm');
+ openmct.forms.showForm.and.callFake(formStructure => {
+ return Promise.resolve({
+ name: 'test',
+ notes: 'test notes',
+ location: parentObjectPath
+ });
+ });
+ });
+
+ afterEach(() => {
+ parentObject = null;
+ unObserve();
+ });
+
+ TYPES.forEach(type => {
+ it(`type ${type}`, (done) => {
+ function callback(newObject) {
+ const composition = newObject.composition;
+
+ openmct.objects.get(composition[0])
+ .then(object => {
+ expect(object.type).toEqual(type);
+ expect(object.location).toEqual(openmct.objects.makeKeyString(parentObject.identifier));
+
+ done();
+ });
+ }
+
+ const deBouncedCallback = debounce(callback, 300);
+ unObserve = openmct.objects.observe(parentObject, '*', deBouncedCallback);
+
+ const createAction = new CreateAction(openmct, type, parentObject);
+ createAction.invoke();
+ });
+ });
+ });
+});
diff --git a/src/plugins/formActions/CreateWizard.js b/src/plugins/formActions/CreateWizard.js
index 67c217b7e..d17bdeb6b 100644
--- a/src/plugins/formActions/CreateWizard.js
+++ b/src/plugins/formActions/CreateWizard.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -90,6 +90,9 @@ export default class CreateWizard {
rows: this.properties.map(property => {
const row = JSON.parse(JSON.stringify(property));
row.value = this.getValue(row);
+ if (property.validate) {
+ row.validate = property.validate;
+ }
return row;
}).filter(row => row && row.control)
@@ -101,7 +104,10 @@ export default class CreateWizard {
// Ensure there is always a 'save in' section
if (includeLocation) {
function validateLocation(data) {
- return self.openmct.composition.checkPolicy(data.value[0], domainObject);
+ const policyCheck = self.openmct.composition.checkPolicy(data.value[0], domainObject);
+ const parentIsPersistable = self.openmct.objects.isPersistable(data.value[0].identifier);
+
+ return policyCheck && parentIsPersistable;
}
sections.push({
diff --git a/src/plugins/formActions/EditPropertiesAction.js b/src/plugins/formActions/EditPropertiesAction.js
index e3ed584fe..65ceaaadd 100644
--- a/src/plugins/formActions/EditPropertiesAction.js
+++ b/src/plugins/formActions/EditPropertiesAction.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -45,47 +45,42 @@ export default class EditPropertiesAction extends PropertiesAction {
}
invoke(objectPath) {
- this._showEditForm(objectPath);
+ return this._showEditForm(objectPath);
}
/**
* @private
*/
- async _onSave(changes) {
- Object.entries(changes).forEach(([key, value]) => {
- const properties = key.split('.');
- let object = this.domainObject;
- const propertiesLength = properties.length;
- properties.forEach((property, index) => {
- const isComplexProperty = propertiesLength > 1 && index !== propertiesLength - 1;
- if (isComplexProperty && object[property] !== null) {
- object = object[property];
- } else {
- object[property] = value;
- }
- });
-
- object = value;
- });
-
- this.domainObject.modified = Date.now();
+ _onSave(changes) {
+ try {
+ Object.entries(changes).forEach(([key, value]) => {
+ const properties = key.split('.');
+ let object = this.domainObject;
+ const propertiesLength = properties.length;
+ properties.forEach((property, index) => {
+ const isComplexProperty = propertiesLength > 1 && index !== propertiesLength - 1;
+ if (isComplexProperty && object[property] !== null) {
+ object = object[property];
+ } else {
+ object[property] = value;
+ }
+ });
- // Show saving progress dialog
- let dialog = this.openmct.overlays.progressDialog({
- progressPerc: 'unknown',
- message: 'Do not navigate away from this page or close this browser tab while this message is displayed.',
- iconClass: 'info',
- title: 'Saving'
- });
-
- const success = await this.openmct.objects.save(this.domainObject);
- if (success) {
- this.openmct.notifications.info('Save successful');
- } else {
+ object = value;
+ this.openmct.objects.mutate(this.domainObject, key, value);
+ this.openmct.notifications.info('Save successful');
+ });
+ } catch (error) {
this.openmct.notifications.error('Error saving objects');
+ console.error(error);
}
+ }
- dialog.dismiss();
+ /**
+ * @private
+ */
+ _onCancel() {
+ //noop
}
/**
@@ -98,7 +93,8 @@ export default class EditPropertiesAction extends PropertiesAction {
const formStructure = createWizard.getFormStructure(false);
formStructure.title = 'Edit ' + this.domainObject.name;
- this.openmct.forms.showForm(formStructure)
- .then(this._onSave.bind(this));
+ return this.openmct.forms.showForm(formStructure)
+ .then(this._onSave.bind(this))
+ .catch(this._onCancel.bind(this));
}
}
diff --git a/src/plugins/formActions/PropertiesAction.js b/src/plugins/formActions/PropertiesAction.js
index cca0a1af6..1e8e01691 100644
--- a/src/plugins/formActions/PropertiesAction.js
+++ b/src/plugins/formActions/PropertiesAction.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/formActions/plugin.js b/src/plugins/formActions/plugin.js
index 53d013139..9245841e6 100644
--- a/src/plugins/formActions/plugin.js
+++ b/src/plugins/formActions/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/formActions/pluginSpec.js b/src/plugins/formActions/pluginSpec.js
new file mode 100644
index 000000000..232ff0d30
--- /dev/null
+++ b/src/plugins/formActions/pluginSpec.js
@@ -0,0 +1,229 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+import {
+ createMouseEvent,
+ createOpenMct,
+ resetApplicationState
+} from 'utils/testing';
+
+import { debounce } from 'lodash';
+
+describe('EditPropertiesAction plugin', () => {
+ let editPropertiesAction;
+ let openmct;
+ let element;
+
+ beforeEach((done) => {
+ element = document.createElement('div');
+ element.style.display = 'block';
+ element.style.width = '1920px';
+ element.style.height = '1080px';
+
+ openmct = createOpenMct();
+ openmct.on('start', done);
+ openmct.startHeadless(element);
+
+ editPropertiesAction = openmct.actions.getAction('properties');
+ });
+
+ afterEach(() => {
+ editPropertiesAction = null;
+
+ return resetApplicationState(openmct);
+ });
+
+ it('editPropertiesAction exists', () => {
+ expect(editPropertiesAction.key).toEqual('properties');
+ });
+
+ it('edit properties action applies to only persistable objects', () => {
+ spyOn(openmct.objects, 'isPersistable').and.returnValue(true);
+
+ const domainObject = {
+ name: 'mock folder',
+ type: 'folder',
+ identifier: {
+ key: 'mock-folder',
+ namespace: ''
+ },
+ composition: []
+ };
+ const isApplicableTo = editPropertiesAction.appliesTo([domainObject]);
+ expect(isApplicableTo).toBe(true);
+ });
+
+ it('edit properties action does not apply to non persistable objects', () => {
+ spyOn(openmct.objects, 'isPersistable').and.returnValue(false);
+
+ const domainObject = {
+ name: 'mock folder',
+ type: 'folder',
+ identifier: {
+ key: 'mock-folder',
+ namespace: ''
+ },
+ composition: []
+ };
+ const isApplicableTo = editPropertiesAction.appliesTo([domainObject]);
+ expect(isApplicableTo).toBe(false);
+ });
+
+ it('edit properties action when invoked shows form', (done) => {
+ const domainObject = {
+ name: 'mock folder',
+ notes: 'mock notes',
+ type: 'folder',
+ identifier: {
+ key: 'mock-folder',
+ namespace: ''
+ },
+ modified: 1643065068597,
+ persisted: 1643065068600,
+ composition: []
+ };
+
+ const deBouncedFormChange = debounce(handleFormPropertyChange, 500);
+ openmct.forms.on('onFormPropertyChange', deBouncedFormChange);
+
+ function handleFormPropertyChange(data) {
+ const form = document.querySelector('.js-form');
+ const title = form.querySelector('input');
+ expect(title.value).toEqual(domainObject.name);
+
+ const notes = form.querySelector('textArea');
+ expect(notes.value).toEqual(domainObject.notes);
+
+ const buttons = form.querySelectorAll('button');
+ expect(buttons[0].textContent.trim()).toEqual('OK');
+ expect(buttons[1].textContent.trim()).toEqual('Cancel');
+
+ const clickEvent = createMouseEvent('click');
+ buttons[1].dispatchEvent(clickEvent);
+
+ openmct.forms.off('onFormPropertyChange', deBouncedFormChange);
+ }
+
+ editPropertiesAction.invoke([domainObject])
+ .then(() => {
+ done();
+ })
+ .catch(() => {
+ done();
+ });
+ });
+
+ it('edit properties action saves changes', (done) => {
+ const oldName = 'mock folder';
+ const newName = 'renamed mock folder';
+ const domainObject = {
+ name: oldName,
+ notes: 'mock notes',
+ type: 'folder',
+ identifier: {
+ key: 'mock-folder',
+ namespace: ''
+ },
+ modified: 1643065068597,
+ persisted: 1643065068600,
+ composition: []
+ };
+ let unObserve;
+
+ function callback(newObject) {
+ expect(newObject.name).not.toEqual(oldName);
+ expect(newObject.name).toEqual(newName);
+
+ unObserve();
+ done();
+ }
+
+ const deBouncedCallback = debounce(callback, 300);
+ unObserve = openmct.objects.observe(domainObject, '*', deBouncedCallback);
+
+ let changed = false;
+ const deBouncedFormChange = debounce(handleFormPropertyChange, 500);
+ openmct.forms.on('onFormPropertyChange', deBouncedFormChange);
+
+ function handleFormPropertyChange(data) {
+ const form = document.querySelector('.js-form');
+ const title = form.querySelector('input');
+ const notes = form.querySelector('textArea');
+
+ const buttons = form.querySelectorAll('button');
+ expect(buttons[0].textContent.trim()).toEqual('OK');
+ expect(buttons[1].textContent.trim()).toEqual('Cancel');
+
+ if (!changed) {
+ expect(title.value).toEqual(domainObject.name);
+ expect(notes.value).toEqual(domainObject.notes);
+
+ // change input field value and dispatch event for it
+ title.focus();
+ title.value = newName;
+ title.dispatchEvent(new Event('input'));
+ title.blur();
+
+ changed = true;
+ } else {
+ // click ok to save form changes
+ const clickEvent = createMouseEvent('click');
+ buttons[0].dispatchEvent(clickEvent);
+
+ openmct.forms.off('onFormPropertyChange', deBouncedFormChange);
+ }
+ }
+
+ editPropertiesAction.invoke([domainObject]);
+ });
+
+ it('edit properties action discards changes', (done) => {
+ const name = 'mock folder';
+ const domainObject = {
+ name,
+ notes: 'mock notes',
+ type: 'folder',
+ identifier: {
+ key: 'mock-folder',
+ namespace: ''
+ },
+ modified: 1643065068597,
+ persisted: 1643065068600,
+ composition: []
+ };
+
+ editPropertiesAction.invoke([domainObject])
+ .then(() => {
+ expect(domainObject.name).toEqual(name);
+ done();
+ })
+ .catch(() => {
+ expect(domainObject.name).toEqual(name);
+
+ done();
+ });
+
+ const form = document.querySelector('.js-form');
+ const buttons = form.querySelectorAll('button');
+ const clickEvent = createMouseEvent('click');
+ buttons[1].dispatchEvent(clickEvent);
+ });
+});
diff --git a/src/plugins/gauge/GaugePlugin.js b/src/plugins/gauge/GaugePlugin.js
new file mode 100644
index 000000000..441e53cd5
--- /dev/null
+++ b/src/plugins/gauge/GaugePlugin.js
@@ -0,0 +1,211 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+import GaugeViewProvider from './GaugeViewProvider';
+import GaugeFormController from './components/GaugeFormController.vue';
+import Vue from 'vue';
+
+export const GAUGE_TYPES = [
+ ['Filled Dial', 'dial-filled'],
+ ['Needle Dial', 'dial-needle'],
+ ['Vertical Meter', 'meter-vertical'],
+ ['Vertical Meter Inverted', 'meter-vertical-inverted'],
+ ['Horizontal Meter', 'meter-horizontal']
+];
+
+export default function () {
+ return function install(openmct) {
+ openmct.objectViews.addProvider(new GaugeViewProvider(openmct));
+
+ openmct.forms.addNewFormControl('gauge-controller', getGaugeFormController(openmct));
+ openmct.types.addType('gauge', {
+ name: "Gauge",
+ creatable: true,
+ description: "Graphically visualize a telemetry element's current value between a minimum and maximum.",
+ cssClass: 'icon-gauge',
+ initialize(domainObject) {
+ domainObject.composition = [];
+ domainObject.configuration = {
+ gaugeController: {
+ gaugeType: GAUGE_TYPES[0][1],
+ isDisplayMinMax: true,
+ isDisplayCurVal: true,
+ isDisplayUnits: true,
+ isUseTelemetryLimits: true,
+ limitLow: 10,
+ limitHigh: 90,
+ max: 100,
+ min: 0,
+ precision: 2
+ }
+ };
+ },
+ form: [
+ {
+ name: "Gauge type",
+ options: GAUGE_TYPES.map(type => {
+ return {
+ name: type[0],
+ value: type[1]
+ };
+ }),
+ control: "select",
+ cssClass: "l-input-sm",
+ key: "gaugeController",
+ property: [
+ "configuration",
+ "gaugeController",
+ "gaugeType"
+ ]
+ },
+ {
+ name: "Display current value",
+ control: "toggleSwitch",
+ cssClass: "l-input",
+ key: "isDisplayCurVal",
+ property: [
+ "configuration",
+ "gaugeController",
+ "isDisplayCurVal"
+ ]
+ },
+ {
+ name: "Display units",
+ control: "toggleSwitch",
+ cssClass: "l-input",
+ key: "isDisplayUnits",
+ property: [
+ "configuration",
+ "gaugeController",
+ "isDisplayUnits"
+ ]
+ },
+ {
+ name: "Display range values",
+ control: "toggleSwitch",
+ cssClass: "l-input",
+ key: "isDisplayMinMax",
+ property: [
+ "configuration",
+ "gaugeController",
+ "isDisplayMinMax"
+ ]
+ },
+ {
+ name: "Float precision",
+ control: "numberfield",
+ cssClass: "l-input-sm",
+ key: "precision",
+ property: [
+ "configuration",
+ "gaugeController",
+ "precision"
+ ]
+ },
+ {
+ name: "Value ranges and limits",
+ control: "gauge-controller",
+ cssClass: "l-input",
+ key: "gaugeController",
+ required: false,
+ hideFromInspector: true,
+ property: [
+ "configuration",
+ "gaugeController"
+ ],
+ validate: ({ value }, callback) => {
+ if (value.isUseTelemetryLimits) {
+ return true;
+ }
+
+ const { min, max, limitLow, limitHigh } = value;
+ const valid = {
+ min: true,
+ max: true,
+ limitLow: true,
+ limitHigh: true
+ };
+
+ if (min === '') {
+ valid.min = false;
+ }
+
+ if (max === '') {
+ valid.max = false;
+ }
+
+ if (max < min) {
+ valid.min = false;
+ valid.max = false;
+ }
+
+ if (limitLow !== '') {
+ valid.limitLow = min <= limitLow && limitLow < max;
+ }
+
+ if (limitHigh !== '') {
+ valid.limitHigh = min < limitHigh && limitHigh <= max;
+ }
+
+ if (valid.limitLow && valid.limitHigh
+ && limitLow !== '' && limitHigh !== ''
+ && limitLow > limitHigh) {
+ valid.limitLow = false;
+ valid.limitHigh = false;
+ }
+
+ if (callback) {
+ callback(valid);
+ }
+
+ return valid.min && valid.max && valid.limitLow && valid.limitHigh;
+ }
+ }
+ ]
+ });
+ };
+
+ function getGaugeFormController(openmct) {
+ return {
+ show(element, model, onChange) {
+ const rowComponent = new Vue({
+ el: element,
+ components: {
+ GaugeFormController
+ },
+ provide: {
+ openmct
+ },
+ data() {
+ return {
+ model,
+ onChange
+ };
+ },
+ template: `<GaugeFormController :model="model" @onChange="onChange"></GaugeFormController>`
+ });
+
+ return rowComponent;
+ }
+ };
+ }
+}
diff --git a/src/plugins/gauge/GaugePluginSpec.js b/src/plugins/gauge/GaugePluginSpec.js
new file mode 100644
index 000000000..601c2bc7f
--- /dev/null
+++ b/src/plugins/gauge/GaugePluginSpec.js
@@ -0,0 +1,807 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * 'License'); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+import {
+ createOpenMct,
+ resetApplicationState
+} from 'utils/testing';
+import { debounce } from 'lodash';
+
+import Vue from 'vue';
+
+let gaugeDomainObject = {
+ identifier: {
+ key: 'gauge',
+ namespace: 'test-namespace'
+ },
+ type: 'gauge',
+ composition: []
+};
+
+describe('Gauge plugin', () => {
+ let openmct;
+ let child;
+ let gaugeHolder;
+
+ beforeEach((done) => {
+ gaugeHolder = document.createElement('div');
+ gaugeHolder.style.display = 'block';
+ gaugeHolder.style.width = '1920px';
+ gaugeHolder.style.height = '1080px';
+
+ child = document.createElement('div');
+ gaugeHolder.appendChild(child);
+
+ openmct = createOpenMct();
+ openmct.on('start', done);
+
+ openmct.install(openmct.plugins.Gauge());
+
+ openmct.startHeadless();
+ });
+
+ afterEach(() => {
+ return resetApplicationState(openmct);
+ });
+
+ it('Plugin installed by default', () => {
+ const GaugeType = openmct.types.get('gauge');
+
+ expect(GaugeType).not.toBeNull();
+ expect(GaugeType.definition.name).toEqual('Gauge');
+ });
+
+ it('Gauge plugin is creatable', () => {
+ const GaugeType = openmct.types.get('gauge');
+
+ expect(GaugeType.definition.creatable).toBeTrue();
+ });
+
+ it('Gauge plugin is creatable', () => {
+ const GaugeType = openmct.types.get('gauge');
+
+ expect(GaugeType.definition.creatable).toBeTrue();
+ });
+
+ it('Gauge form controller', () => {
+ const gaugeController = openmct.forms.getFormControl('gauge-controller');
+ expect(gaugeController).toBeDefined();
+ });
+
+ describe('Gauge with Filled Dial', () => {
+ let gaugeViewProvider;
+ let gaugeView;
+ let gaugeViewObject;
+ let mutablegaugeObject;
+ let randomValue;
+
+ const minValue = -1;
+ const maxValue = 1;
+
+ beforeEach(() => {
+ randomValue = Math.random();
+ gaugeViewObject = {
+ ...gaugeDomainObject,
+ configuration: {
+ gaugeController: {
+ gaugeType: 'dial-filled',
+ isDisplayMinMax: true,
+ isDisplayCurVal: true,
+ isDisplayUnits: true,
+ isUseTelemetryLimits: false,
+ limitLow: -0.9,
+ limitHigh: 0.9,
+ max: maxValue,
+ min: minValue,
+ precision: 2
+ }
+ },
+ composition: [
+ {
+ namespace: 'test-namespace',
+ key: 'test-object'
+ }
+ ],
+ id: 'test-object',
+ name: 'gauge'
+ };
+
+ const testObjectProvider = jasmine.createSpyObj('testObjectProvider', [
+ 'get',
+ 'create',
+ 'update',
+ 'observe'
+ ]);
+
+ openmct.editor = {};
+ openmct.editor.isEditing = () => false;
+
+ const applicableViews = openmct.objectViews.get(gaugeViewObject, [gaugeViewObject]);
+ gaugeViewProvider = applicableViews.find(viewProvider => viewProvider.key === 'gauge');
+
+ testObjectProvider.get.and.returnValue(Promise.resolve(gaugeViewObject));
+ testObjectProvider.create.and.returnValue(Promise.resolve(gaugeViewObject));
+ openmct.objects.addProvider('test-namespace', testObjectProvider);
+ testObjectProvider.observe.and.returnValue(() => {});
+ testObjectProvider.create.and.returnValue(Promise.resolve(true));
+ testObjectProvider.update.and.returnValue(Promise.resolve(true));
+
+ spyOn(openmct.telemetry, 'getMetadata').and.returnValue({
+ valuesForHints: () => {
+ return [
+ {
+ source: 'sin'
+ }
+ ];
+ },
+ value: () => 1
+ });
+ spyOn(openmct.telemetry, 'getValueFormatter').and.returnValue({
+ parse: () => {
+ return 2000;
+ }
+ });
+ spyOn(openmct.telemetry, 'getFormatMap').and.returnValue({
+ sin: {
+ format: (datum) => {
+ return randomValue;
+ }
+ }
+ });
+ spyOn(openmct.telemetry, 'getLimits').and.returnValue({ limits: () => Promise.resolve() });
+ spyOn(openmct.telemetry, 'request').and.returnValue(Promise.resolve([randomValue]));
+ spyOn(openmct.time, 'bounds').and.returnValue({
+ start: 1000,
+ end: 5000
+ });
+
+ return openmct.objects.getMutable(gaugeViewObject.identifier).then((mutableObject) => {
+ mutablegaugeObject = mutableObject;
+ gaugeView = gaugeViewProvider.view(mutablegaugeObject);
+ gaugeView.show(child);
+
+ return Vue.nextTick();
+ });
+ });
+
+ afterEach(() => {
+ gaugeView.destroy();
+
+ return resetApplicationState(openmct);
+ });
+
+ it('provides gauge view', () => {
+ expect(gaugeViewProvider).toBeDefined();
+ });
+
+ it('renders gauge element', () => {
+ const gaugeElement = gaugeHolder.querySelectorAll('.js-gauge-wrapper');
+ expect(gaugeElement.length).toBe(1);
+ });
+
+ it('renders major elements', () => {
+ const wrapperElement = gaugeHolder.querySelector('.js-gauge-wrapper');
+ const rangeElement = gaugeHolder.querySelector('.js-gauge-dial-range');
+ const valueElement = gaugeHolder.querySelector('.js-dial-current-value');
+
+ const hasMajorElements = Boolean(wrapperElement && rangeElement && valueElement);
+
+ expect(hasMajorElements).toBe(true);
+ });
+
+ it('renders correct min max values', () => {
+ expect(gaugeHolder.querySelector('.js-gauge-dial-range').textContent).toEqual(`${minValue} ${maxValue}`);
+ });
+
+ it('renders correct current value', (done) => {
+ function WatchUpdateValue() {
+ const textElement = gaugeHolder.querySelector('.js-dial-current-value');
+ expect(Number(textElement.textContent).toFixed(gaugeViewObject.configuration.gaugeController.precision)).toBe(randomValue.toFixed(gaugeViewObject.configuration.gaugeController.precision));
+ done();
+ }
+
+ const debouncedWatchUpdate = debounce(WatchUpdateValue, 200);
+ Vue.nextTick(debouncedWatchUpdate);
+ });
+ });
+
+ describe('Gauge with Needle Dial', () => {
+ let gaugeViewProvider;
+ let gaugeView;
+ let gaugeViewObject;
+ let mutablegaugeObject;
+ let randomValue;
+
+ const minValue = -1;
+ const maxValue = 1;
+ beforeEach(() => {
+ randomValue = Math.random();
+ gaugeViewObject = {
+ ...gaugeDomainObject,
+ configuration: {
+ gaugeController: {
+ gaugeType: 'dial-needle',
+ isDisplayMinMax: true,
+ isDisplayCurVal: true,
+ isDisplayUnits: true,
+ isUseTelemetryLimits: false,
+ limitLow: -0.9,
+ limitHigh: 0.9,
+ max: maxValue,
+ min: minValue,
+ precision: 2
+ }
+ },
+ composition: [
+ {
+ namespace: 'test-namespace',
+ key: 'test-object'
+ }
+ ],
+ id: 'test-object',
+ name: 'gauge'
+ };
+
+ const testObjectProvider = jasmine.createSpyObj('testObjectProvider', [
+ 'get',
+ 'create',
+ 'update',
+ 'observe'
+ ]);
+
+ openmct.editor = {};
+ openmct.editor.isEditing = () => false;
+
+ const applicableViews = openmct.objectViews.get(gaugeViewObject, [gaugeViewObject]);
+ gaugeViewProvider = applicableViews.find(viewProvider => viewProvider.key === 'gauge');
+
+ testObjectProvider.get.and.returnValue(Promise.resolve(gaugeViewObject));
+ testObjectProvider.create.and.returnValue(Promise.resolve(gaugeViewObject));
+ openmct.objects.addProvider('test-namespace', testObjectProvider);
+ testObjectProvider.observe.and.returnValue(() => {});
+ testObjectProvider.create.and.returnValue(Promise.resolve(true));
+ testObjectProvider.update.and.returnValue(Promise.resolve(true));
+
+ spyOn(openmct.telemetry, 'getMetadata').and.returnValue({
+ valuesForHints: () => {
+ return [
+ {
+ source: 'sin'
+ }
+ ];
+ },
+ value: () => 1
+ });
+ spyOn(openmct.telemetry, 'getValueFormatter').and.returnValue({
+ parse: () => {
+ return 2000;
+ }
+ });
+ spyOn(openmct.telemetry, 'getFormatMap').and.returnValue({
+ sin: {
+ format: (datum) => {
+ return randomValue;
+ }
+ }
+ });
+ spyOn(openmct.telemetry, 'getLimits').and.returnValue({ limits: () => Promise.resolve() });
+ spyOn(openmct.telemetry, 'request').and.returnValue(Promise.resolve([randomValue]));
+ spyOn(openmct.time, 'bounds').and.returnValue({
+ start: 1000,
+ end: 5000
+ });
+
+ return openmct.objects.getMutable(gaugeViewObject.identifier).then((mutableObject) => {
+ mutablegaugeObject = mutableObject;
+ gaugeView = gaugeViewProvider.view(mutablegaugeObject);
+ gaugeView.show(child);
+
+ return Vue.nextTick();
+ });
+ });
+
+ afterEach(() => {
+ gaugeView.destroy();
+
+ return resetApplicationState(openmct);
+ });
+
+ it('provides gauge view', () => {
+ expect(gaugeViewProvider).toBeDefined();
+ });
+
+ it('renders gauge element', () => {
+ const gaugeElement = gaugeHolder.querySelectorAll('.js-gauge-wrapper');
+ expect(gaugeElement.length).toBe(1);
+ });
+
+ it('renders major elements', () => {
+ const wrapperElement = gaugeHolder.querySelector('.js-gauge-wrapper');
+ const rangeElement = gaugeHolder.querySelector('.js-gauge-dial-range');
+ const valueElement = gaugeHolder.querySelector('.js-dial-current-value');
+
+ const hasMajorElements = Boolean(wrapperElement && rangeElement && valueElement);
+
+ expect(hasMajorElements).toBe(true);
+ });
+
+ it('renders correct min max values', () => {
+ expect(gaugeHolder.querySelector('.js-gauge-dial-range').textContent).toEqual(`${minValue} ${maxValue}`);
+ });
+
+ it('renders correct current value', (done) => {
+ function WatchUpdateValue() {
+ const textElement = gaugeHolder.querySelector('.js-dial-current-value');
+ expect(Number(textElement.textContent).toFixed(gaugeViewObject.configuration.gaugeController.precision)).toBe(randomValue.toFixed(gaugeViewObject.configuration.gaugeController.precision));
+ done();
+ }
+
+ const debouncedWatchUpdate = debounce(WatchUpdateValue, 200);
+ Vue.nextTick(debouncedWatchUpdate);
+ });
+ });
+
+ describe('Gauge with Vertical Meter', () => {
+ let gaugeViewProvider;
+ let gaugeView;
+ let gaugeViewObject;
+ let mutablegaugeObject;
+ let randomValue;
+
+ const minValue = -1;
+ const maxValue = 1;
+ beforeEach(() => {
+ randomValue = Math.random();
+ gaugeViewObject = {
+ ...gaugeDomainObject,
+ configuration: {
+ gaugeController: {
+ gaugeType: 'meter-vertical',
+ isDisplayMinMax: true,
+ isDisplayCurVal: true,
+ isDisplayUnits: true,
+ isUseTelemetryLimits: false,
+ limitLow: -0.9,
+ limitHigh: 0.9,
+ max: maxValue,
+ min: minValue,
+ precision: 2
+ }
+ },
+ composition: [
+ {
+ namespace: 'test-namespace',
+ key: 'test-object'
+ }
+ ],
+ id: 'test-object',
+ name: 'gauge'
+ };
+
+ const testObjectProvider = jasmine.createSpyObj('testObjectProvider', [
+ 'get',
+ 'create',
+ 'update',
+ 'observe'
+ ]);
+
+ openmct.editor = {};
+ openmct.editor.isEditing = () => false;
+
+ const applicableViews = openmct.objectViews.get(gaugeViewObject, [gaugeViewObject]);
+ gaugeViewProvider = applicableViews.find(viewProvider => viewProvider.key === 'gauge');
+
+ testObjectProvider.get.and.returnValue(Promise.resolve(gaugeViewObject));
+ testObjectProvider.create.and.returnValue(Promise.resolve(gaugeViewObject));
+ openmct.objects.addProvider('test-namespace', testObjectProvider);
+ testObjectProvider.observe.and.returnValue(() => {});
+ testObjectProvider.create.and.returnValue(Promise.resolve(true));
+ testObjectProvider.update.and.returnValue(Promise.resolve(true));
+
+ spyOn(openmct.telemetry, 'getMetadata').and.returnValue({
+ valuesForHints: () => {
+ return [
+ {
+ source: 'sin'
+ }
+ ];
+ },
+ value: () => 1
+ });
+ spyOn(openmct.telemetry, 'getValueFormatter').and.returnValue({
+ parse: () => {
+ return 2000;
+ }
+ });
+ spyOn(openmct.telemetry, 'getFormatMap').and.returnValue({
+ sin: {
+ format: (datum) => {
+ return randomValue;
+ }
+ }
+ });
+ spyOn(openmct.telemetry, 'getLimits').and.returnValue({ limits: () => Promise.resolve() });
+ spyOn(openmct.telemetry, 'request').and.returnValue(Promise.resolve([randomValue]));
+ spyOn(openmct.time, 'bounds').and.returnValue({
+ start: 1000,
+ end: 5000
+ });
+
+ return openmct.objects.getMutable(gaugeViewObject.identifier).then((mutableObject) => {
+ mutablegaugeObject = mutableObject;
+ gaugeView = gaugeViewProvider.view(mutablegaugeObject);
+ gaugeView.show(child);
+
+ return Vue.nextTick();
+ });
+ });
+
+ afterEach(() => {
+ gaugeView.destroy();
+
+ return resetApplicationState(openmct);
+ });
+
+ it('provides gauge view', () => {
+ expect(gaugeViewProvider).toBeDefined();
+ });
+
+ it('renders gauge element', () => {
+ const gaugeElement = gaugeHolder.querySelectorAll('.js-gauge-wrapper');
+ expect(gaugeElement.length).toBe(1);
+ });
+
+ it('renders major elements', () => {
+ const wrapperElement = gaugeHolder.querySelector('.js-gauge-wrapper');
+ const rangeElement = gaugeHolder.querySelector('.js-gauge-meter-range');
+ const valueElement = gaugeHolder.querySelector('.js-gauge-current-value');
+
+ const hasMajorElements = Boolean(wrapperElement && rangeElement && valueElement);
+
+ expect(hasMajorElements).toBe(true);
+ });
+
+ it('renders correct min max values', () => {
+ expect(gaugeHolder.querySelector('.js-gauge-meter-range').textContent).toEqual(`${maxValue} ${minValue}`);
+ });
+
+ it('renders correct current value', (done) => {
+ function WatchUpdateValue() {
+ const textElement = gaugeHolder.querySelector('.js-gauge-current-value');
+ expect(Number(textElement.textContent).toFixed(gaugeViewObject.configuration.gaugeController.precision)).toBe(randomValue.toFixed(gaugeViewObject.configuration.gaugeController.precision));
+ done();
+ }
+
+ const debouncedWatchUpdate = debounce(WatchUpdateValue, 200);
+ Vue.nextTick(debouncedWatchUpdate);
+ });
+ });
+
+ describe('Gauge with Vertical Meter Inverted', () => {
+ let gaugeViewProvider;
+ let gaugeView;
+ let gaugeViewObject;
+ let mutablegaugeObject;
+
+ beforeEach(() => {
+ gaugeViewObject = {
+ ...gaugeDomainObject,
+ configuration: {
+ gaugeController: {
+ gaugeType: 'meter-vertical',
+ isDisplayMinMax: true,
+ isDisplayCurVal: true,
+ isDisplayUnits: true,
+ isUseTelemetryLimits: false,
+ limitLow: -0.9,
+ limitHigh: 0.9,
+ max: 1,
+ min: -1,
+ precision: 2
+ }
+ },
+ id: 'test-object',
+ name: 'gauge'
+ };
+
+ const testObjectProvider = jasmine.createSpyObj('testObjectProvider', [
+ 'get',
+ 'create',
+ 'update',
+ 'observe'
+ ]);
+
+ openmct.editor = {};
+ openmct.editor.isEditing = () => false;
+
+ const applicableViews = openmct.objectViews.get(gaugeViewObject, [gaugeViewObject]);
+ gaugeViewProvider = applicableViews.find(viewProvider => viewProvider.key === 'gauge');
+
+ testObjectProvider.get.and.returnValue(Promise.resolve(gaugeViewObject));
+ testObjectProvider.create.and.returnValue(Promise.resolve(gaugeViewObject));
+ openmct.objects.addProvider('test-namespace', testObjectProvider);
+ testObjectProvider.observe.and.returnValue(() => {});
+ testObjectProvider.create.and.returnValue(Promise.resolve(true));
+ testObjectProvider.update.and.returnValue(Promise.resolve(true));
+
+ return openmct.objects.getMutable(gaugeViewObject.identifier).then((mutableObject) => {
+ mutablegaugeObject = mutableObject;
+
+ gaugeView = gaugeViewProvider.view(mutablegaugeObject);
+ gaugeView.show(child);
+
+ return Vue.nextTick();
+ });
+ });
+
+ afterEach(() => {
+ gaugeView.destroy();
+
+ return resetApplicationState(openmct);
+ });
+
+ it('provides gauge view', () => {
+ expect(gaugeViewProvider).toBeDefined();
+ });
+
+ it('renders gauge element', () => {
+ const gaugeElement = gaugeHolder.querySelectorAll('.js-gauge-wrapper');
+ expect(gaugeElement.length).toBe(1);
+ });
+
+ it('renders major elements', () => {
+ const wrapperElement = gaugeHolder.querySelector('.js-gauge-wrapper');
+ const rangeElement = gaugeHolder.querySelector('.js-gauge-meter-range');
+ const valueElement = gaugeHolder.querySelector('.js-gauge-current-value');
+
+ const hasMajorElements = Boolean(wrapperElement && rangeElement && valueElement);
+
+ expect(hasMajorElements).toBe(true);
+ });
+ });
+
+ describe('Gauge with Horizontal Meter', () => {
+ let gaugeViewProvider;
+ let gaugeView;
+ let gaugeViewObject;
+ let mutablegaugeObject;
+
+ beforeEach(() => {
+ gaugeViewObject = {
+ ...gaugeDomainObject,
+ configuration: {
+ gaugeController: {
+ gaugeType: 'meter-vertical',
+ isDisplayMinMax: true,
+ isDisplayCurVal: true,
+ isDisplayUnits: true,
+ isUseTelemetryLimits: false,
+ limitLow: -0.9,
+ limitHigh: 0.9,
+ max: 1,
+ min: -1,
+ precision: 2
+ }
+ },
+ id: 'test-object',
+ name: 'gauge'
+ };
+
+ const testObjectProvider = jasmine.createSpyObj('testObjectProvider', [
+ 'get',
+ 'create',
+ 'update',
+ 'observe'
+ ]);
+
+ openmct.editor = {};
+ openmct.editor.isEditing = () => false;
+
+ const applicableViews = openmct.objectViews.get(gaugeViewObject, [gaugeViewObject]);
+ gaugeViewProvider = applicableViews.find(viewProvider => viewProvider.key === 'gauge');
+
+ testObjectProvider.get.and.returnValue(Promise.resolve(gaugeViewObject));
+ testObjectProvider.create.and.returnValue(Promise.resolve(gaugeViewObject));
+ openmct.objects.addProvider('test-namespace', testObjectProvider);
+ testObjectProvider.observe.and.returnValue(() => {});
+ testObjectProvider.create.and.returnValue(Promise.resolve(true));
+ testObjectProvider.update.and.returnValue(Promise.resolve(true));
+
+ return openmct.objects.getMutable(gaugeViewObject.identifier).then((mutableObject) => {
+ mutablegaugeObject = mutableObject;
+
+ gaugeView = gaugeViewProvider.view(mutablegaugeObject);
+ gaugeView.show(child);
+
+ return Vue.nextTick();
+ });
+ });
+
+ afterEach(() => {
+ gaugeView.destroy();
+
+ return resetApplicationState(openmct);
+ });
+
+ it('provides gauge view', () => {
+ expect(gaugeViewProvider).toBeDefined();
+ });
+
+ it('renders gauge element', () => {
+ const gaugeElement = gaugeHolder.querySelectorAll('.js-gauge-wrapper');
+ expect(gaugeElement.length).toBe(1);
+ });
+
+ it('renders major elements', () => {
+ const wrapperElement = gaugeHolder.querySelector('.js-gauge-wrapper');
+ const rangeElement = gaugeHolder.querySelector('.c-gauge__range');
+ const curveElement = gaugeHolder.querySelector('.c-meter');
+
+ const hasMajorElements = Boolean(wrapperElement && rangeElement && curveElement);
+
+ expect(hasMajorElements).toBe(true);
+ });
+ });
+
+ describe('Gauge with Filled Dial with Use Telemetry Limits', () => {
+ let gaugeViewProvider;
+ let gaugeView;
+ let gaugeViewObject;
+ let mutablegaugeObject;
+ let randomValue;
+
+ beforeEach(() => {
+ randomValue = Math.random();
+
+ gaugeViewObject = {
+ ...gaugeDomainObject,
+ configuration: {
+ gaugeController: {
+ gaugeType: 'dial-filled',
+ isDisplayMinMax: true,
+ isDisplayCurVal: true,
+ isDisplayUnits: true,
+ isUseTelemetryLimits: true,
+ limitLow: 10,
+ limitHigh: 90,
+ max: 100,
+ min: 0,
+ precision: 2
+ }
+ },
+ composition: [
+ {
+ namespace: 'test-namespace',
+ key: 'test-object'
+ }
+ ],
+ id: 'test-object',
+ name: 'gauge'
+ };
+
+ const testObjectProvider = jasmine.createSpyObj('testObjectProvider', [
+ 'get',
+ 'create',
+ 'update',
+ 'observe'
+ ]);
+
+ openmct.editor = {};
+ openmct.editor.isEditing = () => false;
+
+ const applicableViews = openmct.objectViews.get(gaugeViewObject, [gaugeViewObject]);
+ gaugeViewProvider = applicableViews.find(viewProvider => viewProvider.key === 'gauge');
+
+ testObjectProvider.get.and.returnValue(Promise.resolve(gaugeViewObject));
+ testObjectProvider.create.and.returnValue(Promise.resolve(gaugeViewObject));
+ openmct.objects.addProvider('test-namespace', testObjectProvider);
+ testObjectProvider.observe.and.returnValue(() => {});
+ testObjectProvider.create.and.returnValue(Promise.resolve(true));
+ testObjectProvider.update.and.returnValue(Promise.resolve(true));
+
+ spyOn(openmct.telemetry, 'getMetadata').and.returnValue({
+ valuesForHints: () => {
+ return [
+ {
+ source: 'sin'
+ }
+ ];
+ },
+ value: () => 1
+ });
+ spyOn(openmct.telemetry, 'getValueFormatter').and.returnValue({
+ parse: () => {
+ return 2000;
+ }
+ });
+ spyOn(openmct.telemetry, 'getFormatMap').and.returnValue({
+ sin: {
+ format: (datum) => {
+ return randomValue;
+ }
+ }
+ });
+ spyOn(openmct.telemetry, 'getLimits').and.returnValue(
+ {
+ limits: () => Promise.resolve({
+ CRITICAL: {
+ high: 0.99,
+ low: -0.99
+ }
+ })
+ }
+ );
+ spyOn(openmct.telemetry, 'request').and.returnValue(Promise.resolve([randomValue]));
+ spyOn(openmct.time, 'bounds').and.returnValue({
+ start: 1000,
+ end: 5000
+ });
+
+ return openmct.objects.getMutable(gaugeViewObject.identifier).then((mutableObject) => {
+ mutablegaugeObject = mutableObject;
+ gaugeView = gaugeViewProvider.view(mutablegaugeObject);
+ gaugeView.show(child);
+
+ return Vue.nextTick();
+ });
+ });
+
+ afterEach(() => {
+ gaugeView.destroy();
+
+ return resetApplicationState(openmct);
+ });
+
+ it('provides gauge view', () => {
+ expect(gaugeViewProvider).toBeDefined();
+ });
+
+ it('renders gauge element', () => {
+ const gaugeElement = gaugeHolder.querySelectorAll('.js-gauge-wrapper');
+ expect(gaugeElement.length).toBe(1);
+ });
+
+ it('renders major elements', () => {
+ const wrapperElement = gaugeHolder.querySelector('.js-gauge-wrapper');
+ const rangeElement = gaugeHolder.querySelector('.js-gauge-dial-range');
+ const valueElement = gaugeHolder.querySelector('.js-dial-current-value');
+
+ const hasMajorElements = Boolean(wrapperElement && rangeElement && valueElement);
+
+ expect(hasMajorElements).toBe(true);
+ });
+
+ it('renders correct min max values', () => {
+ expect(gaugeHolder.querySelector('.js-gauge-dial-range').textContent).toEqual(`${gaugeViewObject.configuration.gaugeController.min} ${gaugeViewObject.configuration.gaugeController.max}`);
+ });
+
+ it('renders correct current value', (done) => {
+ function WatchUpdateValue() {
+ const textElement = gaugeHolder.querySelector('.js-dial-current-value');
+ expect(Number(textElement.textContent).toFixed(gaugeViewObject.configuration.gaugeController.precision)).toBe(randomValue.toFixed(gaugeViewObject.configuration.gaugeController.precision));
+ done();
+ }
+
+ const debouncedWatchUpdate = debounce(WatchUpdateValue, 200);
+ Vue.nextTick(debouncedWatchUpdate);
+ });
+ });
+});
diff --git a/src/plugins/gauge/GaugeViewProvider.js b/src/plugins/gauge/GaugeViewProvider.js
new file mode 100644
index 000000000..dcd2ab7d1
--- /dev/null
+++ b/src/plugins/gauge/GaugeViewProvider.js
@@ -0,0 +1,67 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+import GaugeComponent from './components/Gauge.vue';
+import Vue from 'vue';
+
+export default function GaugeViewProvider(openmct) {
+ return {
+ key: 'gauge',
+ name: 'Gauge',
+ cssClass: 'icon-gauge',
+ canView: function (domainObject) {
+ return domainObject.type === 'gauge';
+ },
+ canEdit: function (domainObject) {
+ if (domainObject.type === 'gauge') {
+ return true;
+ }
+ },
+ view: function (domainObject) {
+ let component;
+
+ return {
+ show: function (element) {
+ component = new Vue({
+ el: element,
+ components: {
+ GaugeComponent
+ },
+ provide: {
+ openmct,
+ domainObject,
+ composition: openmct.composition.get(domainObject)
+ },
+ template: '<gauge-component></gauge-component>'
+ });
+ },
+ destroy: function (element) {
+ component.$destroy();
+ component = undefined;
+ }
+ };
+ },
+ priority: function () {
+ return 1;
+ }
+ };
+}
diff --git a/src/plugins/gauge/components/Gauge.vue b/src/plugins/gauge/components/Gauge.vue
new file mode 100644
index 000000000..2d250158e
--- /dev/null
+++ b/src/plugins/gauge/components/Gauge.vue
@@ -0,0 +1,732 @@
+/*****************************************************************************
+* Open MCT, Copyright (c) 2014-2022, United States Government
+* as represented by the Administrator of the National Aeronautics and Space
+* Administration. All rights reserved.
+*
+* Open MCT is licensed under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+* http://www.apache.org/licenses/LICENSE-2.0.
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+* License for the specific language governing permissions and limitations
+* under the License.
+*
+* Open MCT includes source code licensed under additional open source
+* licenses. See the Open Source Licenses file (LICENSES.md) included with
+* this source code distribution or the Licensing information page available
+* at runtime from the About dialog for additional information.
+*****************************************************************************/
+<template>
+<div
+ class="c-gauge__wrapper js-gauge-wrapper"
+ :class="`c-gauge--${gaugeType}`"
+ :title="gaugeTitle"
+>
+ <template v-if="typeDial">
+ <svg
+ class="c-gauge c-dial"
+ viewBox="0 0 10 10"
+ >
+ <g class="c-dial__masks">
+ <mask id="gaugeValueMask">
+ <path
+ d="M1.8926 8.1074C1.09734 7.31215 0.605469 6.21352 0.605469 5C0.605469 2.57297 2.57297 0.605469 5 0.605469C7.42703 0.605469 9.39453 2.57297 9.39453 5C9.39453 6.21352 8.90266 7.31215 8.1074 8.1074L7.14066 7.14066C7.6885 6.59281 8.02734 5.83598 8.02734 5C8.02734 3.32804 6.67196 1.97266 5 1.97266C3.32804 1.97266 1.97266 3.32804 1.97266 5C1.97266 5.83598 2.3115 6.59281 2.85934 7.14066L1.8926 8.1074Z"
+ fill="white"
+ />
+ </mask>
+ <mask id="gaugeBgMask">
+ <path
+ d="M8.53553 8.53553C9.44036 7.63071 10 6.38071 10 5C10 2.23858 7.76142 0 5 0C2.23858 0 0 2.23858 0 5C0 6.38071 0.559644 7.63071 1.46447 8.53553L2.85934 7.14066C2.3115 6.59281 1.97266 5.83598 1.97266 5C1.97266 3.32804 3.32804 1.97266 5 1.97266C6.67196 1.97266 8.02734 3.32804 8.02734 5C8.02734 5.83598 7.6885 6.59281 7.14066 7.14066L8.53553 8.53553Z"
+ fill="white"
+ />
+ </mask>
+ </g>
+
+ <g
+ class="c-dial__graphics"
+ mask="url(#gaugeBgMask)"
+ >
+ <rect
+ class="c-dial__bg"
+ x="0"
+ y="0"
+ width="10"
+ height="10"
+ />
+ <g
+ v-if="isDialLowLimit"
+ class="c-dial__limit-low"
+ :style="`transform: rotate(${dialLowLimitDeg}deg)`"
+ >
+ <rect
+ v-if="isDialLowLimitLow"
+ class="c-dial__low-limit__low"
+ x="5"
+ y="5"
+ width="5"
+ height="5"
+ />
+ <rect
+ v-if="isDialLowLimitMid"
+ class="c-dial__low-limit__mid"
+ x="5"
+ y="0"
+ width="5"
+ height="5"
+ />
+ <rect
+ v-if="isDialLowLimitHigh"
+ class="c-dial__low-limit__high"
+ x="0"
+ y="0"
+ width="5"
+ height="5"
+ />
+ </g>
+ <g
+ v-if="isDialHighLimit"
+ class="c-dial__limit-high"
+ :style="`transform: rotate(${dialHighLimitDeg}deg)`"
+ >
+ <rect
+ v-if="isDialHighLimitLow"
+ class="c-dial__high-limit__low"
+ x="0"
+ y="5"
+ width="5"
+ height="5"
+ />
+ <rect
+ v-if="isDialHighLimitMid"
+ class="c-dial__high-limit__mid"
+ x="0"
+ y="0"
+ width="5"
+ height="5"
+ />
+ <rect
+ v-if="isDialHighLimitHigh"
+ class="c-dial__high-limit__high"
+ x="5"
+ y="0"
+ width="5"
+ height="5"
+ />
+ </g>
+ </g>
+
+ <g
+ class="c-dial__graphics"
+ mask="url(#gaugeValueMask)"
+ >
+ <g
+ v-if="typeFilledDial"
+ class="c-dial__filled-value"
+ :style="`transform: rotate(${degValueFilledDial}deg)`"
+ >
+ <rect
+ v-if="isDialFilledValueLow"
+ class="c-dial__filled-value__low"
+ x="5"
+ y="5"
+ width="5"
+ height="5"
+ />
+ <rect
+ v-if="isDialFilledValueMid"
+ class="c-dial__filled-value__mid"
+ x="5"
+ y="0"
+ width="5"
+ height="5"
+ />
+ <rect
+ v-if="isDialFilledValueHigh"
+ class="c-dial__filled-value__high"
+ x="0"
+ y="0"
+ width="5"
+ height="5"
+ />
+ </g>
+ <g
+ v-if="valueInBounds && typeNeedleDial"
+ class="c-dial__needle-value"
+ :style="`transform: rotate(${degValue}deg)`"
+ >
+ <path d="M4.90234 9.39453L5.09766 9.39453L5.30146 8.20874C6.93993 8.05674 8.22265 6.67817 8.22266 5C8.22266 3.22018 6.77982 1.77734 5 1.77734C3.22018 1.77734 1.77734 3.22018 1.77734 5C1.77734 6.67817 3.06007 8.05674 4.69854 8.20874L4.90234 9.39453Z" />
+ </g>
+ <path
+ id="dialTextPath"
+ class="c-dial__range-msg-path"
+ d="M8.3501 5.0001C8.3501 6.85025 6.85025 8.3501 5.0001 8.3501C3.14994 8.3501 1.6501 6.85025 1.6501 5.0001C1.6501 3.14994 3.14994 1.6501 5.0001 1.6501C6.85025 1.6501 8.3501 3.14994 8.3501 5.0001Z"
+ fill="none"
+ style="transform-origin: center; transform: rotate(182deg)"
+ />
+ </g>
+ <g class="c-dial__text">
+ <text
+ v-if="displayUnits"
+ x="50%"
+ y="70%"
+ text-anchor="middle"
+ class="c-gauge__units"
+ font-size="8%"
+ >{{ units }}</text>
+
+ <g
+ v-if="displayMinMax"
+ class="c-dial__range-text js-gauge-dial-range"
+ :font-size="rangeFontSize"
+ >
+ <text
+ transform="translate(1.5 8.7) rotate(-45)"
+ dominant-baseline="hanging"
+ >{{ rangeLow }}</text>
+ <text
+ transform="translate(8.4 8.7) rotate(45)"
+ dominant-baseline="hanging"
+ text-anchor="end"
+ >{{ rangeHigh }}</text>
+ </g>
+ </g>
+
+ <svg
+ v-if="!valueInBounds && valueExpected"
+ xmlns="http://www.w3.org/2000/svg"
+ viewBox="0 0 512 512"
+ xml:space="preserve"
+ class="c-dial__value-oor-indicator"
+ x="45%"
+ y="80%"
+ width="1"
+ height="1"
+ ><path
+ d="M448 0H64C28.7.1.1 28.7 0 64v384c.1 35.3 28.7 63.9 64 64h384c35.3-.1 63.9-28.7 64-64V64c-.1-35.3-28.7-63.9-64-64zM288 448h-64v-64h64v64zm10.9-192L280 352h-48l-18.9-96V64H299v192h-.1z"
+ /></svg>
+
+ <svg
+ class="c-gauge__current-value-text-wrapper"
+ :viewBox="curValViewBox"
+ preserveAspectRatio="xMidYMid meet"
+ >
+ <rect
+ class="svg-viewbox-debug"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%"
+ />
+ <text
+ class="c-dial__current-value-text js-dial-current-value"
+ font-size="3.5"
+ lengthAdjust="spacing"
+ text-anchor="middle"
+ dominant-baseline="middle"
+ x="50%"
+ y="50%"
+ >
+ <template v-if="displayCurVal">
+ <tspan>{{ curVal }}</tspan>
+ </template>
+ </text>
+ </svg>
+
+ </svg>
+ </template>
+
+ <template v-if="typeMeter">
+ <div class="c-meter">
+ <div
+ v-if="displayMinMax"
+ class="c-gauge__range c-meter__range js-gauge-meter-range"
+ >
+ <div class="c-meter__range__high">{{ rangeHigh }}</div>
+ <div class="c-meter__range__low">{{ rangeLow }}</div>
+ </div>
+ <div class="c-meter__bg">
+ <div
+ v-if="!valueInBounds && valueExpected"
+ class="c-meter__value-oor-indicator"
+ ><svg
+ xmlns="http://www.w3.org/2000/svg"
+ viewBox="0 0 512 512"
+ :preserveAspectRatio="meterOutOfRangeIndicatorAspectRatio"
+ ><path
+ d="M448 0H64C28.7.1.1 28.7 0 64v384c.1 35.3 28.7 63.9 64 64h384c35.3-.1 63.9-28.7 64-64V64c-.1-35.3-28.7-63.9-64-64zM288 448h-64v-64h64v64zm10.9-192L280 352h-48l-18.9-96V64H299v192h-.1z"
+ /></svg></div>
+
+ <template v-if="typeMeterVertical">
+ <div
+ v-if="valueExpected"
+ class="c-meter__value"
+ :class="{'c-meter__value-needle' : typeNeedleMeter }"
+ :style="`transform: translateY(${meterValueToPerc}%)`"
+ ></div>
+
+ <div
+ v-if="isMeterLimitHigh"
+ class="c-meter__limit-high"
+ :style="`height: ${meterHighLimitPerc}%`"
+ ></div>
+
+ <div
+ v-if="isMeterLimitLow"
+ class="c-meter__limit-low"
+ :style="`height: ${meterLowLimitPerc}%`"
+ ></div>
+ </template>
+
+ <template v-if="typeMeterHorizontal">
+ <div
+ v-if="valueExpected"
+ class="c-meter__value"
+ :class="{'c-meter__value-needle' : typeNeedleMeter }"
+ :style="`transform: translateX(${meterValueToPerc * -1}%)`"
+ ></div>
+
+ <div
+ v-if="isMeterLimitHigh"
+ class="c-meter__limit-high"
+ :style="`width: ${meterHighLimitPerc}%`"
+ ></div>
+
+ <div
+ v-if="isMeterLimitLow"
+ class="c-meter__limit-low"
+ :style="`width: ${meterLowLimitPerc}%`"
+ ></div>
+ </template>
+
+ <svg
+ class="c-gauge__current-value-text-wrapper"
+ :viewBox="curValViewBox"
+ preserveAspectRatio="xMidYMid meet"
+ >
+ <rect
+ class="svg-viewbox-debug"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%"
+ />
+ <text
+ class="c-meter__current-value-text js-gauge-current-value"
+ font-size="4"
+ lengthAdjust="spacing"
+ text-anchor="middle"
+ :dominant-baseline="meterTextBaseline"
+ x="50%"
+ y="50%"
+ >
+ <template v-if="displayCurVal">
+ <tspan>{{ curVal }}</tspan>
+ <tspan
+ v-if="typeMeterHorizontal && displayUnits"
+ class="c-gauge__units"
+ font-size="80%"
+ >{{ units }}</tspan>
+ <tspan
+ v-if="typeMeterVertical && displayUnits"
+ x="50%"
+ dy="3.5"
+ class="c-gauge__units"
+ font-size="80%"
+ >{{ units }}</tspan>
+ </template>
+ </text>
+ </svg>
+ </div>
+ </div>
+ </template>
+</div>
+</template>
+
+<script>
+import { DIAL_VALUE_DEG_OFFSET, getLimitDegree } from '../gauge-limit-util';
+
+const LIMIT_PADDING_IN_PERCENT = 10;
+const DEFAULT_CURRENT_VALUE = '--';
+
+export default {
+ name: 'Gauge',
+ inject: ['openmct', 'domainObject', 'composition'],
+ data() {
+ let gaugeController = this.domainObject.configuration.gaugeController;
+
+ return {
+ curVal: DEFAULT_CURRENT_VALUE,
+ digits: 3,
+ precision: gaugeController.precision,
+ displayMinMax: gaugeController.isDisplayMinMax,
+ displayCurVal: gaugeController.isDisplayCurVal,
+ displayUnits: gaugeController.isDisplayUnits,
+ limitHigh: gaugeController.limitHigh,
+ limitLow: gaugeController.limitLow,
+ rangeHigh: gaugeController.max,
+ rangeLow: gaugeController.min,
+ gaugeType: gaugeController.gaugeType,
+ showUnits: gaugeController.showUnits,
+ activeTimeSystem: this.openmct.time.timeSystem(),
+ units: ''
+ };
+ },
+ computed: {
+ degValue() {
+ return this.percentToDegrees(this.valToPercent(this.curVal));
+ },
+ degValueFilledDial() {
+ if (this.curVal > this.rangeHigh) {
+ return this.percentToDegrees(100);
+ }
+
+ return this.percentToDegrees(this.valToPercent(this.curVal));
+ },
+ dialHighLimitDeg() {
+ return this.percentToDegrees(this.valToPercent(this.limitHigh));
+ },
+ dialLowLimitDeg() {
+ return this.percentToDegrees(this.valToPercent(this.limitLow));
+ },
+ meterOutOfRangeIndicatorAspectRatio() {
+ return this.typeMeterVertical ? 'xMidYMax meet' : 'xMinYMid meet';
+ },
+ meterTextBaseline() {
+ return this.typeMeterVertical ? 'auto' : 'middle';
+ },
+ curValViewBox() {
+ const DIGITS_RATIO = 3;
+ const VIEWBOX_STR = '0 0 X 10';
+
+ return VIEWBOX_STR.replace('X', this.digits * DIGITS_RATIO);
+ },
+ rangeFontSize() {
+ const CHAR_THRESHOLD = 3;
+ const START_PERC = 8.5;
+ const REDUCE_PERC = 0.8;
+ const RANGE_CHARS_MAX = Math.max(this.rangeLow.toString().length, this.rangeHigh.toString().length);
+
+ return this.fontSizeFromChars(RANGE_CHARS_MAX, CHAR_THRESHOLD, START_PERC, REDUCE_PERC);
+ },
+ isDialLowLimit() {
+ return this.limitLow.toString().length > 0 && this.dialLowLimitDeg < getLimitDegree('low', 'max');
+ },
+ isDialLowLimitLow() {
+ return this.dialLowLimitDeg >= getLimitDegree('low', 'q1');
+ },
+ isDialLowLimitMid() {
+ return this.dialLowLimitDeg >= getLimitDegree('low', 'q2');
+ },
+ isDialLowLimitHigh() {
+ return this.dialLowLimitDeg >= getLimitDegree('low', 'q3');
+ },
+ isDialHighLimit() {
+ return this.limitHigh.toString().length > 0 && this.dialHighLimitDeg < getLimitDegree('high', 'max');
+ },
+ isDialHighLimitLow() {
+ return this.dialHighLimitDeg <= getLimitDegree('high', 'max');
+ },
+ isDialHighLimitMid() {
+ return this.dialHighLimitDeg <= getLimitDegree('high', 'q2');
+ },
+ isDialHighLimitHigh() {
+ return this.dialHighLimitDeg <= getLimitDegree('high', 'q3');
+ },
+ isDialFilledValueLow() {
+ return this.degValue >= getLimitDegree('low', 'q1');
+ },
+ isDialFilledValueMid() {
+ return this.degValue >= getLimitDegree('low', 'q2');
+ },
+ isDialFilledValueHigh() {
+ return this.degValue >= getLimitDegree('low', 'q3');
+ },
+ isMeterLimitHigh() {
+ return this.limitHigh.toString().length > 0 && this.meterHighLimitPerc > 0;
+ },
+ isMeterLimitLow() {
+ return this.limitLow.toString().length > 0 && this.meterLowLimitPerc > 0;
+ },
+ gaugeTitle() {
+ return this.valueInBounds ? 'Gauge' : 'Value is currently out of range and cannot be graphically displayed';
+ },
+ typeDial() {
+ return this.matchGaugeType('dial');
+ },
+ typeFilledDial() {
+ return this.matchGaugeType('dial-filled');
+ },
+ typeNeedleDial() {
+ return this.matchGaugeType('dial-needle');
+ },
+ typeMeter() {
+ return this.matchGaugeType('meter');
+ },
+ typeMeterHorizontal() {
+ return this.matchGaugeType('horizontal');
+ },
+ typeMeterVertical() {
+ return this.matchGaugeType('vertical');
+ },
+ typeMeterInverted() {
+ return this.matchGaugeType('inverted');
+ },
+ typeFilledMeter() {
+ return true; // Stubbing in for future capability
+ },
+ typeNeedleMeter() {
+ return false; // Stubbing in for future capability
+ },
+ meterValueToPerc() {
+ const meterDirection = (this.typeMeterInverted) ? -1 : 1;
+
+ if (this.typeFilledMeter) {
+ // Filled meter is a filled rectangle that is transformed along a vertical or horizontal axis
+ // So never move it below the low range more than 100%, or above the high range more than 0%
+ if (this.curVal <= this.rangeLow) {
+ return meterDirection * 100;
+ }
+
+ if (this.curVal >= this.rangeHigh) {
+ return 0;
+ }
+ }
+
+ return this.valToPercentMeter(this.curVal) * meterDirection;
+ },
+ meterHighLimitPerc() {
+ return this.valToPercentMeter(this.limitHigh);
+ },
+ meterLowLimitPerc() {
+ return 100 - this.valToPercentMeter(this.limitLow);
+ },
+ valueExpected() {
+ if (this.curVal === undefined || Object.is(this.curVal, 'null')) {
+ return false;
+ }
+
+ return this.curVal.toString().indexOf(DEFAULT_CURRENT_VALUE) === -1;
+ },
+ valueInBounds() {
+ return (this.curVal >= this.rangeLow && this.curVal <= this.rangeHigh);
+ },
+ timeFormatter() {
+ const timeSystem = this.activeTimeSystem;
+ const metadataValue = this.metadata.value(timeSystem.key) || { format: timeSystem.key };
+
+ return this.openmct.telemetry.getValueFormatter(metadataValue);
+ }
+ },
+ watch: {
+ curVal(newCurValue) {
+ if (this.digits < newCurValue.toString().length) {
+ this.digits = newCurValue.toString().length;
+ }
+ }
+ },
+ mounted() {
+ this.composition.on('add', this.addedToComposition);
+ this.composition.on('remove', this.removeTelemetryObject);
+
+ this.composition.load();
+
+ this.openmct.time.on('bounds', this.refreshData);
+ this.openmct.time.on('timeSystem', this.setTimeSystem);
+ },
+ destroyed() {
+ this.composition.off('add', this.addedToComposition);
+ this.composition.off('remove', this.removeTelemetryObject);
+
+ if (this.unsubscribe) {
+ this.unsubscribe();
+ }
+
+ this.openmct.time.off('bounds', this.refreshData);
+ this.openmct.time.off('timeSystem', this.setTimeSystem);
+ },
+ methods: {
+ getLimitDegree: getLimitDegree,
+ addTelemetryObjectAndSubscribe(domainObject) {
+ this.telemetryObject = domainObject;
+ this.request();
+ this.subscribe();
+ },
+ addedToComposition(domainObject) {
+ if (this.telemetryObject) {
+ this.confirmRemoval(domainObject);
+ } else {
+ this.addTelemetryObjectAndSubscribe(domainObject);
+ }
+ },
+ confirmRemoval(domainObject) {
+ const dialog = this.openmct.overlays.dialog({
+ iconClass: 'alert',
+ message: 'This action will replace the current telemetry source. Do you want to continue?',
+ buttons: [
+ {
+ label: 'Ok',
+ emphasis: true,
+ callback: () => {
+ this.removeFromComposition();
+ this.removeTelemetryObject();
+ this.addTelemetryObjectAndSubscribe(domainObject);
+ dialog.dismiss();
+ }
+ },
+ {
+ label: 'Cancel',
+ callback: () => {
+ this.removeFromComposition(domainObject);
+ dialog.dismiss();
+ }
+ }
+ ]
+ });
+ },
+ fontSizeFromChars(charNum, charThreshold, startPerc, reducePerc) {
+ const fs = (charNum <= charThreshold) ? startPerc : (startPerc - ((charNum - charThreshold) * reducePerc));
+
+ return fs.toString() + "%";
+ },
+ matchGaugeType(str) {
+ return this.gaugeType.indexOf(str) !== -1;
+ },
+ percentToDegrees(vPercent) {
+ return this.round(((vPercent / 100) * 270) + DIAL_VALUE_DEG_OFFSET, 2);
+ },
+ removeFromComposition(telemetryObject = this.telemetryObject) {
+ let composition = this.domainObject.composition.filter(id =>
+ !this.openmct.objects.areIdsEqual(id, telemetryObject.identifier)
+ );
+
+ this.openmct.objects.mutate(this.domainObject, 'composition', composition);
+ },
+ refreshData(bounds, isTick) {
+ if (!isTick) {
+ this.request();
+ }
+ },
+ removeTelemetryObject() {
+ if (this.unsubscribe) {
+ this.unsubscribe();
+ this.unsubscribe = null;
+ }
+
+ this.curVal = DEFAULT_CURRENT_VALUE;
+ this.formats = null;
+ this.limitHigh = '';
+ this.limitLow = '';
+ this.metadata = null;
+ this.rangeHigh = null;
+ this.rangeLow = null;
+ this.valueKey = null;
+ },
+ request(domainObject = this.telemetryObject) {
+ this.metadata = this.openmct.telemetry.getMetadata(domainObject);
+ this.formats = this.openmct.telemetry.getFormatMap(this.metadata);
+ const LimitEvaluator = this.openmct.telemetry.getLimits(domainObject);
+ LimitEvaluator.limits().then(this.updateLimits);
+
+ this.valueKey = this
+ .metadata
+ .valuesForHints(['range'])[0].source;
+
+ this.openmct
+ .telemetry
+ .request(domainObject, { strategy: 'latest' })
+ .then(values => {
+ const length = values.length;
+ this.updateValue(values[length - 1]);
+ });
+
+ this.units = this.metadata.value(this.valueKey).unit || '';
+ },
+ round(val, decimals = this.precision) {
+ let precision = Math.pow(10, decimals);
+
+ return Math.round(val * precision) / precision;
+ },
+ setTimeSystem(timeSystem) {
+ this.activeTimeSystem = timeSystem;
+ },
+ subscribe(domainObject = this.telemetryObject) {
+ this.unsubscribe = this.openmct
+ .telemetry
+ .subscribe(domainObject, this.updateValue.bind(this));
+ },
+ updateLimits(telemetryLimit) {
+ if (!telemetryLimit || !this.domainObject.configuration.gaugeController.isUseTelemetryLimits) {
+ return;
+ }
+
+ let limits = {
+ high: 0,
+ low: 0
+ };
+ if (telemetryLimit.CRITICAL) {
+ limits = telemetryLimit.CRITICAL;
+ } else if (telemetryLimit.DISTRESS) {
+ limits = telemetryLimit.DISTRESS;
+ } else if (telemetryLimit.SEVERE) {
+ limits = telemetryLimit.SEVERE;
+ } else if (telemetryLimit.WARNING) {
+ limits = telemetryLimit.WARNING;
+ } else if (telemetryLimit.WATCH) {
+ limits = telemetryLimit.WATCH;
+ } else {
+ this.openmct.notifications.error('No limits definition for given telemetry, hiding low and high limits');
+ this.displayMinMax = false;
+ this.limitHigh = '';
+ this.limitLow = '';
+
+ return;
+ }
+
+ this.limitHigh = this.round(limits.high[this.valueKey]);
+ this.limitLow = this.round(limits.low[this.valueKey]);
+ this.rangeHigh = this.round(this.limitHigh + this.limitHigh * LIMIT_PADDING_IN_PERCENT / 100);
+ this.rangeLow = this.round(this.limitLow - Math.abs(this.limitLow * LIMIT_PADDING_IN_PERCENT / 100));
+
+ this.displayMinMax = this.domainObject.configuration.gaugeController.isDisplayMinMax;
+ },
+ updateValue(datum) {
+ this.datum = datum;
+
+ if (this.isRendering) {
+ return;
+ }
+
+ const { start, end } = this.openmct.time.bounds();
+ const parsedValue = this.timeFormatter.parse(this.datum);
+
+ const beforeStartOfBounds = parsedValue < start;
+ const afterEndOfBounds = parsedValue > end;
+ if (afterEndOfBounds || beforeStartOfBounds) {
+ return;
+ }
+
+ this.isRendering = true;
+ requestAnimationFrame(() => {
+ this.isRendering = false;
+
+ this.curVal = this.round(this.formats[this.valueKey].format(this.datum), this.precision);
+ });
+ },
+ valToPercent(vValue) {
+ // Used by dial
+ if (vValue >= this.rangeHigh && this.typeFilledDial) {
+ // For filled dial, clip values over the high range to prevent over-rotation
+ return 100;
+ }
+
+ return ((vValue - this.rangeLow) / (this.rangeHigh - this.rangeLow)) * 100;
+ },
+ valToPercentMeter(vValue) {
+ return this.round((this.rangeHigh - vValue) / (this.rangeHigh - this.rangeLow) * 100, 2);
+ }
+ }
+};
+</script>
diff --git a/src/plugins/gauge/components/GaugeFormController.vue b/src/plugins/gauge/components/GaugeFormController.vue
new file mode 100644
index 000000000..b9556e79b
--- /dev/null
+++ b/src/plugins/gauge/components/GaugeFormController.vue
@@ -0,0 +1,173 @@
+/*****************************************************************************
+* Open MCT, Copyright (c) 2014-2022, United States Government
+* as represented by the Administrator of the National Aeronautics and Space
+* Administration. All rights reserved.
+*
+* Open MCT is licensed under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+* http://www.apache.org/licenses/LICENSE-2.0.
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+* License for the specific language governing permissions and limitations
+* under the License.
+*
+* Open MCT includes source code licensed under additional open source
+* licenses. See the Open Source Licenses file (LICENSES.md) included with
+* this source code distribution or the Licensing information page available
+* at runtime from the About dialog for additional information.
+*****************************************************************************/
+
+<template>
+<span class="form-control">
+ <span
+ class="field control"
+ :class="model.cssClass"
+ >
+ <ToggleSwitch
+ :id="'gaugeToggle'"
+ :checked="isUseTelemetryLimits"
+ label="Use telemetry limits for minimum and maximum ranges"
+ @change="toggleUseTelemetryLimits"
+ />
+
+ <div
+ v-if="!isUseTelemetryLimits"
+ class="c-form--sub-grid"
+ >
+ <div class="c-form__row">
+ <span class="req-indicator req">
+ </span>
+ <label>Minimum value</label>
+ <input
+ ref="min"
+ v-model.number="min"
+ data-field-name="min"
+ type="number"
+ @input="onChange"
+ >
+ </div>
+
+ <div class="c-form__row">
+ <span class="req-indicator">
+ </span>
+ <label>Low limit</label>
+ <input
+ ref="limitLow"
+ v-model.number="limitLow"
+ data-field-name="limitLow"
+ type="number"
+ @input="onChange"
+ >
+ </div>
+
+ <div class="c-form__row">
+ <span class="req-indicator">
+ </span>
+ <label>High limit</label>
+ <input
+ ref="limitHigh"
+ v-model.number="limitHigh"
+ data-field-name="limitHigh"
+ type="number"
+ @input="onChange"
+ >
+ </div>
+
+ <div class="c-form__row">
+ <span class="req-indicator req">
+ </span>
+ <label>Maximum value</label>
+ <input
+ ref="max"
+ v-model.number="max"
+ data-field-name="max"
+ type="number"
+ @input="onChange"
+ >
+ </div>
+ </div>
+ </span>
+</span>
+</template>
+
+<script>
+import ToggleSwitch from '@/ui/components/ToggleSwitch.vue';
+
+export default {
+ components: {
+ ToggleSwitch
+ },
+ props: {
+ model: {
+ type: Object,
+ required: true
+ }
+ },
+ data() {
+ return {
+ isUseTelemetryLimits: this.model.value.isUseTelemetryLimits,
+ isDisplayMinMax: this.model.value.isDisplayMinMax,
+ isDisplayCurVal: this.model.value.isDisplayCurVal,
+ isDisplayUnits: this.model.value.isDisplayUnits,
+ limitHigh: this.model.value.limitHigh,
+ limitLow: this.model.value.limitLow,
+ max: this.model.value.max,
+ min: this.model.value.min
+ };
+ },
+ methods: {
+ onChange(event) {
+ const data = {
+ model: this.model,
+ value: {
+ gaugeType: this.model.value.gaugeType,
+ isDisplayMinMax: this.isDisplayMinMax,
+ isDisplayCurVal: this.isDisplayCurVal,
+ isDisplayUnits: this.isDisplayUnits,
+ isUseTelemetryLimits: this.isUseTelemetryLimits,
+ limitLow: this.limitLow,
+ limitHigh: this.limitHigh,
+ max: this.max,
+ min: this.min,
+ precision: this.model.value.precision
+ }
+ };
+
+ if (event) {
+ const target = event.target;
+ const targetIndicator = target.parentElement.querySelector('.req-indicator');
+ if (targetIndicator.classList.contains('req')) {
+ targetIndicator.classList.add('visited');
+ }
+
+ this.model.validate(data, (valid) => {
+ Object.entries(valid).forEach(([key, isValid]) => {
+ const element = this.$refs[key];
+ const reqIndicatorElement = element.parentElement.querySelector('.req-indicator');
+ reqIndicatorElement.classList.toggle('invalid', !isValid);
+
+ if (reqIndicatorElement.classList.contains('req') && (!isValid || reqIndicatorElement.classList.contains('visited'))) {
+ reqIndicatorElement.classList.toggle('valid', isValid);
+ }
+ });
+ });
+ }
+
+ this.$emit('onChange', data);
+ },
+ toggleUseTelemetryLimits() {
+ this.isUseTelemetryLimits = !this.isUseTelemetryLimits;
+
+ this.onChange();
+ },
+ toggleMinMax() {
+ this.isDisplayMinMax = !this.isDisplayMinMax;
+
+ this.onChange();
+ }
+ }
+};
+</script>
diff --git a/src/plugins/gauge/gauge-limit-util.js b/src/plugins/gauge/gauge-limit-util.js
new file mode 100644
index 000000000..39ea55db7
--- /dev/null
+++ b/src/plugins/gauge/gauge-limit-util.js
@@ -0,0 +1,39 @@
+const GAUGE_LIMITS = {
+ q1: 0,
+ q2: 90,
+ q3: 180,
+ q4: 270
+};
+
+export const DIAL_VALUE_DEG_OFFSET = 45;
+
+// type: low, high
+// quadrant: low, mid, high, max
+export function getLimitDegree(type, quadrant) {
+ if (quadrant === 'max') {
+ return GAUGE_LIMITS.q4 + DIAL_VALUE_DEG_OFFSET;
+ }
+
+ return type === 'low'
+ ? getLowLimitDegree(quadrant)
+ : getHighLimitDegree(quadrant)
+ ;
+}
+
+function getLowLimitDegree(quadrant) {
+ return GAUGE_LIMITS[quadrant] + DIAL_VALUE_DEG_OFFSET;
+}
+
+function getHighLimitDegree(quadrant) {
+ if (quadrant === 'q1') {
+ return GAUGE_LIMITS.q4 + DIAL_VALUE_DEG_OFFSET;
+ }
+
+ if (quadrant === 'q2') {
+ return GAUGE_LIMITS.q3 + DIAL_VALUE_DEG_OFFSET;
+ }
+
+ if (quadrant === 'q3') {
+ return GAUGE_LIMITS.q2 + DIAL_VALUE_DEG_OFFSET;
+ }
+}
diff --git a/src/plugins/gauge/gauge.scss b/src/plugins/gauge/gauge.scss
new file mode 100644
index 000000000..96b9efbbd
--- /dev/null
+++ b/src/plugins/gauge/gauge.scss
@@ -0,0 +1,318 @@
+$meterNeedlePerc: 1%;
+$meterNeedleMinPx: 4px;
+$meterNeedleMaxPx: 20px;
+$meterNeedleBorderRadius: 5px;
+
+.is-object-type-gauge {
+ overflow: hidden;
+}
+
+.req-indicator {
+ width: 20px;
+
+ &.invalid,
+ &.invalid.req { @include validationState($glyph-icon-x, $colorFormInvalid); }
+ &.valid,
+ &.valid.req { @include validationState($glyph-icon-check, $colorFormValid); }
+ &.req { @include validationState($glyph-icon-asterisk, $colorFormRequired); }
+}
+
+.c-gauge {
+ // Both dial and meter types
+ overflow: hidden;
+
+ &__range,
+ &__units,
+ &__units text {
+ $c: $colorGaugeRange;
+ color: $c;
+ fill: $c;
+ }
+
+ &__wrapper {
+ @include abs();
+ overflow: hidden;
+ }
+
+ &__current-value-text-wrapper {
+ // SVG
+ position: absolute;
+ height: 100%;
+ width: 100%;
+ }
+}
+
+.c-dial__value-oor-indicator,
+.c-meter__value-oor-indicator {
+ fill: $colorGaugeRange;
+ opacity: 0.5;
+}
+
+/********************************************** DIAL GAUGE */
+.c-dial {
+ max-height: 100%;
+ max-width: 100%;
+
+ &__bg {
+ fill: $colorGaugeBg;
+ }
+ &__limit-high rect {
+ fill: $colorGaugeLimitHigh;
+ }
+ &__limit-low rect {
+ fill: $colorGaugeLimitLow;
+ }
+ &__filled-value,
+ &__range-msg-text {
+ fill: $colorGaugeValue;
+ }
+ &__needle-value {
+ fill: $colorGaugeValue;
+ }
+ &__current-value-text {
+ fill: $colorGaugeTextValue;
+ font-family: $heroFont;
+ }
+
+ &__units-text,
+ &__range-text {
+ fill: $colorGaugeRange;
+ }
+
+ &__graphics g {
+ transform-origin: center;
+ }
+}
+
+/********************************************** METER GAUGE */
+.c-meter {
+ // Common styles for c-meter
+ $meterOutOfRangeIndicatorMaxSize: 50%;
+ @include abs();
+ display: flex;
+
+ &__range {
+ display: flex;
+ flex: 0 0 auto;
+ justify-content: space-between;
+ }
+
+ &__bg {
+ background: $colorGaugeBg;
+ border-radius: $basicCr;
+ flex: 1 1 auto;
+ overflow: hidden;
+ }
+
+ &__value {
+ // Filled area
+ position: absolute;
+ background: $colorGaugeValue;
+ z-index: 1;
+ }
+
+ &__value-needle {
+ background: none !important;
+ &:before {
+ @include abs();
+ content: '';
+ display: block;
+ background: $colorGaugeValue;
+ }
+ }
+
+ &__value-oor-indicator {
+ $mxPx: 50px;
+ $wh: 50%;
+ position: absolute;
+ height: $wh;
+ width: $wh;
+ max-height: $mxPx;
+ max-width: $mxPx;
+
+ svg {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ max-height: 100%;
+ max-width: 100%;
+ }
+ }
+
+ &__current-value-text {
+ fill: $colorGaugeTextValue;
+ font-family: $heroFont;
+ }
+
+ .c-gauge__curval {
+ fill: $colorGaugeMeterTextValue !important;
+ }
+
+ [class*='limit'] {
+ position: absolute;
+ }
+
+ &__limit-high {
+ background: $colorGaugeLimitHigh;
+ }
+
+ &__limit-low {
+ background: $colorGaugeLimitLow;
+ }
+}
+
+.c-meter {
+ .c-gauge--meter-vertical &,
+ .c-gauge--meter-vertical-inverted & {
+ &__range {
+ flex-direction: column;
+ min-width: min-content;
+ margin-right: $interiorMarginSm;
+ text-align: right;
+ }
+
+ &__value {
+ // Filled area
+ $lrM: $marginGaugeMeterValue;
+ left: $lrM;
+ right: $lrM;
+ top: 0;
+ bottom: 0;
+ }
+
+ &__value-needle {
+ right: 0;
+
+ &:before {
+ border-bottom-left-radius: $meterNeedleBorderRadius;
+ border-top-left-radius: $meterNeedleBorderRadius;
+ height: $meterNeedlePerc;
+ min-height: $meterNeedleMinPx;
+ max-height: $meterNeedleMaxPx;
+ }
+ }
+
+ [class*='limit'] {
+ left: 0;
+ right: 0;
+ }
+
+ .c-meter__value-oor-indicator {
+ bottom: 10%;
+ left: 50%;
+ transform: translateX(-50%);
+ }
+ }
+
+ .c-gauge--meter-vertical & {
+ &__limit-low {
+ bottom: 0;
+ }
+
+ &__limit-high {
+ top: 0;
+ }
+
+ &__value-needle {
+ &:before {
+ bottom: auto;
+ transform: translateY(-50%);
+ }
+ }
+ }
+
+ .c-gauge--meter-vertical-inverted & {
+ &__limit-low {
+ top: 0;
+ }
+
+ &__limit-high {
+ bottom: 0;
+ }
+
+ &__range__low {
+ order: 1;
+ }
+
+ &__range__high {
+ order: 2;
+ }
+
+ &__value-needle {
+ &:before {
+ top: auto;
+ transform: translateY(50%);
+ }
+ }
+ }
+
+ .c-gauge--meter-horizontal & {
+ flex-direction: column;
+
+ &__range {
+ flex-direction: row;
+ min-height: min-content;
+ margin-top: $interiorMarginSm;
+ order: 2;
+
+ &__high {
+ order: 2;
+ }
+
+ &__low {
+ order: 1;
+ }
+ }
+
+ &__bg {
+ order: 1;
+ }
+
+ &__value {
+ // Filled area
+ $m: $marginGaugeMeterValue;
+ top: $m;
+ bottom: $m;
+ left: 0;
+ right: 0;
+ }
+
+ &__value-needle {
+ top: 0;
+
+ &:before {
+ border-bottom-left-radius: $meterNeedleBorderRadius;
+ border-bottom-right-radius: $meterNeedleBorderRadius;
+ left: auto;
+ width: $meterNeedlePerc;
+ min-width: $meterNeedleMinPx;
+ max-width: $meterNeedleMaxPx;
+ transform: translateX(50%);
+ }
+ }
+
+ [class*='limit'] {
+ top: 0;
+ bottom: 0;
+ }
+
+ &__limit-low {
+ left: 0;
+ }
+
+ &__limit-high {
+ right: 0;
+ }
+
+ .c-meter__value-oor-indicator {
+ // Horizontal meter
+ left: 2%;
+ top: 50%;
+ transform: translateY(-50%);
+ }
+ }
+}
+.svg-viewbox-debug {
+ fill: rgba(deeppink, 0.5);
+ display: none;
+}
diff --git a/src/plugins/goToOriginalAction/goToOriginalAction.js b/src/plugins/goToOriginalAction/goToOriginalAction.js
index 1eb156f33..c85577be7 100644
--- a/src/plugins/goToOriginalAction/goToOriginalAction.js
+++ b/src/plugins/goToOriginalAction/goToOriginalAction.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/goToOriginalAction/plugin.js b/src/plugins/goToOriginalAction/plugin.js
index 19decac2a..eb1a5530f 100644
--- a/src/plugins/goToOriginalAction/plugin.js
+++ b/src/plugins/goToOriginalAction/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/goToOriginalAction/pluginSpec.js b/src/plugins/goToOriginalAction/pluginSpec.js
index 6bd428e2e..8fc0ef9d7 100644
--- a/src/plugins/goToOriginalAction/pluginSpec.js
+++ b/src/plugins/goToOriginalAction/pluginSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/hyperlink/HyperlinkLayout.vue b/src/plugins/hyperlink/HyperlinkLayout.vue
index efb03c983..c4cdf0c6c 100644
--- a/src/plugins/hyperlink/HyperlinkLayout.vue
+++ b/src/plugins/hyperlink/HyperlinkLayout.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
-* Open MCT, Copyright (c) 2014-2021, United States Government
+* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -22,12 +22,13 @@
<template>
-<a class="c-hyperlink"
- :class="{
- 'c-hyperlink--button' : isButton
- }"
- :target="domainObject.linkTarget"
- :href="url"
+<a
+ class="c-hyperlink"
+ :class="{
+ 'c-hyperlink--button' : isButton
+ }"
+ :target="domainObject.linkTarget"
+ :href="url"
>
<span class="c-hyperlink__label">{{ domainObject.displayText }}</span>
</a>
diff --git a/src/plugins/hyperlink/HyperlinkProvider.js b/src/plugins/hyperlink/HyperlinkProvider.js
index 6a39a3631..abf6957ed 100644
--- a/src/plugins/hyperlink/HyperlinkProvider.js
+++ b/src/plugins/hyperlink/HyperlinkProvider.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/hyperlink/plugin.js b/src/plugins/hyperlink/plugin.js
index d89bf8ecd..4452a5ad4 100644
--- a/src/plugins/hyperlink/plugin.js
+++ b/src/plugins/hyperlink/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/hyperlink/pluginSpec.js b/src/plugins/hyperlink/pluginSpec.js
index e7e5475a6..8450c5a4e 100644
--- a/src/plugins/hyperlink/pluginSpec.js
+++ b/src/plugins/hyperlink/pluginSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2009-2016, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/imagery/ImageryTimestripViewProvider.js b/src/plugins/imagery/ImageryTimestripViewProvider.js
index 8f1e40ed2..4f48eb5dc 100644
--- a/src/plugins/imagery/ImageryTimestripViewProvider.js
+++ b/src/plugins/imagery/ImageryTimestripViewProvider.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/imagery/ImageryView.js b/src/plugins/imagery/ImageryView.js
index e10405e72..391cd86df 100644
--- a/src/plugins/imagery/ImageryView.js
+++ b/src/plugins/imagery/ImageryView.js
@@ -2,11 +2,16 @@ import ImageryViewComponent from './components/ImageryView.vue';
import Vue from 'vue';
+const DEFAULT_IMAGE_FRESHNESS_OPTIONS = {
+ fadeOutDelayTime: '0s',
+ fadeOutDurationTime: '30s'
+};
export default class ImageryView {
- constructor(openmct, domainObject, objectPath) {
+ constructor(openmct, domainObject, objectPath, options) {
this.openmct = openmct;
this.domainObject = domainObject;
this.objectPath = objectPath;
+ this.options = options;
this.component = undefined;
}
@@ -27,6 +32,7 @@ export default class ImageryView {
openmct: this.openmct,
domainObject: this.domainObject,
objectPath: alternateObjectPath || this.objectPath,
+ imageFreshnessOptions: this.options?.imageFreshness || DEFAULT_IMAGE_FRESHNESS_OPTIONS,
currentView: this
},
data() {
diff --git a/src/plugins/imagery/ImageryViewProvider.js b/src/plugins/imagery/ImageryViewProvider.js
index d8a06be42..6450b6258 100644
--- a/src/plugins/imagery/ImageryViewProvider.js
+++ b/src/plugins/imagery/ImageryViewProvider.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -21,7 +21,7 @@
*****************************************************************************/
import ImageryView from './ImageryView';
-export default function ImageryViewProvider(openmct) {
+export default function ImageryViewProvider(openmct, options) {
const type = 'example.imagery';
function hasImageTelemetry(domainObject) {
@@ -43,7 +43,7 @@ export default function ImageryViewProvider(openmct) {
return hasImageTelemetry(domainObject) && (!isChildOfTimeStrip || openmct.router.isNavigatedObject(objectPath));
},
view: function (domainObject, objectPath) {
- return new ImageryView(openmct, domainObject, objectPath);
+ return new ImageryView(openmct, domainObject, objectPath, options);
}
};
}
diff --git a/src/plugins/imagery/components/Compass/Compass.vue b/src/plugins/imagery/components/Compass/Compass.vue
index 18b5353d2..850cd3c7d 100644
--- a/src/plugins/imagery/components/Compass/Compass.vue
+++ b/src/plugins/imagery/components/Compass/Compass.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/imagery/components/Compass/CompassHUD.vue b/src/plugins/imagery/components/Compass/CompassHUD.vue
index c3920c0bd..4820cedde 100644
--- a/src/plugins/imagery/components/Compass/CompassHUD.vue
+++ b/src/plugins/imagery/components/Compass/CompassHUD.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/imagery/components/Compass/CompassRose.vue b/src/plugins/imagery/components/Compass/CompassRose.vue
index 3f61d6cf5..d66e382a4 100644
--- a/src/plugins/imagery/components/Compass/CompassRose.vue
+++ b/src/plugins/imagery/components/Compass/CompassRose.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -21,124 +21,144 @@
*****************************************************************************/
<template>
-<div ref="compassRoseWrapper"
- class="w-direction-rose"
- :class="compassRoseSizingClasses"
- @click="toggleLockCompass"
+<div
+ ref="compassRoseWrapper"
+ class="w-direction-rose"
+ :class="compassRoseSizingClasses"
+ @click="toggleLockCompass"
>
- <svg ref="compassRoseSvg"
- class="c-compass-rose-svg"
- viewBox="0 0 100 100"
+ <svg
+ ref="compassRoseSvg"
+ class="c-compass-rose-svg"
+ viewBox="0 0 100 100"
>
- <mask id="mask0"
- mask-type="alpha"
- maskUnits="userSpaceOnUse"
- x="0"
- y="0"
- width="100"
- height="100"
+ <mask
+ id="mask0"
+ mask-type="alpha"
+ maskUnits="userSpaceOnUse"
+ x="0"
+ y="0"
+ width="100"
+ height="100"
>
- <circle cx="50"
- cy="50"
- r="50"
- fill="black"
+ <circle
+ cx="50"
+ cy="50"
+ r="50"
+ fill="black"
/>
</mask>
<g class="c-cr__compass-wrapper">
- <g class="c-cr__compass-main"
- mask="url(#mask0)"
+ <g
+ class="c-cr__compass-main"
+ mask="url(#mask0)"
>
<!-- Background and clipped elements -->
- <rect class="c-cr__bg"
- width="100"
- height="100"
- fill="black"
+ <rect
+ class="c-cr__bg"
+ width="100"
+ height="100"
+ fill="black"
/>
- <rect class="c-cr__edge"
- width="100"
- height="100"
- fill="url(#paint0_radial)"
+ <rect
+ class="c-cr__edge"
+ width="100"
+ height="100"
+ fill="url(#paint0_radial)"
/>
- <rect v-if="hasSunHeading"
- class="c-cr__sun"
- width="100"
- height="100"
- fill="url(#paint1_radial)"
- :style="sunHeadingStyle"
+ <rect
+ v-if="hasSunHeading"
+ class="c-cr__sun"
+ width="100"
+ height="100"
+ fill="url(#paint1_radial)"
+ :style="sunHeadingStyle"
/>
<!-- Camera FOV -->
- <mask id="mask2"
- class="c-cr__cam-fov-l-mask"
- mask-type="alpha"
- maskUnits="userSpaceOnUse"
- x="0"
- y="0"
- width="50"
- height="100"
+ <mask
+ id="mask2"
+ class="c-cr__cam-fov-l-mask"
+ mask-type="alpha"
+ maskUnits="userSpaceOnUse"
+ x="0"
+ y="0"
+ width="50"
+ height="100"
>
- <rect width="51"
- height="100"
+ <rect
+ width="51"
+ height="100"
/>
</mask>
- <mask id="mask1"
- class="c-cr__cam-fov-r-mask"
- mask-type="alpha"
- maskUnits="userSpaceOnUse"
- x="50"
- y="0"
- width="50"
- height="100"
+ <mask
+ id="mask1"
+ class="c-cr__cam-fov-r-mask"
+ mask-type="alpha"
+ maskUnits="userSpaceOnUse"
+ x="50"
+ y="0"
+ width="50"
+ height="100"
>
- <rect x="49"
- width="51"
- height="100"
+ <rect
+ x="49"
+ width="51"
+ height="100"
/>
</mask>
- <g class="c-cr__cam-fov"
- :style="cameraPanStyle"
+ <g
+ class="c-cr__cam-fov"
+ :style="cameraPanStyle"
>
<g mask="url(#mask2)">
- <rect class="c-cr__cam-fov-r"
- x="49"
- width="51"
- height="100"
- :style="cameraFOVStyleRightHalf"
+ <rect
+ class="c-cr__cam-fov-r"
+ x="49"
+ width="51"
+ height="100"
+ :style="cameraFOVStyleRightHalf"
/>
</g>
<g mask="url(#mask1)">
- <rect class="c-cr__cam-fov-l"
- width="51"
- height="100"
- :style="cameraFOVStyleLeftHalf"
+ <rect
+ class="c-cr__cam-fov-l"
+ width="51"
+ height="100"
+ :style="cameraFOVStyleLeftHalf"
/>
</g>
</g>
</g>
<!-- Spacecraft body -->
- <path v-if="hasHeading"
- class="c-cr__spacecraft-body"
- fill-rule="evenodd"
- clip-rule="evenodd"
- d="M37 49C35.3431 49 34 50.3431 34 52V82C34 83.6569 35.3431 85 37 85H63C64.6569 85 66 83.6569 66 82V52C66 50.3431 64.6569 49 63 49H37ZM50 52L58 60H55V67H45V60H42L50 52Z"
- :style="headingStyle"
+ <path
+ v-if="hasHeading"
+ class="c-cr__spacecraft-body"
+ fill-rule="evenodd"
+ clip-rule="evenodd"
+ d="M37 49C35.3431 49 34 50.3431 34 52V82C34 83.6569 35.3431 85 37 85H63C64.6569 85 66 83.6569 66 82V52C66 50.3431 64.6569 49 63 49H37ZM50 52L58 60H55V67H45V60H42L50 52Z"
+ :style="headingStyle"
/>
<!-- NSEW and ticks -->
- <g class="c-cr__nsew"
- :style="compassRoseStyle"
+ <g
+ class="c-cr__nsew"
+ :style="compassRoseStyle"
>
<g class="c-cr__ticks-major">
<path d="M50 3L43 10H57L50 3Z" />
- <path d="M4 51V49H10V51H4Z"
- class="--hide-min"
+ <path
+ d="M4 51V49H10V51H4Z"
+ class="--hide-min"
/>
- <path d="M49 96V90H51V96H49Z"
- class="--hide-min"
+ <path
+ d="M49 96V90H51V96H49Z"
+ class="--hide-min"
/>
- <path d="M90 49V51H96V49H90Z"
- class="--hide-min"
+ <path
+ d="M90 49V51H96V49H90Z"
+ class="--hide-min"
/>
</g>
<g class="c-cr__ticks-minor --hide-small">
@@ -148,53 +168,63 @@
<path d="M51 10L49 10V4L51 4V10Z" />
</g>
<g class="c-cr__nsew-text">
- <path :style="cardinalTextRotateW"
- class="c-cr__nsew-w --hide-small"
- d="M56.7418 45.004H54.1378L52.7238 52.312H52.6958L51.2258 45.004H48.7758L47.3058 52.312H47.2778L45.8638 45.004H43.2598L45.9618 55H48.6078L49.9798 48.112H50.0078L51.3798 55H53.9838L56.7418 45.004Z"
+ <path
+ :style="cardinalTextRotateW"
+ class="c-cr__nsew-w --hide-small"
+ d="M56.7418 45.004H54.1378L52.7238 52.312H52.6958L51.2258 45.004H48.7758L47.3058 52.312H47.2778L45.8638 45.004H43.2598L45.9618 55H48.6078L49.9798 48.112H50.0078L51.3798 55H53.9838L56.7418 45.004Z"
/>
- <path :style="cardinalTextRotateE"
- class="c-cr__nsew-e --hide-small"
- d="M46.104 55H54.21V52.76H48.708V50.856H53.608V48.84H48.708V47.09H54.07V45.004H46.104V55Z"
+ <path
+ :style="cardinalTextRotateE"
+ class="c-cr__nsew-e --hide-small"
+ d="M46.104 55H54.21V52.76H48.708V50.856H53.608V48.84H48.708V47.09H54.07V45.004H46.104V55Z"
/>
- <path :style="cardinalTextRotateS"
- class="c-cr__nsew-s --hide-small"
- d="M45.6531 51.64C45.6671 54.202 47.6971 55.21 49.9931 55.21C52.1911 55.21 54.3471 54.398 54.3471 51.864C54.3471 50.058 52.8911 49.386 51.4491 48.98C49.9931 48.574 48.5511 48.434 48.5511 47.664C48.5511 47.006 49.2511 46.81 49.8111 46.81C50.6091 46.81 51.4631 47.104 51.4211 48.014H54.0251C54.0111 45.76 52.0091 44.794 50.0211 44.794C48.1451 44.794 45.9471 45.648 45.9471 47.832C45.9471 49.666 47.4451 50.31 48.8731 50.716C50.3151 51.122 51.7431 51.29 51.7431 52.172C51.7431 52.914 50.9311 53.194 50.1471 53.194C49.0411 53.194 48.3131 52.816 48.2571 51.64H45.6531Z"
+ <path
+ :style="cardinalTextRotateS"
+ class="c-cr__nsew-s --hide-small"
+ d="M45.6531 51.64C45.6671 54.202 47.6971 55.21 49.9931 55.21C52.1911 55.21 54.3471 54.398 54.3471 51.864C54.3471 50.058 52.8911 49.386 51.4491 48.98C49.9931 48.574 48.5511 48.434 48.5511 47.664C48.5511 47.006 49.2511 46.81 49.8111 46.81C50.6091 46.81 51.4631 47.104 51.4211 48.014H54.0251C54.0111 45.76 52.0091 44.794 50.0211 44.794C48.1451 44.794 45.9471 45.648 45.9471 47.832C45.9471 49.666 47.4451 50.31 48.8731 50.716C50.3151 51.122 51.7431 51.29 51.7431 52.172C51.7431 52.914 50.9311 53.194 50.1471 53.194C49.0411 53.194 48.3131 52.816 48.2571 51.64H45.6531Z"
/>
- <path :style="cardinalTextRotateN"
- class="c-cr__nsew-n"
- d="M42.5935 60H46.7935V49.32H46.8415L52.7935 60H57.3775V42.864H53.1775V53.424H53.1295L47.1775 42.864H42.5935V60Z"
+ <path
+ :style="cardinalTextRotateN"
+ class="c-cr__nsew-n"
+ d="M42.5935 60H46.7935V49.32H46.8415L52.7935 60H57.3775V42.864H53.1775V53.424H53.1295L47.1775 42.864H42.5935V60Z"
/>
</g>
</g>
</g>
<defs>
- <radialGradient id="paint0_radial"
- cx="0"
- cy="0"
- r="1"
- gradientUnits="userSpaceOnUse"
- gradientTransform="translate(50 50) rotate(90) scale(50)"
+ <radialGradient
+ id="paint0_radial"
+ cx="0"
+ cy="0"
+ r="1"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(50 50) rotate(90) scale(50)"
>
- <stop offset="0.751387"
- stop-opacity="0"
+ <stop
+ offset="0.751387"
+ stop-opacity="0"
/>
- <stop offset="1"
- stop-color="white"
+ <stop
+ offset="1"
+ stop-color="white"
/>
</radialGradient>
- <radialGradient id="paint1_radial"
- cx="0"
- cy="0"
- r="1"
- gradientUnits="userSpaceOnUse"
- gradientTransform="translate(50 -7) rotate(-90) scale(18.5)"
+ <radialGradient
+ id="paint1_radial"
+ cx="0"
+ cy="0"
+ r="1"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(50 -7) rotate(-90) scale(18.5)"
>
- <stop offset="0.716377"
- stop-color="#FFCC00"
+ <stop
+ offset="0.716377"
+ stop-color="#FFCC00"
/>
- <stop offset="1"
- stop-color="#FF9900"
- stop-opacity="0"
+ <stop
+ offset="1"
+ stop-color="#FF9900"
+ stop-opacity="0"
/>
</radialGradient>
</defs>
diff --git a/src/plugins/imagery/components/Compass/compass.scss b/src/plugins/imagery/components/Compass/compass.scss
index dba4106d2..5609bf9f6 100644
--- a/src/plugins/imagery/components/Compass/compass.scss
+++ b/src/plugins/imagery/components/Compass/compass.scss
@@ -14,7 +14,7 @@ $elemBg: rgba(black, 0.7);
position: absolute;
left: 0;
top: 0;
- z-index: 1;
+ z-index: 2;
@include userSelectNone;
}
diff --git a/src/plugins/imagery/components/Compass/pluginSpec.js b/src/plugins/imagery/components/Compass/pluginSpec.js
index 52e5c8b77..1b2ab8aba 100644
--- a/src/plugins/imagery/components/Compass/pluginSpec.js
+++ b/src/plugins/imagery/components/Compass/pluginSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/imagery/components/FilterSettings.vue b/src/plugins/imagery/components/FilterSettings.vue
new file mode 100644
index 000000000..16aaadbb5
--- /dev/null
+++ b/src/plugins/imagery/components/FilterSettings.vue
@@ -0,0 +1,78 @@
+<template>
+<div
+ class="c-control-menu c-menu--to-left c-menu--has-close-btn c-image-controls c-image-controls--filters"
+ @click="handleClose"
+>
+ <div
+ class="c-image-controls__controls"
+ @click="$event.stopPropagation()"
+ >
+ <span class="c-image-controls__sliders">
+ <div class="c-image-controls__slider-wrapper icon-brightness">
+ <input
+ v-model="filters.brightness"
+ type="range"
+ min="0"
+ max="500"
+ draggable="true"
+ @dragstart.stop.prevent
+ @change="notifyFiltersChanged"
+ @input="notifyFiltersChanged"
+ >
+ </div>
+ <div class="c-image-controls__slider-wrapper icon-contrast">
+ <input
+ v-model="filters.contrast"
+ type="range"
+ min="0"
+ max="500"
+ draggable="true"
+ @dragstart.stop.prevent
+ @change="notifyFiltersChanged"
+ @input="notifyFiltersChanged"
+ >
+ </div>
+ </span>
+ <span class="c-image-controls__reset-btn">
+ <a
+ class="s-icon-button icon-reset t-btn-reset"
+ @click="resetFilters"
+ ></a>
+ </span>
+ </div>
+
+ <button class="c-click-icon icon-x t-btn-close c-switcher-menu__close-button"></button>
+</div>
+</template>
+
+<script>
+export default {
+ inject: ['openmct'],
+ data() {
+ return {
+ filters: {
+ brightness: 100,
+ contrast: 100
+ }
+ };
+ },
+ methods: {
+ handleClose(e) {
+ const closeButton = e.target.classList.contains('c-switcher-menu__close-button');
+ if (!closeButton) {
+ e.stopPropagation();
+ }
+ },
+ notifyFiltersChanged() {
+ this.$emit('filterChanged', this.filters);
+ },
+ resetFilters() {
+ this.filters = {
+ brightness: 100,
+ contrast: 100
+ };
+ this.notifyFiltersChanged();
+ }
+ }
+};
+</script>
diff --git a/src/plugins/imagery/components/ImageControls.vue b/src/plugins/imagery/components/ImageControls.vue
new file mode 100644
index 000000000..96cf702bc
--- /dev/null
+++ b/src/plugins/imagery/components/ImageControls.vue
@@ -0,0 +1,283 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+<template>
+<div
+ class="h-local-controls h-local-controls--overlay-content h-local-controls--menus-aligned c-local-controls--show-on-hover"
+ role="toolbar"
+ aria-label="Image controls"
+>
+ <imagery-view-menu-switcher
+ :icon-class="'icon-brightness'"
+ :title="'Brightness and contrast'"
+ >
+ <filter-settings @filterChanged="updateFilterValues" />
+ </imagery-view-menu-switcher>
+
+ <imagery-view-menu-switcher
+ v-if="layers.length"
+ :icon-class="'icon-layers'"
+ :title="'Layers'"
+ >
+ <layer-settings
+ :layers="layers"
+ @toggleLayerVisibility="toggleLayerVisibility"
+ />
+ </imagery-view-menu-switcher>
+
+ <zoom-settings
+ class="--hide-if-less-than-220"
+ :pan-zoom-locked="panZoomLocked"
+ :zoom-factor="zoomFactor"
+ @zoomOut="zoomOut"
+ @zoomIn="zoomIn"
+ @toggleZoomLock="toggleZoomLock"
+ @handleResetImage="handleResetImage"
+ />
+
+ <imagery-view-menu-switcher
+ class="--show-if-less-than-220"
+ :icon-class="'icon-magnify'"
+ :title="'Zoom settings'"
+ >
+ <zoom-settings
+ :pan-zoom-locked="panZoomLocked"
+ :class="'c-control-menu c-menu--has-close-btn'"
+ :zoom-factor="zoomFactor"
+ :is-menu="true"
+ @zoomOut="zoomOut"
+ @zoomIn="zoomIn"
+ @toggleZoomLock="toggleZoomLock"
+ @handleResetImage="handleResetImage"
+ />
+ </imagery-view-menu-switcher>
+</div>
+</template>
+
+<script>
+import _ from 'lodash';
+
+import FilterSettings from "./FilterSettings.vue";
+import LayerSettings from "./LayerSettings.vue";
+import ZoomSettings from "./ZoomSettings.vue";
+import ImageryViewMenuSwitcher from "./ImageryViewMenuSwitcher.vue";
+
+const DEFAULT_FILTER_VALUES = {
+ brightness: '100',
+ contrast: '100'
+};
+
+const ZOOM_LIMITS_MAX_DEFAULT = 20;
+const ZOOM_LIMITS_MIN_DEFAULT = 1;
+const ZOOM_STEP = 1;
+const ZOOM_WHEEL_SENSITIVITY_REDUCTION = 0.01;
+
+export default {
+ components: {
+ FilterSettings,
+ LayerSettings,
+ ImageryViewMenuSwitcher,
+ ZoomSettings
+ },
+ inject: ['openmct', 'domainObject'],
+ props: {
+ layers: {
+ type: Array,
+ required: true
+ },
+ zoomFactor: {
+ type: Number,
+ required: true
+ },
+ imageUrl: {
+ type: String,
+ default: () => {
+ return '';
+ }
+ }
+ },
+ data() {
+ return {
+ altPressed: false,
+ shiftPressed: false,
+ metaPressed: false,
+ panZoomLocked: false,
+ wheelZooming: false,
+ filters: {
+ brightness: 100,
+ contrast: 100
+ }
+ };
+ },
+ computed: {
+ cursorStates() {
+ const isPannable = this.altPressed && this.zoomFactor > 1;
+ const showCursorZoomIn = this.metaPressed && !this.shiftPressed;
+ const showCursorZoomOut = this.metaPressed && this.shiftPressed;
+ const modifierKeyPressed = Boolean(this.metaPressed || this.shiftPressed || this.altPressed);
+
+ return {
+ isPannable,
+ showCursorZoomIn,
+ showCursorZoomOut,
+ modifierKeyPressed
+ };
+ }
+ },
+ watch: {
+ imageUrl(newUrl, oldUrl) {
+ // reset image pan/zoom if newUrl only if not locked
+ if (newUrl && !this.panZoomLocked) {
+ this.$emit('resetImage');
+ }
+ },
+ cursorStates(states) {
+ this.$emit('cursorsUpdated', states);
+ }
+ },
+ mounted() {
+ document.addEventListener('keydown', this.handleKeyDown);
+ document.addEventListener('keyup', this.handleKeyUp);
+ this.clearWheelZoom = _.debounce(this.clearWheelZoom, 600);
+ },
+ beforeDestroy() {
+ document.removeEventListener('keydown', this.handleKeyDown);
+ document.removeEventListener('keyup', this.handleKeyUp);
+ },
+ methods: {
+ handleResetImage() {
+ this.$emit('resetImage');
+ },
+ handleUpdatePanZoom(options) {
+ this.$emit('panZoomUpdated', options);
+ },
+ toggleZoomLock() {
+ this.panZoomLocked = !this.panZoomLocked;
+ },
+ notifyFiltersChanged() {
+ this.$emit('filtersUpdated', this.filters);
+ },
+ handleResetFilters() {
+ this.filters = {...DEFAULT_FILTER_VALUES};
+ this.notifyFiltersChanged();
+ },
+ limitZoomRange(factor) {
+ return Math.min(Math.max(ZOOM_LIMITS_MIN_DEFAULT, factor), ZOOM_LIMITS_MAX_DEFAULT);
+ },
+ // used to increment the zoom without knowledge of current level
+ processZoom(increment, userCoordX, userCoordY) {
+ const newFactor = this.limitZoomRange(this.zoomFactor + increment);
+ this.zoomImage(newFactor, userCoordX, userCoordY);
+ },
+ zoomImage(newScaleFactor, screenClientX, screenClientY) {
+ if (!(newScaleFactor || Number.isInteger(newScaleFactor))) {
+ console.error('Scale factor provided is invalid');
+
+ return;
+ }
+
+ if (newScaleFactor > ZOOM_LIMITS_MAX_DEFAULT) {
+ newScaleFactor = ZOOM_LIMITS_MAX_DEFAULT;
+ }
+
+ if (newScaleFactor <= 0 || newScaleFactor <= ZOOM_LIMITS_MIN_DEFAULT) {
+ return this.handleResetImage();
+ }
+
+ this.handleUpdatePanZoom({
+ newScaleFactor,
+ screenClientX,
+ screenClientY
+ });
+ },
+ wheelZoom(e) {
+ // only use x,y coordinates on scrolling in
+ if (this.wheelZooming === false && e.deltaY > 0) {
+ this.wheelZooming = true;
+ // grab first x,y coordinates
+ this.processZoom(e.deltaY * ZOOM_WHEEL_SENSITIVITY_REDUCTION, e.clientX, e.clientY);
+ } else {
+ // ignore subsequent event x,y so scroll drift doesn't occur
+ this.processZoom(e.deltaY * ZOOM_WHEEL_SENSITIVITY_REDUCTION);
+ }
+
+ // debounced method that will only fire after the scroll series is complete
+ this.clearWheelZoom();
+ },
+ /* debounced method so that wheelZooming state will
+ ** remain true through a zoom event series
+ */
+ clearWheelZoom() {
+ this.wheelZooming = false;
+ },
+ handleKeyDown(event) {
+ if (event.key === 'Alt') {
+ this.altPressed = true;
+ }
+
+ if (event.metaKey) {
+ this.metaPressed = true;
+ }
+
+ if (event.shiftKey) {
+ this.shiftPressed = true;
+ }
+ },
+ handleKeyUp(event) {
+ if (event.key === 'Alt') {
+ this.altPressed = false;
+ }
+
+ this.shiftPressed = false;
+ if (!event.metaKey) {
+ this.metaPressed = false;
+ }
+ },
+ zoomIn() {
+ this.processZoom(ZOOM_STEP);
+ },
+ zoomOut() {
+ this.processZoom(-ZOOM_STEP);
+ },
+ // attached to onClick listener in ImageryView
+ handlePanZoomClick(e) {
+ if (this.altPressed) {
+ return this.$emit('startPan', e);
+ }
+
+ if (!(this.metaPressed && e.button === 0)) {
+ return;
+ }
+
+ const newScaleFactor = this.zoomFactor + (this.shiftPressed ? -ZOOM_STEP : ZOOM_STEP);
+ this.zoomImage(newScaleFactor, e.clientX, e.clientY);
+ },
+ toggleLayerVisibility(index) {
+ this.$emit('toggleLayerVisibility', index);
+ },
+ updateFilterValues(filters) {
+ this.filters = filters;
+ this.notifyFiltersChanged();
+ }
+ }
+};
+</script>
diff --git a/src/plugins/imagery/components/ImageryTimeView.vue b/src/plugins/imagery/components/ImageryTimeView.vue
index 14e317fff..ccaf7b71b 100644
--- a/src/plugins/imagery/components/ImageryTimeView.vue
+++ b/src/plugins/imagery/components/ImageryTimeView.vue
@@ -1,5 +1,5 @@
<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
+ Open MCT, Copyright (c) 2014-2022, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
@@ -21,11 +21,13 @@
-->
<template>
-<div ref="imagery"
- class="c-imagery-tsv c-timeline-holder"
+<div
+ ref="imagery"
+ class="c-imagery-tsv c-timeline-holder"
>
- <div ref="imageryHolder"
- class="c-imagery-tsv__contents u-contents"
+ <div
+ ref="imageryHolder"
+ class="c-imagery-tsv__contents u-contents"
>
</div>
</div>
diff --git a/src/plugins/imagery/components/ImageryView.vue b/src/plugins/imagery/components/ImageryView.vue
index d2698dd7a..66d53669b 100644
--- a/src/plugins/imagery/components/ImageryView.vue
+++ b/src/plugins/imagery/components/ImageryView.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -28,53 +28,68 @@
@keydown="arrowDownHandler"
@mouseover="focusElement"
>
- <div class="c-imagery__main-image-wrapper has-local-controls">
- <div class="h-local-controls h-local-controls--overlay-content c-local-controls--show-on-hover c-image-controls__controls">
- <span class="c-image-controls__sliders"
- draggable="true"
- @dragstart="startDrag"
- >
- <div class="c-image-controls__slider-wrapper icon-brightness">
- <input v-model="filters.brightness"
- type="range"
- min="0"
- max="500"
- >
- </div>
- <div class="c-image-controls__slider-wrapper icon-contrast">
- <input v-model="filters.contrast"
- type="range"
- min="0"
- max="500"
- >
- </div>
- </span>
- <span class="t-reset-btn-holder c-imagery__lc__reset-btn c-image-controls__btn-reset">
- <a class="s-icon-button icon-reset t-btn-reset"
- @click="filters={brightness: 100, contrast: 100}"
- ></a>
- </span>
- </div>
- <div ref="imageBG"
- class="c-imagery__main-image__bg"
- :class="{'paused unnsynced': isPaused && !isFixed,'stale':false }"
- @click="expand"
+ <div
+ class="c-imagery__main-image-wrapper has-local-controls"
+ :class="imageWrapperStyle"
+ @mousedown="handlePanZoomClick"
+ >
+ <ImageControls
+ ref="imageControls"
+ :zoom-factor="zoomFactor"
+ :image-url="imageUrl"
+ :layers="layers"
+ @resetImage="resetImage"
+ @panZoomUpdated="handlePanZoomUpdate"
+ @filtersUpdated="setFilters"
+ @cursorsUpdated="setCursorStates"
+ @startPan="startPan"
+ @toggleLayerVisibility="toggleLayerVisibility"
+ />
+ <div
+ ref="imageBG"
+ class="c-imagery__main-image__bg"
+ @click="expand"
>
- <div class="image-wrapper"
- :style="{
- 'width': `${sizedImageDimensions.width}px`,
- 'height': `${sizedImageDimensions.height}px`
- }"
+ <div
+ v-if="zoomFactor > 1"
+ class="c-imagery__hints"
+ >
+ {{ formatImageAltText }}
+ </div>
+ <div
+ ref="focusedImageWrapper"
+ class="image-wrapper"
+ :style="{
+ 'width': `${sizedImageWidth}px`,
+ 'height': `${sizedImageHeight}px`
+ }"
+ @mousedown="handlePanZoomClick"
>
- <img ref="focusedImage"
- class="c-imagery__main-image__image js-imageryView-image"
- :src="imageUrl"
- :style="{
- 'filter': `brightness(${filters.brightness}%) contrast(${filters.contrast}%)`
- }"
- :data-openmct-image-timestamp="time"
- :data-openmct-object-keystring="keyString"
+ <div
+ v-for="(layer, index) in visibleLayers"
+ :key="index"
+ class="layer-image s-image-layer c-imagery__layer-image js-layer-image"
+ :style="getVisibleLayerStyles(layer)"
+ >
+ </div>
+ <img
+ ref="focusedImage"
+ class="c-imagery__main-image__image js-imageryView-image "
+ :src="imageUrl"
+ :draggable="!isSelectable"
+ :style="{
+ 'filter': `brightness(${filters.brightness}%) contrast(${filters.contrast}%)`
+ }"
+ :data-openmct-image-timestamp="time"
+ :data-openmct-object-keystring="keyString"
>
+ <div
+ v-if="imageUrl"
+ ref="focusedImageElement"
+ class="c-imagery__main-image__background-image"
+ :draggable="!isSelectable"
+ :style="focusImageStyles"
+ ></div>
<Compass
v-if="shouldDisplayCompass"
:compass-rose-sizing-classes="compassRoseSizingClasses"
@@ -85,16 +100,18 @@
</div>
</div>
- <button class="c-local-controls c-local-controls--show-on-hover c-imagery__prev-next-button c-nav c-nav--prev"
- title="Previous image"
- :disabled="isPrevDisabled"
- @click="prevImage()"
+ <button
+ class="c-local-controls c-local-controls--show-on-hover c-imagery__prev-next-button c-nav c-nav--prev"
+ title="Previous image"
+ :disabled="isPrevDisabled"
+ @click="prevImage()"
></button>
- <button class="c-local-controls c-local-controls--show-on-hover c-imagery__prev-next-button c-nav c-nav--next"
- title="Next image"
- :disabled="isNextDisabled"
- @click="nextImage()"
+ <button
+ class="c-local-controls c-local-controls--show-on-hover c-imagery__prev-next-button c-nav c-nav--next"
+ title="Next image"
+ :disabled="isNextDisabled"
+ @click="nextImage()"
></button>
<div class="c-imagery__control-bar">
@@ -104,6 +121,10 @@
<!-- image fresh -->
<div
v-if="canTrackDuration"
+ :style="{
+ 'animation-delay': imageFreshnessOptions.fadeOutDelayTime,
+ 'animation-duration': imageFreshnessOptions.fadeOutDurationTime
+ }"
:class="{'c-imagery--new': isImageNew && !refreshCSS}"
class="c-imagery__age icon-timer"
>{{ formattedDuration }}</div>
@@ -111,13 +132,13 @@
<!-- spacecraft position fresh -->
<div
v-if="relatedTelemetry.hasRelatedTelemetry && isSpacecraftPositionFresh"
- class="c-imagery__age icon-check c-imagery--new"
+ class="c-imagery__age icon-check c-imagery--new no-animation"
>POS</div>
<!-- camera position fresh -->
<div
v-if="relatedTelemetry.hasRelatedTelemetry && isCameraPositionFresh"
- class="c-imagery__age icon-check c-imagery--new"
+ class="c-imagery__age icon-check c-imagery--new no-animation"
>CAM</div>
</div>
<div class="h-local-controls">
@@ -125,34 +146,42 @@
v-if="!isFixed"
class="c-button icon-pause pause-play"
:class="{'is-paused': isPaused}"
- @click="paused(!isPaused, 'button')"
+ @click="paused(!isPaused)"
></button>
</div>
</div>
</div>
- <div class="c-imagery__thumbs-wrapper"
- :class="[
- { 'is-paused': isPaused && !isFixed },
- { 'is-autoscroll-off': !resizingWindow && !autoScroll && !isPaused }
- ]"
+ <div
+ v-if="displayThumbnails"
+ class="c-imagery__thumbs-wrapper"
+ :class="[
+ { 'is-paused': isPaused && !isFixed },
+ { 'is-autoscroll-off': !resizingWindow && !autoScroll && !isPaused },
+ { 'is-small-thumbs': displayThumbnailsSmall },
+ { 'hide': !displayThumbnails }
+ ]"
>
<div
ref="thumbsWrapper"
class="c-imagery__thumbs-scroll-area"
@scroll="handleScroll"
>
- <div v-for="(image, index) in imageHistory"
- :key="image.url + image.time"
- class="c-imagery__thumb c-thumb"
- :class="{ selected: focusedImageIndex === index && isPaused }"
- @click="setFocusedImage(index, thumbnailClick)"
+ <div
+ v-for="(image, index) in imageHistory"
+ :key="image.url + image.time"
+ class="c-imagery__thumb c-thumb"
+ :class="{ selected: focusedImageIndex === index && isPaused }"
+ :title="image.formattedTime"
+ @click="thumbnailClicked(index)"
>
- <a href=""
- :download="image.imageDownloadName"
- @click.prevent
+ <a
+ href=""
+ :download="image.imageDownloadName"
+ @click.prevent
>
- <img class="c-thumb__image"
- :src="image.url"
+ <img
+ class="c-thumb__image"
+ :src="image.url"
>
</a>
<div class="c-thumb__timestamp">{{ image.formattedTime }}</div>
@@ -169,12 +198,13 @@
</template>
<script>
+import eventHelpers from '../lib/eventHelpers';
import _ from 'lodash';
import moment from 'moment';
import RelatedTelemetry from './RelatedTelemetry/RelatedTelemetry';
import Compass from './Compass/Compass.vue';
-
+import ImageControls from './ImageControls.vue';
import imageryData from "../../imagery/mixins/imageryData";
const REFRESH_CSS_MS = 500;
@@ -194,12 +224,17 @@ const ARROW_LEFT = 37;
const SCROLL_LATENCY = 250;
+const ZOOM_SCALE_DEFAULT = 1;
+const SHOW_THUMBS_THRESHOLD_HEIGHT = 200;
+const SHOW_THUMBS_FULLSIZE_THRESHOLD_HEIGHT = 600;
+
export default {
components: {
- Compass
+ Compass,
+ ImageControls
},
mixins: [imageryData],
- inject: ['openmct', 'domainObject', 'objectPath', 'currentView'],
+ inject: ['openmct', 'domainObject', 'objectPath', 'currentView', 'imageFreshnessOptions'],
props: {
focusedImageTimestamp: {
type: Number,
@@ -214,15 +249,14 @@ export default {
this.requestCount = 0;
return {
+ timeFormat: '',
+ layers: [],
+ visibleLayers: [],
durationFormatter: undefined,
imageHistory: [],
timeSystem: timeSystem,
keyString: undefined,
autoScroll: true,
- filters: {
- brightness: 100,
- contrast: 100
- },
thumbnailClick: THUMBNAIL_CLICKED,
isPaused: false,
refreshCSS: false,
@@ -234,32 +268,100 @@ export default {
focusedImageNaturalAspectRatio: undefined,
imageContainerWidth: undefined,
imageContainerHeight: undefined,
+ sizedImageWidth: 0,
+ sizedImageHeight: 0,
+ viewHeight: 0,
lockCompass: true,
resizingWindow: false,
- timeContext: undefined
+ timeContext: undefined,
+ zoomFactor: ZOOM_SCALE_DEFAULT,
+ filters: {
+ brightness: 100,
+ contrast: 100
+ },
+ cursorStates: {
+ isPannable: false,
+ showCursorZoomIn: false,
+ showCursorZoomOut: false,
+ modifierKeyPressed: false
+ },
+ imageTranslateX: 0,
+ imageTranslateY: 0,
+ pan: undefined,
+ animateZoom: true,
+ imagePanned: false,
+ forceShowThumbnails: false
};
},
computed: {
compassRoseSizingClasses() {
let compassRoseSizingClasses = '';
- if (this.sizedImageDimensions.width < 300) {
+ if (this.sizedImageWidth < 300) {
compassRoseSizingClasses = '--rose-small --rose-min';
- } else if (this.sizedImageDimensions.width < 500) {
+ } else if (this.sizedImageWidth < 500) {
compassRoseSizingClasses = '--rose-small';
- } else if (this.sizedImageDimensions.width > 1000) {
+ } else if (this.sizedImageWidth > 1000) {
compassRoseSizingClasses = '--rose-max';
}
return compassRoseSizingClasses;
},
+ displayThumbnails() {
+ return (
+ this.forceShowThumbnails
+ || this.viewHeight >= SHOW_THUMBS_THRESHOLD_HEIGHT
+ );
+ },
+ displayThumbnailsSmall() {
+ return this.viewHeight > SHOW_THUMBS_THRESHOLD_HEIGHT && this.viewHeight <= SHOW_THUMBS_FULLSIZE_THRESHOLD_HEIGHT;
+ },
+ focusImageStyles() {
+ return {
+ 'filter': `brightness(${this.filters.brightness}%) contrast(${this.filters.contrast}%)`,
+ 'background-image':
+ `${this.imageUrl ? (
+ `url(${this.imageUrl}),
+ repeating-linear-gradient(
+ 45deg,
+ transparent,
+ transparent 4px,
+ rgba(125,125,125,.2) 4px,
+ rgba(125,125,125,.2) 8px
+ )`
+ ) : ''}`,
+ 'transform': `scale(${this.zoomFactor}) translate(${this.imageTranslateX}px, ${this.imageTranslateY}px)`,
+ 'transition': `${!this.pan && this.animateZoom ? 'transform 250ms ease-in' : 'initial'}`,
+ 'width': `${this.sizedImageWidth}px`,
+ 'height': `${this.sizedImageHeight}px`
+ };
+ },
time() {
return this.formatTime(this.focusedImage);
},
imageUrl() {
return this.formatImageUrl(this.focusedImage);
},
+ imageWrapperStyle() {
+ return {
+ 'cursor-zoom-in': this.cursorStates.showCursorZoomIn,
+ 'cursor-zoom-out': this.cursorStates.showCursorZoomOut,
+ 'pannable': this.cursorStates.isPannable,
+ 'paused unnsynced': this.isPaused && !this.isFixed,
+ 'stale': false
+ };
+ },
isImageNew() {
let cutoff = FIVE_MINUTES;
+ if (this.imageFreshnessOptions) {
+ const { fadeOutDelayTime, fadeOutDurationTime} = this.imageFreshnessOptions;
+ // convert css duration to IS8601 format for parsing
+ const isoFormattedDuration = 'PT' + fadeOutDurationTime.toUpperCase();
+ const isoFormattedDelay = 'PT' + fadeOutDelayTime.toUpperCase();
+ const parsedDuration = moment.duration(isoFormattedDuration).asMilliseconds();
+ const parsedDelay = moment.duration(isoFormattedDelay).asMilliseconds();
+ cutoff = parsedDuration + parsedDelay;
+ }
+
let age = this.numericDuration;
return age < cutoff && !this.refreshCSS;
@@ -301,6 +403,9 @@ export default {
formattedDuration() {
let result = 'N/A';
let negativeAge = -1;
+ if (!Number.isInteger(this.numericDuration)) {
+ return result;
+ }
if (this.numericDuration > TWENTYFOUR_HOURS) {
negativeAge *= (this.numericDuration / TWENTYFOUR_HOURS);
@@ -315,10 +420,18 @@ export default {
return result;
},
shouldDisplayCompass() {
- return this.focusedImage !== undefined
+ const imageHeightAndWidth = this.sizedImageHeight !== 0
+ && this.sizedImageWidth !== 0;
+
+ const display = this.focusedImage !== undefined
&& this.focusedImageNaturalAspectRatio !== undefined
&& this.imageContainerWidth !== undefined
- && this.imageContainerHeight !== undefined;
+ && this.imageContainerHeight !== undefined
+ && imageHeightAndWidth
+ && this.zoomFactor === 1
+ && this.imagePanned !== true;
+
+ return display;
},
isSpacecraftPositionFresh() {
let isFresh = undefined;
@@ -380,20 +493,6 @@ export default {
return isFresh;
},
- sizedImageDimensions() {
- let sizedImageDimensions = {};
- if ((this.imageContainerWidth / this.imageContainerHeight) > this.focusedImageNaturalAspectRatio) {
- // container is wider than image
- sizedImageDimensions.width = this.imageContainerHeight * this.focusedImageNaturalAspectRatio;
- sizedImageDimensions.height = this.imageContainerHeight;
- } else {
- // container is taller than image
- sizedImageDimensions.width = this.imageContainerWidth;
- sizedImageDimensions.height = this.imageContainerWidth / this.focusedImageNaturalAspectRatio;
- }
-
- return sizedImageDimensions;
- },
isFixed() {
let clock;
if (this.timeContext) {
@@ -403,6 +502,26 @@ export default {
}
return clock === undefined;
+ },
+ isSelectable() {
+ return true;
+
+ },
+ sizedImageDimensions() {
+ return {
+ width: this.sizedImageWidth,
+ height: this.sizedImageHeight
+ };
+ },
+ formatImageAltText() {
+ const regexLinux = /Linux/;
+ const navigator = window.navigator.userAgent;
+
+ if (regexLinux.test(navigator)) {
+ return 'Ctrl+Alt drag to pan';
+ }
+
+ return 'Alt drag to pan';
}
},
watch: {
@@ -411,16 +530,37 @@ export default {
const newSize = newHistory.length;
let imageIndex;
if (this.focusedImageTimestamp !== undefined) {
- const foundImageIndex = this.imageHistory.findIndex(image => {
- return image.time === this.focusedImageTimestamp;
- });
- imageIndex = foundImageIndex > -1 ? foundImageIndex : newSize - 1;
+ const foundImageIndex = newHistory.findIndex(img => img.time === this.focusedImageTimestamp);
+ imageIndex = foundImageIndex > -1
+ ? foundImageIndex
+ : newSize - 1;
} else {
- imageIndex = newSize > 0 ? newSize - 1 : undefined;
+ imageIndex = newSize > 0
+ ? newSize - 1
+ : undefined;
}
- this.setFocusedImage(imageIndex, false);
- this.scrollToRight();
+ this.nextImageIndex = imageIndex;
+
+ if (this.previousFocusedImage && newHistory.length) {
+ const matchIndex = this.matchIndexOfPreviousImage(
+ this.previousFocusedImage,
+ newHistory
+ );
+
+ if (matchIndex > -1) {
+ this.setFocusedImage(matchIndex);
+ } else {
+ this.paused();
+ }
+ }
+
+ if (!this.isPaused) {
+ this.setFocusedImage(imageIndex);
+ this.scrollToRight();
+ } else {
+ this.scrollToFocused();
+ }
},
deep: true
},
@@ -432,6 +572,10 @@ export default {
}
},
async mounted() {
+ eventHelpers.extend(this);
+ this.focusedImageWrapper = this.$refs.focusedImageWrapper;
+ this.focusedImageElement = this.$refs.focusedImageElement;
+
//We only need to use this till the user focuses an image manually
if (this.focusedImageTimestamp !== undefined) {
this.isPaused = true;
@@ -455,7 +599,7 @@ export default {
this.updateRelatedTelemetryForFocusedImage = _.debounce(this.updateRelatedTelemetryForFocusedImage, 400);
// for resizing the object view
- this.resizeImageContainer = _.debounce(this.resizeImageContainer, 400);
+ this.resizeImageContainer = _.debounce(this.resizeImageContainer, 400, { leading: true });
if (this.$refs.imageBG) {
this.imageContainerResizeObserver = new ResizeObserver(this.resizeImageContainer);
@@ -472,8 +616,11 @@ export default {
this.thumbWrapperResizeObserver.observe(this.$refs.thumbsWrapper);
}
+ this.listenTo(this.focusedImageWrapper, 'wheel', this.wheelZoom, this);
+ this.loadVisibleLayers();
},
beforeDestroy() {
+ this.persistVisibleLayers();
this.stopFollowingTimeContext();
if (this.thumbWrapperResizeObserver) {
@@ -496,8 +643,21 @@ export default {
}
}
}
+
+ this.stopListening(this.focusedImageWrapper, 'wheel', this.wheelZoom, this);
+
},
methods: {
+ calculateViewHeight() {
+ this.viewHeight = this.$el.clientHeight;
+ },
+ getVisibleLayerStyles(layer) {
+ return {
+ 'background-image': `url(${layer.source})`,
+ 'transform': `scale(${this.zoomFactor}) translate(${this.imageTranslateX}px, ${this.imageTranslateY}px)`,
+ 'transition': `${!this.pan && this.animateZoom ? 'transform 250ms ease-in' : 'initial'}`
+ };
+ },
setTimeContext() {
this.stopFollowingTimeContext();
this.timeContext = this.openmct.time.getContextForView(this.objectPath);
@@ -512,6 +672,11 @@ export default {
}
},
expand() {
+ // check for modifier keys so it doesnt interfere with the layout
+ if (this.cursorStates.modifierKeyPressed) {
+ return;
+ }
+
const actionCollection = this.openmct.actions.getActionsCollection(this.objectPath, this.currentView);
const visibleActions = actionCollection.getVisibleActions();
const viewLargeAction = visibleActions
@@ -561,6 +726,37 @@ export default {
return mostRecent[valueKey];
},
+ loadVisibleLayers() {
+ const metaDataValues = this.metadata.valuesForHints(['image'])[0];
+ this.imageFormat = this.openmct.telemetry.getValueFormatter(metaDataValues);
+ let layersMetadata = metaDataValues.layers;
+ if (layersMetadata) {
+ this.layers = layersMetadata;
+ if (this.domainObject.configuration) {
+ let persistedLayers = this.domainObject.configuration.layers;
+ layersMetadata.forEach((layer) => {
+ const persistedLayer = persistedLayers.find(object => object.name === layer.name);
+ if (persistedLayer) {
+ layer.visible = persistedLayer.visible === true;
+ }
+ });
+ this.visibleLayers = this.layers.filter(layer => layer.visible);
+ } else {
+ this.visibleLayers = [];
+ this.layers.forEach((layer) => {
+ layer.visible = false;
+ });
+ }
+ }
+ },
+ persistVisibleLayers() {
+ if (this.domainObject.configuration) {
+ this.openmct.objects.mutate(this.domainObject, 'configuration.layers', this.layers);
+ }
+
+ this.visibleLayers = [];
+ this.layers = [];
+ },
// will subscribe to data for this key if not already done
subscribeToDataForKey(key) {
if (this.relatedTelemetry[key].isSubscribed) {
@@ -617,6 +813,7 @@ export default {
focusElement() {
this.$el.focus();
},
+
handleScroll() {
const thumbsWrapper = this.$refs.thumbsWrapper;
if (!thumbsWrapper || this.resizingWindow) {
@@ -627,20 +824,15 @@ export default {
const disableScroll = scrollWidth > Math.ceil(scrollLeft + clientWidth);
this.autoScroll = !disableScroll;
},
- paused(state, type) {
- this.isPaused = state;
+ paused(state) {
+ this.isPaused = Boolean(state);
- if (type === 'button') {
- this.setFocusedImage(this.imageHistory.length - 1);
- }
-
- if (this.nextImageIndex) {
+ if (!state) {
+ this.previousFocusedImage = null;
this.setFocusedImage(this.nextImageIndex);
- delete this.nextImageIndex;
+ this.autoScroll = true;
+ this.scrollToRight();
}
-
- this.autoScroll = true;
- this.scrollToRight();
},
scrollToFocused() {
const thumbsWrapper = this.$refs.thumbsWrapper;
@@ -679,51 +871,24 @@ export default {
&& x.time === previous.time
));
},
- setFocusedImage(index, thumbnailClick = false) {
- let focusedIndex = index;
- if (!(Number.isInteger(index) && index > -1)) {
- return;
- }
-
- if (thumbnailClick) {
- //We use the props till the user changes what they want to see
- this.focusedImageTimestamp = undefined;
- //set the previousFocusedImage when a user chooses an image
- this.previousFocusedImage = this.imageHistory[focusedIndex] ? JSON.parse(JSON.stringify(this.imageHistory[focusedIndex])) : undefined;
- }
-
- if (this.previousFocusedImage) {
- // determine if the previous image exists in the new bounds of imageHistory
- if (!thumbnailClick) {
- const matchIndex = this.matchIndexOfPreviousImage(
- this.previousFocusedImage,
- this.imageHistory
- );
- focusedIndex = matchIndex > -1 ? matchIndex : this.imageHistory.length - 1;
- }
-
- if (!(this.isPaused || thumbnailClick)
- || focusedIndex === this.imageHistory.length - 1) {
- delete this.previousFocusedImage;
- }
- }
-
- this.focusedImageIndex = focusedIndex;
-
- //TODO: do we even need this anymore?
- if (this.isPaused && !thumbnailClick && this.focusedImageTimestamp === undefined) {
- this.nextImageIndex = focusedIndex;
- //this could happen if bounds changes
- if (this.focusedImageIndex > this.imageHistory.length - 1) {
- this.focusedImageIndex = focusedIndex;
- }
+ thumbnailClicked(index) {
+ this.setFocusedImage(index);
+ this.paused(true);
+ this.setPreviousFocusedImage(index);
+ },
+ setPreviousFocusedImage(index) {
+ this.focusedImageTimestamp = undefined;
+ this.previousFocusedImage = this.imageHistory[index]
+ ? JSON.parse(JSON.stringify(this.imageHistory[index]))
+ : undefined;
+ },
+ setFocusedImage(index) {
+ if (!(Number.isInteger(index) && index > -1)) {
return;
}
- if (thumbnailClick && !this.isPaused) {
- this.paused(true);
- }
+ this.focusedImageIndex = index;
},
trackDuration() {
if (this.canTrackDuration) {
@@ -743,8 +908,10 @@ export default {
let currentTime = this.timeContext.clock() && this.timeContext.clock().currentValue();
if (currentTime === undefined) {
this.numericDuration = currentTime;
- } else {
+ } else if (Number.isInteger(this.parsedSelectedTime)) {
this.numericDuration = currentTime - this.parsedSelectedTime;
+ } else {
+ this.numericDuration = undefined;
}
},
resetAgeCSS() {
@@ -761,7 +928,7 @@ export default {
let index = this.focusedImageIndex;
- this.setFocusedImage(++index, THUMBNAIL_CLICKED);
+ this.thumbnailClicked(++index);
if (index === this.imageHistory.length - 1) {
this.paused(false);
}
@@ -774,14 +941,46 @@ export default {
let index = this.focusedImageIndex;
if (index === this.imageHistory.length - 1) {
- this.setFocusedImage(this.imageHistory.length - 2, THUMBNAIL_CLICKED);
+ this.thumbnailClicked(this.imageHistory.length - 2);
} else {
- this.setFocusedImage(--index, THUMBNAIL_CLICKED);
+ this.thumbnailClicked(--index);
}
},
- startDrag(e) {
- e.preventDefault();
- e.stopPropagation();
+ resetImage() {
+ this.imagePanned = false;
+ this.zoomFactor = ZOOM_SCALE_DEFAULT;
+ this.imageTranslateX = 0;
+ this.imageTranslateY = 0;
+ },
+ handlePanZoomUpdate({ newScaleFactor, screenClientX, screenClientY }) {
+ if (!(screenClientX || screenClientY)) {
+ return this.updatePanZoom(newScaleFactor, 0, 0);
+ }
+
+ // handle mouse events
+ const imageRect = this.focusedImageWrapper.getBoundingClientRect();
+ const imageContainerX = screenClientX - imageRect.left;
+ const imageContainerY = screenClientY - imageRect.top;
+ const offsetFromCenterX = (imageRect.width / 2) - imageContainerX;
+ const offsetFromCenterY = (imageRect.height / 2) - imageContainerY;
+
+ this.updatePanZoom(newScaleFactor, offsetFromCenterX, offsetFromCenterY);
+ },
+ updatePanZoom(newScaleFactor, offsetFromCenterX, offsetFromCenterY) {
+ const currentScale = this.zoomFactor;
+ const previousTranslateX = this.imageTranslateX;
+ const previousTranslateY = this.imageTranslateY;
+
+ const offsetXInOriginalScale = offsetFromCenterX / currentScale;
+ const offsetYInOriginalScale = offsetFromCenterY / currentScale;
+ const translateX = offsetXInOriginalScale + previousTranslateX;
+ const translateY = offsetYInOriginalScale + previousTranslateY;
+ this.imageTranslateX = translateX;
+ this.imageTranslateY = translateY;
+ this.zoomFactor = newScaleFactor;
+ },
+ handlePanZoomClick(e) {
+ this.$refs.imageControls.handlePanZoomClick(e);
},
arrowDownHandler(event) {
let key = event.keyCode;
@@ -844,7 +1043,7 @@ export default {
// TODO - should probably cache this
img.addEventListener('load', () => {
- this.focusedImageNaturalAspectRatio = img.naturalWidth / img.naturalHeight;
+ this.setSizedImageDimensions();
}, { once: true });
},
resizeImageContainer() {
@@ -859,6 +1058,23 @@ export default {
if (this.$refs.imageBG.clientHeight !== this.imageContainerHeight) {
this.imageContainerHeight = this.$refs.imageBG.clientHeight;
}
+
+ this.setSizedImageDimensions();
+ this.calculateViewHeight();
+ this.scrollToFocused();
+ },
+ setSizedImageDimensions() {
+ this.focusedImageNaturalAspectRatio = this.$refs.focusedImage.naturalWidth / this.$refs.focusedImage.naturalHeight;
+
+ if ((this.imageContainerWidth / this.imageContainerHeight) > this.focusedImageNaturalAspectRatio) {
+ // container is wider than image
+ this.sizedImageWidth = this.imageContainerHeight * this.focusedImageNaturalAspectRatio;
+ this.sizedImageHeight = this.imageContainerHeight;
+ } else {
+ // container is taller than image
+ this.sizedImageWidth = this.imageContainerWidth;
+ this.sizedImageHeight = this.imageContainerWidth / this.focusedImageNaturalAspectRatio;
+ }
},
handleThumbWindowResizeStart() {
if (!this.autoScroll) {
@@ -874,9 +1090,79 @@ export default {
this.scrollToRight('reset');
}
+ this.calculateViewHeight();
+
this.$nextTick(() => {
this.resizingWindow = false;
});
+ },
+ clearWheelZoom() {
+ this.$refs.imageControls.clearWheelZoom();
+ },
+ wheelZoom(e) {
+ e.preventDefault();
+
+ this.$refs.imageControls.wheelZoom(e);
+ },
+ startPan(e) {
+ e.preventDefault();
+ if (!this.pan && this.zoomFactor > 1) {
+ this.animateZoom = false;
+ this.imagePanned = true;
+ this.pan = {
+ x: e.clientX,
+ y: e.clientY
+ };
+ this.listenTo(window, 'mouseup', this.onMouseUp, this);
+ this.listenTo(window, 'mousemove', this.trackMousePosition, this);
+ }
+
+ return false;
+ },
+ trackMousePosition(e) {
+ if (!e.altKey) {
+ return this.onMouseUp(e);
+ }
+
+ this.updatePan(e);
+ e.preventDefault();
+
+ },
+ updatePan(e) {
+ if (!this.pan) {
+ return;
+ }
+
+ const dX = e.clientX - this.pan.x;
+ const dY = e.clientY - this.pan.y;
+ this.pan = {
+ x: e.clientX,
+ y: e.clientY
+ };
+ this.updatePanZoom(this.zoomFactor, dX, dY);
+ },
+ endPan() {
+ this.pan = undefined;
+ this.animateZoom = true;
+ },
+ onMouseUp(event) {
+ this.stopListening(window, 'mouseup', this.onMouseUp, this);
+ this.stopListening(window, 'mousemove', this.trackMousePosition, this);
+
+ if (this.pan) {
+ return this.endPan(event);
+ }
+ },
+ setFilters(filtersObj) {
+ this.filters = filtersObj;
+ },
+ setCursorStates(states) {
+ this.cursorStates = states;
+ },
+ toggleLayerVisibility(index) {
+ let isVisible = this.layers[index].visible === true;
+ this.layers[index].visible = !isVisible;
+ this.visibleLayers = this.layers.filter(layer => layer.visible);
}
}
};
diff --git a/src/plugins/imagery/components/ImageryViewMenuSwitcher.vue b/src/plugins/imagery/components/ImageryViewMenuSwitcher.vue
new file mode 100644
index 000000000..784cdff46
--- /dev/null
+++ b/src/plugins/imagery/components/ImageryViewMenuSwitcher.vue
@@ -0,0 +1,65 @@
+<template>
+<div class="c-switcher-menu">
+ <button
+ :id="id"
+ class="c-button c-button--menu c-switcher-menu__button"
+ :class="iconClass"
+ :title="title"
+ @click="toggleMenu"
+ >
+ <span class="c-button__label"></span>
+ </button>
+ <div
+ v-show="showMenu"
+ class="c-switcher-menu__content"
+ >
+ <slot></slot>
+ </div>
+</div>
+</template>
+
+<script>
+import {v4 as uuid} from 'uuid';
+
+export default {
+ inject: ['openmct'],
+ props: {
+ iconClass: {
+ type: String,
+ default() {
+ return '';
+ }
+ },
+ title: {
+ type: String,
+ default() {
+ return '';
+ }
+ }
+ },
+ data() {
+ return {
+ id: uuid(),
+ showMenu: false
+ };
+ },
+ mounted() {
+ document.addEventListener('click', this.hideMenu);
+ },
+ destroyed() {
+ document.removeEventListener('click', this.hideMenu);
+ },
+ methods: {
+ toggleMenu() {
+ this.showMenu = !this.showMenu;
+ },
+ hideMenu(e) {
+ if (this.id === e.target.id) {
+ return;
+ }
+
+ this.showMenu = false;
+ }
+ }
+};
+</script>
diff --git a/src/plugins/imagery/components/LayerSettings.vue b/src/plugins/imagery/components/LayerSettings.vue
new file mode 100644
index 000000000..1e99a0aee
--- /dev/null
+++ b/src/plugins/imagery/components/LayerSettings.vue
@@ -0,0 +1,59 @@
+<template>
+<div
+ class="c-control-menu c-menu--to-left c-menu--has-close-btn c-image-controls"
+ @click="handleClose"
+>
+ <div class="c-checkbox-list js-checkbox-menu c-menu--to-left c-menu--has-close-btn">
+ <ul
+ @click="$event.stopPropagation()"
+ >
+ <li
+ v-for="(layer, index) in layers"
+ :key="index"
+ >
+ <input
+ v-if="layer.visible"
+ :id="index + 'LayerControl'"
+ checked
+ type="checkbox"
+ @change="toggleLayerVisibility(index)"
+ >
+ <input
+ v-else
+ :id="index + 'LayerControl'"
+ type="checkbox"
+ @change="toggleLayerVisibility(index)"
+ >
+ <label :for="index + 'LayerControl'">{{ layer.name }}</label>
+ </li>
+ </ul>
+ </div>
+
+ <button class="c-click-icon icon-x t-btn-close c-switcher-menu__close-button"></button>
+</div>
+</template>
+
+<script>
+export default {
+ inject: ['openmct'],
+ props: {
+ layers: {
+ type: Array,
+ default() {
+ return [];
+ }
+ }
+ },
+ methods: {
+ handleClose(e) {
+ const closeButton = e.target.classList.contains('c-switcher-menu__close-button');
+ if (!closeButton) {
+ e.stopPropagation();
+ }
+ },
+ toggleLayerVisibility(index) {
+ this.$emit('toggleLayerVisibility', index);
+ }
+ }
+};
+</script>
diff --git a/src/plugins/imagery/components/RelatedTelemetry/RelatedTelemetry.js b/src/plugins/imagery/components/RelatedTelemetry/RelatedTelemetry.js
index b4393044c..f874d8129 100644
--- a/src/plugins/imagery/components/RelatedTelemetry/RelatedTelemetry.js
+++ b/src/plugins/imagery/components/RelatedTelemetry/RelatedTelemetry.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/imagery/components/ZoomSettings.vue b/src/plugins/imagery/components/ZoomSettings.vue
new file mode 100644
index 000000000..e53d6289e
--- /dev/null
+++ b/src/plugins/imagery/components/ZoomSettings.vue
@@ -0,0 +1,89 @@
+<template>
+<div
+ class="c-image-controls__controls-wrapper"
+ @click="handleClose"
+>
+ <div class="c-image-controls__control c-image-controls__zoom">
+ <div class="c-button-set c-button-set--strip-h">
+ <button
+ class="c-button t-btn-zoom-out icon-minus"
+ title="Zoom out"
+ @click="zoomOut"
+ ></button>
+
+ <button
+ class="c-button t-btn-zoom-in icon-plus"
+ title="Zoom in"
+ @click="zoomIn"
+ ></button>
+
+ <button
+ class="c-button t-btn-zoom-lock"
+ title="Lock current zoom and pan across all images"
+ :class="{'icon-unlocked': !panZoomLocked, 'icon-lock': panZoomLocked}"
+ @click="toggleZoomLock"
+ ></button>
+
+ <button
+ class="c-button icon-reset t-btn-zoom-reset"
+ title="Remove zoom and pan"
+ @click="handleResetImage"
+ ></button>
+ </div>
+ <div class="c-image-controls__zoom-factor">x{{ formattedZoomFactor }}</div>
+ </div>
+ <button
+ v-if="isMenu"
+ class="c-click-icon icon-x t-btn-close c-switcher-menu__close-button"
+ ></button>
+</div>
+</template>
+
+<script>
+export default {
+ inject: ['openmct'],
+ props: {
+ zoomFactor: {
+ type: Number,
+ required: true
+ },
+ panZoomLocked: {
+ type: Boolean,
+ required: true
+ },
+ isMenu: {
+ type: Boolean,
+ required: false
+ }
+ },
+ data() {
+ return {
+ };
+ },
+ computed: {
+ formattedZoomFactor() {
+ return Number.parseFloat(this.zoomFactor).toPrecision(2);
+ }
+ },
+ methods: {
+ handleClose(e) {
+ const closeButton = e.target.classList.contains('c-switcher-menu__close-button');
+ if (!closeButton) {
+ e.stopPropagation();
+ }
+ },
+ handleResetImage() {
+ this.$emit('handleResetImage');
+ },
+ toggleZoomLock() {
+ this.$emit('toggleZoomLock');
+ },
+ zoomIn() {
+ this.$emit('zoomIn');
+ },
+ zoomOut() {
+ this.$emit('zoomOut');
+ }
+ }
+};
+</script>
diff --git a/src/plugins/imagery/components/imagery-view.scss b/src/plugins/imagery/components/imagery-view.scss
index 8282d39ad..e29624fd1 100644
--- a/src/plugins/imagery/components/imagery-view.scss
+++ b/src/plugins/imagery/components/imagery-view.scss
@@ -1,3 +1,15 @@
+@use 'sass:math';
+
+@keyframes fade-out {
+ from {
+ background-color: rgba($colorOk, 0.5);
+ }
+ to {
+ background-color: rgba($colorOk, 0);
+ color: inherit;
+ }
+}
+
.c-imagery {
display: flex;
flex-direction: column;
@@ -16,6 +28,32 @@
display: flex;
flex-direction: column;
flex: 1 1 auto;
+
+ &.unnsynced{
+ @include sUnsynced();
+ }
+
+ &.cursor-zoom-in {
+ cursor: zoom-in;
+ }
+
+ &.cursor-zoom-out {
+ cursor: zoom-out;
+ }
+
+ &.pannable {
+ @include cursorGrab();
+ }
+ }
+
+ .image-wrapper {
+ overflow: visible clip;
+ background-image: repeating-linear-gradient(45deg, transparent, transparent 4px, rgba(125, 125, 125, 0.2) 4px, rgba(125, 125, 125, 0.2) 8px);
+ }
+
+ .image-wrapper {
+ overflow: visible clip;
+ background-image: repeating-linear-gradient(45deg, transparent, transparent 4px, rgba(125, 125, 125, 0.2) 4px, rgba(125, 125, 125, 0.2) 8px);
}
&__main-image {
@@ -27,18 +65,43 @@
justify-content: center;
flex: 1 1 auto;
height: 0;
-
- &.unnsynced{
- @include sUnsynced();
- }
+ overflow: hidden;
+ }
+ &__background-image {
+ // Actually does the image display
+ background-position: center;
+ background-repeat: no-repeat;
+ background-size: contain;
+ height: 100%; //fallback value
}
-
&__image {
+ // Present to allow Save As... image
+ position: absolute;
height: 100%;
width: 100%;
+ opacity: 0;
+ }
+
+ &__image-save-proxy {
+ height: 100%;
+ width: 100%;
+ z-index: 10;
}
}
+ &__hints {
+ $m: $interiorMargin;
+ background: rgba(black, 0.2);
+ border-radius: $smallCr;
+ padding: 2px $interiorMargin;
+ pointer-events: none;
+ position: absolute;
+ right: $m;
+ top: $m;
+ opacity: 0.9;
+ z-index: 2;
+ }
+
&__control-bar,
&__time {
display: flex;
@@ -90,12 +153,25 @@
// New imagery
$bgColor: $colorOk;
color: $colorOkFg;
- background: rgba($bgColor, 0.5);
- @include flash($animName: flashImageAge, $iter: 2, $dur: 250ms, $valStart: rgba($colorOk, 0.7), $valEnd: rgba($colorOk, 0));
+ background-color: rgba($bgColor, 0.5);
+ animation-name: fade-out;
+ animation-timing-function: ease-in;
+ animation-iteration-count: 1;
+ animation-fill-mode: forwards;
+ &.no-animation {
+ animation: none;
+ }
+ }
+
+
+ &__layer-image {
+ pointer-events: none;
+ z-index: 1;
}
&__thumbs-wrapper {
display: flex; // Uses row layout
+ justify-content: flex-end;
&.is-autoscroll-off {
background: $colorInteriorBorder;
@@ -113,17 +189,11 @@
flex: 0 1 auto;
display: flex;
flex-direction: row;
- height: 135px;
+ height: 145px;
overflow-x: auto;
overflow-y: hidden;
margin-bottom: 1px;
padding-bottom: $interiorMarginSm;
-
- .c-thumb:last-child {
- // Hilite the lastest thumb
- background: $colorBodyFg;
- color: $colorBodyBg;
- }
}
&__auto-scroll-resume-button {
@@ -132,14 +202,61 @@
font-size: 0.8em;
margin: $interiorMarginSm;
}
+
+ .c-control-menu {
+ // Controls on left of flex column layout, close btn on right
+ @include menuOuter();
+
+ border-radius: $controlCr;
+ display: flex;
+ align-items: flex-start;
+ flex-direction: row;
+ justify-content: space-between;
+ padding: $interiorMargin;
+ width: max-content;
+
+ > * + * {
+ margin-left: $interiorMargin;
+ }
+ }
+
+ .c-switcher-menu {
+ display: contents;
+
+ &__content {
+ // Menu panel
+ top: 28px;
+ position: absolute;
+
+ .c-so-view & {
+ top: 25px;
+ }
+ }
+ }
+}
+
+.--width-less-than-220 .--show-if-less-than-220.c-switcher-menu {
+ display: contents !important;
+}
+
+.s-image-layer {
+ position: absolute;
+ height: 100%;
+ width: 100%;
+ opacity: 0.5;
+ background-size: contain;
+ background-repeat: no-repeat;
+ background-position: center;
}
/*************************************** THUMBS */
.c-thumb {
+ $w: $imageThumbsD;
display: flex;
flex-direction: column;
padding: 4px;
- width: $imageThumbsD;
+ min-width: $w;
+ width: $w;
&:hover {
background: $colorThumbHoverBg;
@@ -161,93 +278,65 @@
}
}
-.l-layout,
-.c-fl {
+.is-small-thumbs {
.c-imagery__thumbs-scroll-area {
- // When Imagery is in a layout, hide the thumbs area
- display: none;
+ height: 60px; // Allow room for scrollbar
+ }
+
+ .c-thumb {
+ $w: math.div($imageThumbsD, 2);
+ min-width: $w;
+ width: $w;
+
+ &__timestamp {
+ display: none;
+ }
}
}
/*************************************** IMAGERY LOCAL CONTROLS*/
.c-imagery {
.h-local-controls--overlay-content {
+ display: flex;
+ flex-direction: row;
position: absolute;
left: $interiorMargin; top: $interiorMargin;
- z-index: 2;
+ z-index: 70;
background: $colorLocalControlOvrBg;
border-radius: $basicCr;
- max-width: 200px;
- min-width: 70px;
- width: 35%;
align-items: center;
- padding: $interiorMargin $interiorMarginLg;
-
- input[type="range"] {
- display: block;
- width: 100%;
- &:not(:first-child) {
- margin-top: $interiorMarginLg;
- }
-
- &:before {
- margin-right: $interiorMarginSm;
- }
- }
+ padding: $interiorMargin $interiorMargin;
.s-status-taking-snapshot & {
display: none;
}
}
-
- &__lc {
- &__reset-btn {
- $bc: $scrollbarTrackColorBg;
-
- &:before,
- &:after {
- border-right: 1px solid $bc;
- content:'';
- display: block;
- width: 5px;
- height: 4px;
- }
-
- &:before {
- border-top: 1px solid $bc;
- margin-bottom: 2px;
- }
-
- &:after {
- border-bottom: 1px solid $bc;
- margin-top: 2px;
- }
+ [class*='--menus-aligned'] {
+ > * + * {
+ button { margin-left: $interiorMarginSm; }
}
}
}
.c-image-controls {
- // Brightness/contrast
-
- &__controls {
- // Sliders and reset element
+ &__controls-wrapper {
+ // Wraps __controls and __close-btn
display: flex;
- align-items: center;
- margin-right: $interiorMargin; // Need some extra space due to proximity to close button
}
- &__sliders {
+ &__controls {
display: flex;
- flex: 1 1 auto;
- flex-direction: column;
+ align-items: stretch;
> * + * {
- margin-top: 11px;
+ margin-top: $interiorMargin;
}
+
+ [class*='c-button'] { flex: 0 0 auto; }
}
- &__slider-wrapper {
- // A wrapper is needed to add the type icon to left of each range input
+ &__control,
+ &__input {
display: flex;
align-items: center;
@@ -256,13 +345,71 @@
margin-right: $interiorMarginSm;
}
- input[type='range'] {
- width: 100px;
- }
}
- &__btn-reset {
- flex: 0 0 auto;
+ &__zoom {
+ > * + * { margin-left: $interiorMargin; } // Is this used?
+ }
+
+ &--filters {
+ // Styles specific to the brightness and contrast controls
+ .c-image-controls {
+ &__controls {
+ width: 80px; // About the minimum this element can be; cannot size based on % due to markup structure
+ }
+
+ &__sliders {
+ display: flex;
+ flex: 1 1 auto;
+ flex-direction: column;
+ width: 100%;
+
+ > * + * {
+ margin-top: 11px;
+ }
+
+ input[type="range"] {
+ display: block;
+ width: 100%;
+ }
+ }
+
+ &__slider-wrapper {
+ display: flex;
+ align-items: center;
+
+ &:before { margin-right: $interiorMargin; }
+ }
+
+ &__reset-btn {
+ // Span that holds bracket graphics and button
+ $bc: $scrollbarTrackColorBg;
+ flex: 0 0 auto;
+
+ &:before,
+ &:after {
+ border-right: 1px solid $bc;
+ content:'';
+ display: block;
+ width: 5px;
+ height: 4px;
+ }
+
+ &:before {
+ border-top: 1px solid $bc;
+ margin-bottom: 2px;
+ }
+
+ &:after {
+ border-bottom: 1px solid $bc;
+ margin-top: 2px;
+ }
+
+ .c-icon-link {
+ color: $colorBtnFg;
+ }
+ }
+ }
}
}
@@ -308,7 +455,7 @@
@include cArrowButtonSizing($dimOuter: 48px);
border-radius: $controlCr;
- .is-in-small-container & {
+ .--width-less-than-600 & {
@include cArrowButtonSizing($dimOuter: 32px);
}
}
@@ -334,10 +481,6 @@
background-color: $colorBodyFg;
}
- //[class*='__image-placeholder'] {
- // display: none;
- //}
-
img {
display: block !important;
}
diff --git a/src/plugins/imagery/layers/example-imagery-layer-16x9.png b/src/plugins/imagery/layers/example-imagery-layer-16x9.png
new file mode 100644
index 000000000..877a0e603
--- /dev/null
+++ b/src/plugins/imagery/layers/example-imagery-layer-16x9.png
Binary files differ
diff --git a/src/plugins/imagery/layers/example-imagery-layer-safe.png b/src/plugins/imagery/layers/example-imagery-layer-safe.png
new file mode 100644
index 000000000..f99907f29
--- /dev/null
+++ b/src/plugins/imagery/layers/example-imagery-layer-safe.png
Binary files differ
diff --git a/src/plugins/imagery/layers/example-imagery-layer-scale.png b/src/plugins/imagery/layers/example-imagery-layer-scale.png
new file mode 100644
index 000000000..7f5f41211
--- /dev/null
+++ b/src/plugins/imagery/layers/example-imagery-layer-scale.png
Binary files differ
diff --git a/src/plugins/imagery/lib/eventHelpers.js b/src/plugins/imagery/lib/eventHelpers.js
new file mode 100644
index 000000000..d4bbac4c7
--- /dev/null
+++ b/src/plugins/imagery/lib/eventHelpers.js
@@ -0,0 +1,98 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2021, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+define([], function () {
+ const helperFunctions = {
+ listenTo: function (object, event, callback, context) {
+ if (!this._listeningTo) {
+ this._listeningTo = [];
+ }
+
+ const listener = {
+ object: object,
+ event: event,
+ callback: callback,
+ context: context,
+ _cb: context ? callback.bind(context) : callback
+ };
+ if (object.$watch && event.indexOf('change:') === 0) {
+ const scopePath = event.replace('change:', '');
+ listener.unlisten = object.$watch(scopePath, listener._cb, true);
+ } else if (object.$on) {
+ listener.unlisten = object.$on(event, listener._cb);
+ } else if (object.addEventListener) {
+ object.addEventListener(event, listener._cb);
+ } else {
+ object.on(event, listener._cb);
+ }
+
+ this._listeningTo.push(listener);
+ },
+
+ stopListening: function (object, event, callback, context) {
+ if (!this._listeningTo) {
+ this._listeningTo = [];
+ }
+
+ this._listeningTo.filter(function (listener) {
+ if (object && object !== listener.object) {
+ return false;
+ }
+
+ if (event && event !== listener.event) {
+ return false;
+ }
+
+ if (callback && callback !== listener.callback) {
+ return false;
+ }
+
+ if (context && context !== listener.context) {
+ return false;
+ }
+
+ return true;
+ })
+ .map(function (listener) {
+ if (listener.unlisten) {
+ listener.unlisten();
+ } else if (listener.object.removeEventListener) {
+ listener.object.removeEventListener(listener.event, listener._cb);
+ } else {
+ listener.object.off(listener.event, listener._cb);
+ }
+
+ return listener;
+ })
+ .forEach(function (listener) {
+ this._listeningTo.splice(this._listeningTo.indexOf(listener), 1);
+ }, this);
+ },
+
+ extend: function (object) {
+ object.listenTo = helperFunctions.listenTo;
+ object.stopListening = helperFunctions.stopListening;
+ }
+ };
+
+ return helperFunctions;
+});
diff --git a/src/plugins/imagery/mixins/imageryData.js b/src/plugins/imagery/mixins/imageryData.js
index 08d87b202..b0040a69f 100644
--- a/src/plugins/imagery/mixins/imageryData.js
+++ b/src/plugins/imagery/mixins/imageryData.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -30,6 +30,7 @@ export default {
this.timeSystemChange = this.timeSystemChange.bind(this);
this.setDataTimeContext = this.setDataTimeContext.bind(this);
this.setDataTimeContext();
+ this.openmct.objectViews.on('clearData', this.dataCleared);
// set
this.keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
@@ -43,9 +44,11 @@ export default {
this.timeKey = this.timeSystem.key;
this.timeFormatter = this.getFormatter(this.timeKey);
- // kickoff
- this.subscribe();
- this.requestHistory();
+ this.telemetryCollection = this.openmct.telemetry.requestCollection(this.domainObject, {});
+ this.telemetryCollection.on('add', this.dataAdded);
+ this.telemetryCollection.on('remove', this.dataRemoved);
+ this.telemetryCollection.on('clear', this.dataCleared);
+ this.telemetryCollection.load();
},
beforeDestroy() {
if (this.unsubscribe) {
@@ -54,8 +57,34 @@ export default {
}
this.stopFollowingDataTimeContext();
+ this.openmct.objectViews.off('clearData', this.dataCleared);
+
+ this.telemetryCollection.off('add', this.dataAdded);
+ this.telemetryCollection.off('remove', this.dataRemoved);
+ this.telemetryCollection.off('clear', this.dataCleared);
+
+ this.telemetryCollection.destroy();
},
methods: {
+ dataAdded(dataToAdd) {
+ const normalizedDataToAdd = dataToAdd.map(datum => this.normalizeDatum(datum));
+ this.imageHistory = this.imageHistory.concat(normalizedDataToAdd);
+ },
+ dataCleared() {
+ this.imageHistory = [];
+ },
+ dataRemoved(dataToRemove) {
+ this.imageHistory = this.imageHistory.filter(existingDatum => {
+ const shouldKeep = dataToRemove.some(datumToRemove => {
+ const existingDatumTimestamp = this.parseTime(existingDatum);
+ const datumToRemoveTimestamp = this.parseTime(datumToRemove);
+
+ return (existingDatumTimestamp !== datumToRemoveTimestamp);
+ });
+
+ return shouldKeep;
+ });
+ },
setDataTimeContext() {
this.stopFollowingDataTimeContext();
this.timeContext = this.openmct.time.getContextForView(this.objectPath);
@@ -69,23 +98,6 @@ export default {
this.timeContext.off('timeSystem', this.timeSystemChange);
}
},
- datumIsNotValid(datum) {
- if (this.imageHistory.length === 0) {
- return false;
- }
-
- const datumURL = this.formatImageUrl(datum);
- const lastHistoryURL = this.formatImageUrl(this.imageHistory.slice(-1)[0]);
-
- // datum is not valid if it matches the last datum in history,
- // or it is before the last datum in the history
- const datumTimeCheck = this.parseTime(datum);
- const historyTimeCheck = this.parseTime(this.imageHistory.slice(-1)[0]);
- const matchesLast = (datumTimeCheck === historyTimeCheck) && (datumURL === lastHistoryURL);
- const isStale = datumTimeCheck < historyTimeCheck;
-
- return matchesLast || isStale;
- },
formatImageUrl(datum) {
if (!datum) {
return;
@@ -127,29 +139,6 @@ export default {
// forcibly reset the imageContainer size to prevent an aspect ratio distortion
delete this.imageContainerWidth;
delete this.imageContainerHeight;
-
- return this.requestHistory();
- },
- async requestHistory() {
- let bounds = this.timeContext.bounds();
- this.requestCount++;
- const requestId = this.requestCount;
- this.imageHistory = [];
-
- let data = await this.openmct.telemetry
- .request(this.domainObject, bounds) || [];
-
- if (this.requestCount === requestId) {
- let imagery = [];
- data.forEach((datum) => {
- let image = this.normalizeDatum(datum);
- if (image) {
- imagery.push(image);
- }
- });
- //this is to optimize anything that reacts to imageHistory length
- this.imageHistory = imagery;
- }
},
timeSystemChange() {
this.timeSystem = this.timeContext.timeSystem();
@@ -157,32 +146,19 @@ export default {
this.timeFormatter = this.getFormatter(this.timeKey);
this.durationFormatter = this.getFormatter(this.timeSystem.durationFormat || DEFAULT_DURATION_FORMATTER);
},
- subscribe() {
- this.unsubscribe = this.openmct.telemetry
- .subscribe(this.domainObject, (datum) => {
- let parsedTimestamp = this.parseTime(datum);
- let bounds = this.timeContext.bounds();
-
- if (parsedTimestamp >= bounds.start && parsedTimestamp <= bounds.end) {
- let image = this.normalizeDatum(datum);
- if (image) {
- this.imageHistory.push(image);
- }
- }
- });
- },
normalizeDatum(datum) {
- if (this.datumIsNotValid(datum)) {
- return;
- }
-
- let image = { ...datum };
- image.formattedTime = this.formatTime(datum);
- image.url = this.formatImageUrl(datum);
- image.time = this.parseTime(image.formattedTime);
- image.imageDownloadName = this.getImageDownloadName(datum);
-
- return image;
+ const formattedTime = this.formatTime(datum);
+ const url = this.formatImageUrl(datum);
+ const time = this.parseTime(formattedTime);
+ const imageDownloadName = this.getImageDownloadName(datum);
+
+ return {
+ ...datum,
+ formattedTime,
+ url,
+ time,
+ imageDownloadName
+ };
},
getFormatter(key) {
let metadataValue = this.metadata.value(key) || { format: key };
diff --git a/src/plugins/imagery/plugin.js b/src/plugins/imagery/plugin.js
index b269ef6ba..f18be0d41 100644
--- a/src/plugins/imagery/plugin.js
+++ b/src/plugins/imagery/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -23,9 +23,9 @@
import ImageryViewProvider from './ImageryViewProvider';
import ImageryTimestripViewProvider from './ImageryTimestripViewProvider';
-export default function () {
+export default function (options) {
return function install(openmct) {
- openmct.objectViews.addProvider(new ImageryViewProvider(openmct));
+ openmct.objectViews.addProvider(new ImageryViewProvider(openmct, options));
openmct.objectViews.addProvider(new ImageryTimestripViewProvider(openmct));
};
}
diff --git a/src/plugins/imagery/pluginSpec.js b/src/plugins/imagery/pluginSpec.js
index 71f0a57f5..e595117a8 100644
--- a/src/plugins/imagery/pluginSpec.js
+++ b/src/plugins/imagery/pluginSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -27,6 +27,7 @@ import {
resetApplicationState,
simulateKeyEvent
} from 'utils/testing';
+import ClearDataPlugin from '../clearData/plugin';
const ONE_MINUTE = 1000 * 60;
const TEN_MINUTES = ONE_MINUTE * 10;
@@ -87,6 +88,7 @@ describe("The Imagery View Layouts", () => {
let openmct;
let parent;
let child;
+ let historicalProvider;
let imageTelemetry = generateTelemetry(START - TEN_MINUTES, COUNT);
let imageryObject = {
identifier: {
@@ -98,61 +100,29 @@ describe("The Imagery View Layouts", () => {
location: "parentId",
modified: 0,
persisted: 0,
+ configuration: {
+ layers: [{
+ name: '16:9',
+ visible: true
+ }]
+ },
telemetry: {
values: [
{
"name": "Image",
"key": "url",
"format": "image",
+ "layers": [
+ {
+ source: location.host + '/images/bg-splash.jpg',
+ name: '16:9'
+ }
+ ],
"hints": {
"image": 1,
"priority": 3
},
"source": "url"
- // "relatedTelemetry": {
- // "heading": {
- // "comparisonFunction": comparisonFunction,
- // "historical": {
- // "telemetryObjectId": "heading",
- // "valueKey": "value"
- // }
- // },
- // "roll": {
- // "comparisonFunction": comparisonFunction,
- // "historical": {
- // "telemetryObjectId": "roll",
- // "valueKey": "value"
- // }
- // },
- // "pitch": {
- // "comparisonFunction": comparisonFunction,
- // "historical": {
- // "telemetryObjectId": "pitch",
- // "valueKey": "value"
- // }
- // },
- // "cameraPan": {
- // "comparisonFunction": comparisonFunction,
- // "historical": {
- // "telemetryObjectId": "cameraPan",
- // "valueKey": "value"
- // }
- // },
- // "cameraTilt": {
- // "comparisonFunction": comparisonFunction,
- // "historical": {
- // "telemetryObjectId": "cameraTilt",
- // "valueKey": "value"
- // }
- // },
- // "sunOrientation": {
- // "comparisonFunction": comparisonFunction,
- // "historical": {
- // "telemetryObjectId": "sunOrientation",
- // "valueKey": "value"
- // }
- // }
- // }
},
{
"name": "Name",
@@ -191,15 +161,18 @@ describe("The Imagery View Layouts", () => {
cleanupFirst = [];
openmct = createOpenMct();
- openmct.time.timeSystem('utc', {
- start: START - (5 * ONE_MINUTE),
- end: START + (5 * ONE_MINUTE)
- });
telemetryPromise = new Promise((resolve) => {
telemetryPromiseResolve = resolve;
});
+ historicalProvider = {
+ request: () => {
+ return Promise.resolve(imageTelemetry);
+ }
+ };
+ spyOn(openmct.telemetry, 'findRequestProvider').and.returnValue(historicalProvider);
+
spyOn(openmct.telemetry, 'request').and.callFake(() => {
telemetryPromiseResolve(imageTelemetry);
@@ -319,51 +292,112 @@ describe("The Imagery View Layouts", () => {
expect(imageryView).toBeDefined();
});
- describe("imagery view", () => {
+ describe("Clear data action for imagery", () => {
let applicableViews;
let imageryViewProvider;
let imageryView;
+ let componentView;
+ let clearDataPlugin;
+ let clearDataAction;
beforeEach(() => {
+ openmct.time.timeSystem('utc', {
+ start: START - (5 * ONE_MINUTE),
+ end: START + (5 * ONE_MINUTE)
+ });
applicableViews = openmct.objectViews.get(imageryObject, [imageryObject]);
imageryViewProvider = applicableViews.find(viewProvider => viewProvider.key === imageryKey);
imageryView = imageryViewProvider.view(imageryObject, [imageryObject]);
imageryView.show(child);
+ componentView = imageryView._getInstance().$children[0];
+
+ clearDataPlugin = new ClearDataPlugin(
+ ['example.imagery'],
+ {indicator: true}
+ );
+ openmct.install(clearDataPlugin);
+ clearDataAction = openmct.actions.getAction('clear-data-action');
return Vue.nextTick();
});
- // afterEach(() => {
- // openmct.time.stopClock();
- // openmct.router.removeListener('change:hash', resolveFunction);
- //
- // imageryView.destroy();
- // });
+ it('clear data action is installed', () => {
+ expect(clearDataAction).toBeDefined();
+ });
- it("on mount should show the the most recent image", (done) => {
- //Looks like we need Vue.nextTick here so that computed properties settle down
+ it('on clearData action should clear data for object is selected', (done) => {
+ // force show the thumbnails
+ componentView.forceShowThumbnails = true;
Vue.nextTick(() => {
- const imageInfo = getImageInfo(parent);
+ let clearDataResolve;
+ let telemetryRequestPromise = new Promise((resolve) => {
+ clearDataResolve = resolve;
+ });
+ expect(parent.querySelectorAll('.c-imagery__thumb').length).not.toBe(0);
- expect(imageInfo.url.indexOf(imageTelemetry[COUNT - 1].timeId)).not.toEqual(-1);
- done();
- });
- });
+ openmct.objectViews.on('clearData', (_domainObject) => {
+ return Vue.nextTick(() => {
+ expect(parent.querySelectorAll('.c-imagery__thumb').length).toBe(0);
- it("should show the clicked thumbnail as the main image", (done) => {
- //Looks like we need Vue.nextTick here so that computed properties settle down
- Vue.nextTick(() => {
- const target = imageTelemetry[5].url;
- parent.querySelectorAll(`img[src='${target}']`)[0].click();
- Vue.nextTick(() => {
- const imageInfo = getImageInfo(parent);
+ clearDataResolve();
+ });
+ });
+ clearDataAction.invoke(imageryObject);
- expect(imageInfo.url.indexOf(imageTelemetry[5].timeId)).not.toEqual(-1);
+ telemetryRequestPromise.then(() => {
done();
});
});
});
+ });
+
+ describe("imagery view", () => {
+ let applicableViews;
+ let imageryViewProvider;
+ let imageryView;
+
+ beforeEach(() => {
+ openmct.time.timeSystem('utc', {
+ start: START - (5 * ONE_MINUTE),
+ end: START + (5 * ONE_MINUTE)
+ });
+
+ applicableViews = openmct.objectViews.get(imageryObject, [imageryObject]);
+ imageryViewProvider = applicableViews.find(viewProvider => viewProvider.key === imageryKey);
+ imageryView = imageryViewProvider.view(imageryObject, [imageryObject]);
+ imageryView.show(child);
+
+ imageryView._getInstance().$children[0].forceShowThumbnails = true;
+
+ return Vue.nextTick();
+ });
+
+ it("on mount should show the the most recent image", async () => {
+ //Looks like we need Vue.nextTick here so that computed properties settle down
+ await Vue.nextTick();
+ const imageInfo = getImageInfo(parent);
+ expect(imageInfo.url.indexOf(imageTelemetry[COUNT - 1].timeId)).not.toEqual(-1);
+ });
+
+ it("on mount should show the any image layers", async () => {
+ //Looks like we need Vue.nextTick here so that computed properties settle down
+ await Vue.nextTick();
+ const layerEls = parent.querySelectorAll('.js-layer-image');
+ console.log(layerEls);
+ expect(layerEls.length).toEqual(1);
+ });
+
+ it("should show the clicked thumbnail as the main image", async () => {
+ //Looks like we need Vue.nextTick here so that computed properties settle down
+ await Vue.nextTick();
+ const target = imageTelemetry[5].url;
+ parent.querySelectorAll(`img[src='${target}']`)[0].click();
+ await Vue.nextTick();
+ const imageInfo = getImageInfo(parent);
+
+ expect(imageInfo.url.indexOf(imageTelemetry[5].timeId)).not.toEqual(-1);
+ });
xit("should show that an image is new", (done) => {
openmct.time.clock('local', {
@@ -381,71 +415,60 @@ describe("The Imagery View Layouts", () => {
});
});
- it("should show that an image is not new", (done) => {
- Vue.nextTick(() => {
- const target = imageTelemetry[2].url;
- parent.querySelectorAll(`img[src='${target}']`)[0].click();
+ it("should show that an image is not new", async () => {
+ await Vue.nextTick();
+ const target = imageTelemetry[4].url;
+ parent.querySelectorAll(`img[src='${target}']`)[0].click();
- Vue.nextTick(() => {
- const imageIsNew = isNew(parent);
+ await Vue.nextTick();
+ const imageIsNew = isNew(parent);
- expect(imageIsNew).toBeFalse();
- done();
- });
- });
+ expect(imageIsNew).toBeFalse();
});
- it("should navigate via arrow keys", (done) => {
- Vue.nextTick(() => {
- let keyOpts = {
- element: parent.querySelector('.c-imagery'),
- key: 'ArrowLeft',
- keyCode: 37,
- type: 'keyup'
- };
-
- simulateKeyEvent(keyOpts);
+ it("should navigate via arrow keys", async () => {
+ await Vue.nextTick();
+ const keyOpts = {
+ element: parent.querySelector('.c-imagery'),
+ key: 'ArrowLeft',
+ keyCode: 37,
+ type: 'keyup'
+ };
- Vue.nextTick(() => {
- const imageInfo = getImageInfo(parent);
+ simulateKeyEvent(keyOpts);
- expect(imageInfo.url.indexOf(imageTelemetry[COUNT - 2].timeId)).not.toEqual(-1);
- done();
- });
- });
+ await Vue.nextTick();
+ const imageInfo = getImageInfo(parent);
+ expect(imageInfo.url.indexOf(imageTelemetry[COUNT - 2].timeId)).not.toEqual(-1);
});
- it("should navigate via numerous arrow keys", (done) => {
- Vue.nextTick(() => {
- let element = parent.querySelector('.c-imagery');
- let type = 'keyup';
- let leftKeyOpts = {
- element,
- type,
- key: 'ArrowLeft',
- keyCode: 37
- };
- let rightKeyOpts = {
- element,
- type,
- key: 'ArrowRight',
- keyCode: 39
- };
-
- // left thrice
- simulateKeyEvent(leftKeyOpts);
- simulateKeyEvent(leftKeyOpts);
- simulateKeyEvent(leftKeyOpts);
- // right once
- simulateKeyEvent(rightKeyOpts);
-
- Vue.nextTick(() => {
- const imageInfo = getImageInfo(parent);
-
- expect(imageInfo.url.indexOf(imageTelemetry[COUNT - 3].timeId)).not.toEqual(-1);
- done();
- });
- });
+ it("should navigate via numerous arrow keys", async () => {
+ await Vue.nextTick();
+ const element = parent.querySelector('.c-imagery');
+ const type = 'keyup';
+ const leftKeyOpts = {
+ element,
+ type,
+ key: 'ArrowLeft',
+ keyCode: 37
+ };
+ const rightKeyOpts = {
+ element,
+ type,
+ key: 'ArrowRight',
+ keyCode: 39
+ };
+
+ // left thrice
+ simulateKeyEvent(leftKeyOpts);
+ simulateKeyEvent(leftKeyOpts);
+ simulateKeyEvent(leftKeyOpts);
+ // right once
+ simulateKeyEvent(rightKeyOpts);
+
+ await Vue.nextTick();
+ const imageInfo = getImageInfo(parent);
+ expect(imageInfo.url.indexOf(imageTelemetry[COUNT - 3].timeId)).not.toEqual(-1);
});
it ('shows an auto scroll button when scroll to left', (done) => {
Vue.nextTick(() => {
@@ -470,6 +493,69 @@ describe("The Imagery View Layouts", () => {
});
});
});
+ xit('should change the image zoom factor when using the zoom buttons', async (done) => {
+ await Vue.nextTick();
+ let imageSizeBefore;
+ let imageSizeAfter;
+
+ // test clicking the zoom in button
+ imageSizeBefore = parent.querySelector('.c-imagery_main-image_background-image').getBoundingClientRect();
+ parent.querySelector('.t-btn-zoom-in').click();
+ await Vue.nextTick();
+ imageSizeAfter = parent.querySelector('.c-imagery_main-image_background-image').getBoundingClientRect();
+ expect(imageSizeAfter.height).toBeGreaterThan(imageSizeBefore.height);
+ expect(imageSizeAfter.width).toBeGreaterThan(imageSizeBefore.width);
+ // test clicking the zoom out button
+ imageSizeBefore = parent.querySelector('.c-imagery_main-image_background-image').getBoundingClientRect();
+ parent.querySelector('.t-btn-zoom-out').click();
+ await Vue.nextTick();
+ imageSizeAfter = parent.querySelector('.c-imagery_main-image_background-image').getBoundingClientRect();
+ expect(imageSizeAfter.height).toBeLessThan(imageSizeBefore.height);
+ expect(imageSizeAfter.width).toBeLessThan(imageSizeBefore.width);
+ done();
+ });
+ xit('should reset the zoom factor on the image when clicking the zoom button', async (done) => {
+ await Vue.nextTick();
+ // test clicking the zoom reset button
+ // zoom in to scale up the image dimensions
+ parent.querySelector('.t-btn-zoom-in').click();
+ await Vue.nextTick();
+ let imageSizeBefore = parent.querySelector('.c-imagery_main-image_background-image').getBoundingClientRect();
+ await Vue.nextTick();
+ parent.querySelector('.t-btn-zoom-reset').click();
+ let imageSizeAfter = parent.querySelector('.c-imagery_main-image_background-image').getBoundingClientRect();
+ expect(imageSizeAfter.height).toBeLessThan(imageSizeBefore.height);
+ expect(imageSizeAfter.width).toBeLessThan(imageSizeBefore.width);
+ done();
+ });
+
+ it('should reset the brightness and contrast when clicking the reset button', async () => {
+ const viewInstance = imageryView._getInstance();
+ await Vue.nextTick();
+
+ // Save the original brightness and contrast values
+ const origBrightness = viewInstance.$refs.ImageryContainer.filters.brightness;
+ const origContrast = viewInstance.$refs.ImageryContainer.filters.contrast;
+
+ // Change them to something else (default: 100)
+ viewInstance.$refs.ImageryContainer.setFilters({
+ brightness: 200,
+ contrast: 200
+ });
+ await Vue.nextTick();
+
+ // Verify that the values actually changed
+ expect(viewInstance.$refs.ImageryContainer.filters.brightness).toBe(200);
+ expect(viewInstance.$refs.ImageryContainer.filters.contrast).toBe(200);
+
+ // Click the reset button
+ parent.querySelector('.t-btn-reset').click();
+ await Vue.nextTick();
+
+ // Verify that the values were reset
+ expect(viewInstance.$refs.ImageryContainer.filters.brightness).toBe(origBrightness);
+ expect(viewInstance.$refs.ImageryContainer.filters.contrast).toBe(origContrast);
+ });
});
describe("imagery time strip view", () => {
@@ -484,6 +570,20 @@ describe("The Imagery View Layouts", () => {
end: START + (5 * ONE_MINUTE)
});
+ const mockClock = jasmine.createSpyObj("clock", [
+ "on",
+ "off",
+ "currentValue"
+ ]);
+ mockClock.key = 'mockClock';
+ mockClock.currentValue.and.returnValue(1);
+
+ openmct.time.addClock(mockClock);
+ openmct.time.clock('mockClock', {
+ start: START - (5 * ONE_MINUTE),
+ end: START + (5 * ONE_MINUTE)
+ });
+
openmct.router.path = [{
identifier: {
key: 'test-timestrip',
@@ -518,7 +618,7 @@ describe("The Imagery View Layouts", () => {
it("on mount should show imagery within the given bounds", (done) => {
Vue.nextTick(() => {
const imageElements = parent.querySelectorAll('.c-imagery-tsv__image-wrapper');
- expect(imageElements.length).toEqual(6);
+ expect(imageElements.length).toEqual(5);
done();
});
});
@@ -538,5 +638,46 @@ describe("The Imagery View Layouts", () => {
});
});
});
+
+ it("should remove images when clock advances", async () => {
+ openmct.time.tick(ONE_MINUTE * 2);
+ await Vue.nextTick();
+ await Vue.nextTick();
+ const imageElements = parent.querySelectorAll('.c-imagery-tsv__image-wrapper');
+ expect(imageElements.length).toEqual(4);
+ });
+
+ it("should remove images when start bounds shorten", async () => {
+ openmct.time.timeSystem('utc', {
+ start: START,
+ end: START + (5 * ONE_MINUTE)
+ });
+ await Vue.nextTick();
+ await Vue.nextTick();
+ const imageElements = parent.querySelectorAll('.c-imagery-tsv__image-wrapper');
+ expect(imageElements.length).toEqual(1);
+ });
+
+ it("should remove images when end bounds shorten", async () => {
+ openmct.time.timeSystem('utc', {
+ start: START - (5 * ONE_MINUTE),
+ end: START - (2 * ONE_MINUTE)
+ });
+ await Vue.nextTick();
+ await Vue.nextTick();
+ const imageElements = parent.querySelectorAll('.c-imagery-tsv__image-wrapper');
+ expect(imageElements.length).toEqual(4);
+ });
+
+ it("should remove images when both bounds shorten", async () => {
+ openmct.time.timeSystem('utc', {
+ start: START - (2 * ONE_MINUTE),
+ end: START + (2 * ONE_MINUTE)
+ });
+ await Vue.nextTick();
+ await Vue.nextTick();
+ const imageElements = parent.querySelectorAll('.c-imagery-tsv__image-wrapper');
+ expect(imageElements.length).toEqual(3);
+ });
});
});
diff --git a/src/plugins/importFromJSONAction/ImportFromJSONAction.js b/src/plugins/importFromJSONAction/ImportFromJSONAction.js
index 7c10560be..5b28c0f60 100644
--- a/src/plugins/importFromJSONAction/ImportFromJSONAction.js
+++ b/src/plugins/importFromJSONAction/ImportFromJSONAction.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -21,7 +21,7 @@
*****************************************************************************/
import objectUtils from 'objectUtils';
-import uuid from "uuid";
+import { v4 as uuid } from 'uuid';
export default class ImportAsJSONAction {
constructor(openmct) {
@@ -82,12 +82,14 @@ export default class ImportAsJSONAction {
* @param {object} seen
*/
_deepInstantiate(parent, tree, seen) {
- if (this.openmct.composition.get(parent)) {
+ let objectIdentifiers = this._getObjectReferenceIds(parent);
+
+ if (objectIdentifiers.length) {
let newObj;
seen.push(parent.id);
- parent.composition.forEach(async (childId) => {
+ objectIdentifiers.forEach(async (childId) => {
const keystring = this.openmct.objects.makeKeyString(childId);
if (!tree[keystring] || seen.includes(keystring)) {
return;
@@ -103,6 +105,27 @@ export default class ImportAsJSONAction {
}
/**
* @private
+ * @param {object} parent
+ * @returns [identifiers]
+ */
+ _getObjectReferenceIds(parent) {
+ let objectIdentifiers = [];
+
+ let parentComposition = this.openmct.composition.get(parent);
+ if (parentComposition) {
+ objectIdentifiers = Array.from(parentComposition.domainObject.composition);
+ }
+
+ //conditional object styles are not saved on the composition, so we need to check for them
+ let parentObjectReference = parent.configuration?.objectStyles?.conditionSetIdentifier;
+ if (parentObjectReference) {
+ objectIdentifiers.push(parentObjectReference);
+ }
+
+ return objectIdentifiers;
+ }
+ /**
+ * @private
* @param {object} tree
* @param {string} namespace
* @returns {object}
diff --git a/src/plugins/importFromJSONAction/ImportFromJSONActionSpec.js b/src/plugins/importFromJSONAction/ImportFromJSONActionSpec.js
index 046cdee28..dd0879c93 100644
--- a/src/plugins/importFromJSONAction/ImportFromJSONActionSpec.js
+++ b/src/plugins/importFromJSONAction/ImportFromJSONActionSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/importFromJSONAction/plugin.js b/src/plugins/importFromJSONAction/plugin.js
index eccee8643..d4d029ebb 100644
--- a/src/plugins/importFromJSONAction/plugin.js
+++ b/src/plugins/importFromJSONAction/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/interceptors/missingObjectInterceptor.js b/src/plugins/interceptors/missingObjectInterceptor.js
index 3e39d7468..4a2167080 100644
--- a/src/plugins/interceptors/missingObjectInterceptor.js
+++ b/src/plugins/interceptors/missingObjectInterceptor.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/interceptors/pluginSpec.js b/src/plugins/interceptors/pluginSpec.js
index ff4ad5c02..c4696f81e 100644
--- a/src/plugins/interceptors/pluginSpec.js
+++ b/src/plugins/interceptors/pluginSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/latestDataClock/LADClock.js b/src/plugins/latestDataClock/LADClock.js
index e43f5f5f0..080936219 100644
--- a/src/plugins/latestDataClock/LADClock.js
+++ b/src/plugins/latestDataClock/LADClock.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT Web, Copyright (c) 2014-2015, United States Government
+ * Open MCT Web, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/latestDataClock/plugin.js b/src/plugins/latestDataClock/plugin.js
index a1f8f71a7..0e04860d8 100644
--- a/src/plugins/latestDataClock/plugin.js
+++ b/src/plugins/latestDataClock/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT Web, Copyright (c) 2014-2015, United States Government
+ * Open MCT Web, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/legacySupport/BundleRegistry.js b/src/plugins/legacySupport/BundleRegistry.js
deleted file mode 100644
index 881b489ba..000000000
--- a/src/plugins/legacySupport/BundleRegistry.js
+++ /dev/null
@@ -1,78 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(function () {
-
- function BundleRegistry() {
- this.bundles = {};
- this.knownBundles = {};
- }
-
- BundleRegistry.prototype.register = function (path, definition) {
- if (Object.prototype.hasOwnProperty.call(this.knownBundles, path)) {
- throw new Error('Cannot register bundle with duplicate path', path);
- }
-
- this.knownBundles[path] = definition;
- };
-
- BundleRegistry.prototype.enable = function (path) {
- if (!this.knownBundles[path]) {
- throw new Error('Unknown bundle ' + path);
- }
-
- this.bundles[path] = this.knownBundles[path];
- };
-
- BundleRegistry.prototype.disable = function (path) {
- if (!this.bundles[path]) {
- throw new Error('Tried to disable inactive bundle ' + path);
- }
-
- delete this.bundles[path];
- };
-
- BundleRegistry.prototype.contains = function (path) {
- return Boolean(this.bundles[path]);
- };
-
- BundleRegistry.prototype.get = function (path) {
- return this.bundles[path];
- };
-
- BundleRegistry.prototype.list = function () {
- return Object.keys(this.bundles);
- };
-
- BundleRegistry.prototype.remove = BundleRegistry.prototype.disable;
-
- BundleRegistry.prototype.delete = function (path) {
- if (!this.knownBundles[path]) {
- throw new Error('Cannot remove Unknown Bundle ' + path);
- }
-
- delete this.bundles[path];
- delete this.knownBundles[path];
- };
-
- return BundleRegistry;
-});
diff --git a/src/plugins/legacySupport/BundleRegistrySpec.js b/src/plugins/legacySupport/BundleRegistrySpec.js
deleted file mode 100644
index a1dd63950..000000000
--- a/src/plugins/legacySupport/BundleRegistrySpec.js
+++ /dev/null
@@ -1,88 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-
-define(['./BundleRegistry'], function (BundleRegistry) {
-
- describe("BundleRegistry", function () {
- let testPath;
- let bundleRegistry;
-
- beforeEach(function () {
- testPath = 'some/bundle';
- bundleRegistry = new BundleRegistry();
- });
-
- it("initially lists no bundles", function () {
- expect(bundleRegistry.list()).toEqual([]);
- });
-
- it("initially contains no bundles", function () {
- expect(bundleRegistry.contains(testPath))
- .toBe(false);
- });
-
- it("initially provides no bundles", function () {
- expect(bundleRegistry.get(testPath))
- .toBeUndefined();
- });
-
- describe("when a bundle has been registered", function () {
- let testBundleDef;
-
- beforeEach(function () {
- testBundleDef = { someKey: "some value" };
- bundleRegistry.register(testPath, testBundleDef);
- bundleRegistry.enable(testPath);
- });
-
- it("lists registered bundles", function () {
- expect(bundleRegistry.list()).toEqual([testPath]);
- });
-
- it("contains registered bundles", function () {
- expect(bundleRegistry.contains(testPath))
- .toBe(true);
- });
-
- it("provides registered bundles", function () {
- expect(bundleRegistry.get(testPath))
- .toBe(testBundleDef);
- });
-
- describe("and then removed", function () {
- beforeEach(function () {
- bundleRegistry.remove(testPath);
- });
-
- it("appears empty again", function () {
- expect(bundleRegistry.list()).toEqual([]);
- });
-
- it("does not contain the removed bundle", function () {
- expect(bundleRegistry.contains(testPath))
- .toBe(false);
- });
- });
- });
- });
-
-});
diff --git a/src/plugins/legacySupport/installDefaultBundles.js b/src/plugins/legacySupport/installDefaultBundles.js
deleted file mode 100644
index 0083c9bda..000000000
--- a/src/plugins/legacySupport/installDefaultBundles.js
+++ /dev/null
@@ -1,101 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-const DEFAULTS = [
- 'src/adapter',
- 'platform/framework',
- 'platform/core',
- 'platform/representation',
- 'platform/commonUI/browse',
- 'platform/commonUI/edit',
- 'platform/commonUI/dialog',
- 'platform/commonUI/general',
- 'platform/commonUI/inspect',
- 'platform/commonUI/mobile',
- 'platform/commonUI/notification',
- 'platform/containment',
- 'platform/exporters',
- 'platform/telemetry',
- 'platform/identity',
- 'platform/persistence/aggregator',
- 'platform/policy',
- 'platform/entanglement',
- 'platform/status',
- 'platform/commonUI/regions'
-];
-
-define([
- '../../adapter/bundle',
- '../../../example/eventGenerator/bundle',
- '../../../example/export/bundle',
- '../../../example/forms/bundle',
- '../../../example/identity/bundle',
- '../../../example/mobile/bundle',
- '../../../example/msl/bundle',
- '../../../example/notifications/bundle',
- '../../../example/persistence/bundle',
- '../../../example/policy/bundle',
- '../../../example/profiling/bundle',
- '../../../example/scratchpad/bundle',
- '../../../example/styleguide/bundle',
- '../../../platform/commonUI/browse/bundle',
- '../../../platform/commonUI/dialog/bundle',
- '../../../platform/commonUI/edit/bundle',
- '../../../platform/commonUI/general/bundle',
- '../../../platform/commonUI/inspect/bundle',
- '../../../platform/commonUI/mobile/bundle',
- '../../../platform/commonUI/notification/bundle',
- '../../../platform/commonUI/regions/bundle',
- '../../../platform/containment/bundle',
- '../../../platform/core/bundle',
- '../../../platform/entanglement/bundle',
- '../../../platform/exporters/bundle',
- '../../../platform/features/static-markup/bundle',
- '../../../platform/framework/bundle',
- '../../../platform/framework/src/load/Bundle',
- '../../../platform/identity/bundle',
- '../../../platform/persistence/aggregator/bundle',
- '../../../platform/persistence/elastic/bundle',
- '../../../platform/persistence/queue/bundle',
- '../../../platform/policy/bundle',
- '../../../platform/representation/bundle',
- '../../../platform/status/bundle',
- '../../../platform/telemetry/bundle'
-], function () {
- const LEGACY_BUNDLES = Array.from(arguments);
-
- return function installDefaultBundles(bundleRegistry) {
- registerLegacyBundles(LEGACY_BUNDLES);
- enableDefaultBundles();
-
- function registerLegacyBundles(bundles) {
- bundles.forEach((bundle, i) => {
- bundleRegistry.register(bundle.name, bundle.definition);
- });
- }
-
- function enableDefaultBundles() {
- DEFAULTS.forEach(function (bundlePath) {
- bundleRegistry.enable(bundlePath);
- });
- }
- };
-});
diff --git a/src/plugins/legacySupport/plugin.js b/src/plugins/legacySupport/plugin.js
deleted file mode 100644
index a1d853f68..000000000
--- a/src/plugins/legacySupport/plugin.js
+++ /dev/null
@@ -1,126 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *****************************************************************************/
-import installDefaultBundles from './installDefaultBundles';
-import BundleRegistry from './BundleRegistry';
-import Main from '../../../platform/framework/src/Main';
-import objectUtils from '../../api/objects/object-utils';
-import DomainObjectImpl from '../../../platform/core/src/objects/DomainObjectImpl';
-import ContextualDomainObject from '../../../platform/core/src/capabilities/ContextualDomainObject';
-
-export default function LegacySupportPlugin() {
- return function install(openmct) {
- openmct.legacyBundle = {
- extensions: {
- services: [
- {
- key: "openmct",
- implementation: function ($injector) {
- openmct.$injector = $injector;
-
- return openmct;
- },
- depends: ['$injector']
- }
- ]
- }
- };
-
- openmct.legacyExtension = function (category, extension) {
- this.legacyBundle.extensions[category] =
- this.legacyBundle.extensions[category] || [];
- this.legacyBundle.extensions[category].push(extension);
- }.bind(openmct);
-
- /**
- * Return a legacy object, for compatibility purposes only. This method
- * will be deprecated and removed in the future.
- * @private
- */
- openmct.legacyObject = function (domainObject) {
- let capabilityService = this.$injector.get('capabilityService');
-
- function instantiate(model, keyString) {
- const capabilities = capabilityService.getCapabilities(model, keyString);
- model.id = keyString;
-
- return new DomainObjectImpl(keyString, model, capabilities);
- }
-
- if (Array.isArray(domainObject)) {
- // an array of domain objects. [object, ...ancestors] representing
- // a single object with a given chain of ancestors. We instantiate
- // as a single contextual domain object.
- return domainObject
- .map((o) => {
- let keyString = objectUtils.makeKeyString(o.identifier);
- let oldModel = objectUtils.toOldFormat(o);
-
- return instantiate(oldModel, keyString);
- })
- .reverse()
- .reduce((parent, child) => {
- return new ContextualDomainObject(child, parent);
- });
-
- } else {
- let keyString = objectUtils.makeKeyString(domainObject.identifier);
- let oldModel = objectUtils.toOldFormat(domainObject);
-
- return instantiate(oldModel, keyString);
- }
- }.bind(openmct);
-
- openmct.legacyRegistry = new BundleRegistry();
- installDefaultBundles(openmct.legacyRegistry);
-
- const patchedStart = openmct.start.bind(openmct);
- openmct.start = async () => {
- openmct.legacyRegistry.register('adapter', openmct.legacyBundle);
- openmct.legacyRegistry.enable('adapter');
-
- openmct.legacyExtension('runs', {
- depends: ['navigationService'],
- implementation: function (navigationService) {
- navigationService
- .addListener(openmct.emit.bind(openmct, 'navigation'));
- }
- });
-
- // TODO: remove with legacy types.
- openmct.types.listKeys().forEach(function (typeKey) {
- const type = openmct.types.get(typeKey);
- const legacyDefinition = type.toLegacyDefinition();
- legacyDefinition.key = typeKey;
- openmct.legacyExtension('types', legacyDefinition);
- });
-
- const main = new Main();
- const angularInstance = await main.run(openmct);
-
- openmct.$angular = angularInstance;
- openmct.$injector.get('objectService');
-
- return patchedStart();
- };
-
- };
-}
diff --git a/src/plugins/licenses/Licenses.vue b/src/plugins/licenses/Licenses.vue
index c166e1915..c477a7ede 100644
--- a/src/plugins/licenses/Licenses.vue
+++ b/src/plugins/licenses/Licenses.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
-* Open MCT, Copyright (c) 2014-2021, United States Government
+* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/licenses/plugin.js b/src/plugins/licenses/plugin.js
index 7b9e9149c..cbaee5e0e 100644
--- a/src/plugins/licenses/plugin.js
+++ b/src/plugins/licenses/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/licenses/third-party-licenses.json b/src/plugins/licenses/third-party-licenses.json
index 184024eb4..c139298ce 100644
--- a/src/plugins/licenses/third-party-licenses.json
+++ b/src/plugins/licenses/third-party-licenses.json
@@ -256,13 +256,6 @@
"licenseFile": "/Users/akhenry/Code/licenses/node_modules/vue/LICENSE",
"licenseText": "The MIT License (MIT)\n\nCopyright (c) 2013-present, Yuxi (Evan) You\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.",
"copyright": "Copyright (c) 2013-present, Yuxi (Evan) You"
- },
- "zepto@1.2.0": {
- "licenses": "MIT",
- "repository": "https://github.com/madrobby/zepto",
- "path": "/Users/akhenry/Code/licenses/node_modules/zepto",
- "licenseFile": "/Users/akhenry/Code/licenses/node_modules/zepto/README.md",
- "licenseText": "Copyright (c) 2010-2018 Thomas Fuchs\nhttp://zeptojs.com/\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."
}
}
diff --git a/src/plugins/linkAction/LinkAction.js b/src/plugins/linkAction/LinkAction.js
index 592295eb2..9390b3e5a 100644
--- a/src/plugins/linkAction/LinkAction.js
+++ b/src/plugins/linkAction/LinkAction.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -33,10 +33,7 @@ export default class LinkAction {
}
appliesTo(objectPath) {
- let domainObject = objectPath[0];
- let type = domainObject && this.openmct.types.get(domainObject.type);
-
- return type && type.definition.creatable;
+ return true; // link away!
}
invoke(objectPath) {
@@ -77,6 +74,7 @@ export default class LinkAction {
{
name: "location",
control: "locator",
+ parent: parentDomainObject,
required: true,
validate: this.validate(parentDomainObject),
key: 'location'
@@ -92,11 +90,26 @@ export default class LinkAction {
validate(currentParent) {
return (data) => {
+
+ // default current parent to ROOT, if it's undefined, then it's a root level item
+ if (currentParent === undefined) {
+ currentParent = {
+ identifier: {
+ key: 'ROOT',
+ namespace: ''
+ }
+ };
+ }
+
const parentCandidate = data.value[0];
const currentParentKeystring = this.openmct.objects.makeKeyString(currentParent.identifier);
const parentCandidateKeystring = this.openmct.objects.makeKeyString(parentCandidate.identifier);
const objectKeystring = this.openmct.objects.makeKeyString(this.object.identifier);
+ if (!this.openmct.objects.isPersistable(parentCandidate.identifier)) {
+ return false;
+ }
+
if (!parentCandidateKeystring || !currentParentKeystring) {
return false;
}
diff --git a/src/plugins/linkAction/plugin.js b/src/plugins/linkAction/plugin.js
index 3889482bb..b573195e1 100644
--- a/src/plugins/linkAction/plugin.js
+++ b/src/plugins/linkAction/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/linkAction/pluginSpec.js b/src/plugins/linkAction/pluginSpec.js
index ac0d12d4c..353d0b8e8 100644
--- a/src/plugins/linkAction/pluginSpec.js
+++ b/src/plugins/linkAction/pluginSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/localStorage/LocalStorageObjectProvider.js b/src/plugins/localStorage/LocalStorageObjectProvider.js
index bf311c1a1..293fb1c60 100644
--- a/src/plugins/localStorage/LocalStorageObjectProvider.js
+++ b/src/plugins/localStorage/LocalStorageObjectProvider.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -41,6 +41,10 @@ export default class LocalStorageObjectProvider {
}
}
+ getAllObjects() {
+ return this.getSpaceAsObject();
+ }
+
create(object) {
return this.persistObject(object);
}
diff --git a/src/plugins/localStorage/pluginSpec.js b/src/plugins/localStorage/pluginSpec.js
index e4c68d911..f8eb4a605 100644
--- a/src/plugins/localStorage/pluginSpec.js
+++ b/src/plugins/localStorage/pluginSpec.js
@@ -1,6 +1,6 @@
/* eslint-disable no-invalid-this */
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/localTimeSystem/LocalTimeFormat.js b/src/plugins/localTimeSystem/LocalTimeFormat.js
index b51b538e3..77c358a45 100644
--- a/src/plugins/localTimeSystem/LocalTimeFormat.js
+++ b/src/plugins/localTimeSystem/LocalTimeFormat.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT Web, Copyright (c) 2014-2015, United States Government
+ * Open MCT Web, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/localTimeSystem/LocalTimeSystem.js b/src/plugins/localTimeSystem/LocalTimeSystem.js
index 3f08d07fe..ed4b70719 100644
--- a/src/plugins/localTimeSystem/LocalTimeSystem.js
+++ b/src/plugins/localTimeSystem/LocalTimeSystem.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT Web, Copyright (c) 2014-2015, United States Government
+ * Open MCT Web, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/localTimeSystem/plugin.js b/src/plugins/localTimeSystem/plugin.js
index 228107a21..062943091 100644
--- a/src/plugins/localTimeSystem/plugin.js
+++ b/src/plugins/localTimeSystem/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT Web, Copyright (c) 2014-2015, United States Government
+ * Open MCT Web, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/localTimeSystem/pluginSpec.js b/src/plugins/localTimeSystem/pluginSpec.js
index bcea24297..60642e822 100644
--- a/src/plugins/localTimeSystem/pluginSpec.js
+++ b/src/plugins/localTimeSystem/pluginSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/move/MoveAction.js b/src/plugins/move/MoveAction.js
index 618eff9ef..d9a4d144e 100644
--- a/src/plugins/move/MoveAction.js
+++ b/src/plugins/move/MoveAction.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -126,6 +126,7 @@ export default class MoveAction {
{
name: "Location",
control: "locator",
+ parent: parentDomainObject,
required: true,
validate: this.validate(parentDomainObject),
key: 'location'
@@ -144,6 +145,10 @@ export default class MoveAction {
const parentCandidatePath = data.value;
const parentCandidate = parentCandidatePath[0];
+ if (!this.openmct.objects.isPersistable(parentCandidate.identifier)) {
+ return false;
+ }
+
let currentParentKeystring = this.openmct.objects.makeKeyString(currentParent.identifier);
let parentCandidateKeystring = this.openmct.objects.makeKeyString(parentCandidate.identifier);
let objectKeystring = this.openmct.objects.makeKeyString(this.object.identifier);
@@ -174,8 +179,9 @@ export default class MoveAction {
let parentType = parent && this.openmct.types.get(parent.type);
let child = objectPath[0];
let childType = child && this.openmct.types.get(child.type);
+ let isPersistable = this.openmct.objects.isPersistable(child.identifier);
- if (child.locked || (parent && parent.locked)) {
+ if (child.locked || (parent && parent.locked) || !isPersistable) {
return false;
}
diff --git a/src/plugins/move/plugin.js b/src/plugins/move/plugin.js
index 9f50d55e3..125be0bc0 100644
--- a/src/plugins/move/plugin.js
+++ b/src/plugins/move/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/move/pluginSpec.js b/src/plugins/move/pluginSpec.js
index 367e6cc09..3e33b05f9 100644
--- a/src/plugins/move/pluginSpec.js
+++ b/src/plugins/move/pluginSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -91,6 +91,7 @@ describe("The Move Action plugin", () => {
});
describe("when moving an object to a new parent and removing from the old parent", () => {
+ let unObserve;
beforeEach((done) => {
openmct.router.path = [];
@@ -104,7 +105,7 @@ describe("The Move Action plugin", () => {
});
});
- openmct.objects.observe(parentObject, '*', (newObject) => {
+ unObserve = openmct.objects.observe(parentObject, '*', (newObject) => {
done();
});
@@ -113,6 +114,10 @@ describe("The Move Action plugin", () => {
moveAction.invoke([childObject, parentObject]);
});
+ afterEach(() => {
+ unObserve();
+ });
+
it("the child object's identifier should be in the new parent's composition", () => {
let newParentChild = anotherParentObject.composition[0];
expect(newParentChild).toEqual(childObject.identifier);
diff --git a/src/plugins/myItems/myItemsInterceptor.js b/src/plugins/myItems/myItemsInterceptor.js
index 94c7d96b7..375ee88e4 100644
--- a/src/plugins/myItems/myItemsInterceptor.js
+++ b/src/plugins/myItems/myItemsInterceptor.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/myItems/plugin.js b/src/plugins/myItems/plugin.js
index 2b95fa879..767d7dbc8 100644
--- a/src/plugins/myItems/plugin.js
+++ b/src/plugins/myItems/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -25,11 +25,15 @@ import myItemsInterceptor from "./myItemsInterceptor";
const MY_ITEMS_DEFAULT_NAME = 'My Items';
-export default function MyItemsPlugin(name = MY_ITEMS_DEFAULT_NAME, namespace = '') {
+export default function MyItemsPlugin(name = MY_ITEMS_DEFAULT_NAME, namespace = '', priority = undefined) {
return function install(openmct) {
const identifier = createMyItemsIdentifier(namespace);
+ if (priority === undefined) {
+ priority = openmct.priority.LOW;
+ }
+
openmct.objects.addGetInterceptor(myItemsInterceptor(openmct, identifier, name));
- openmct.objects.addRoot(identifier);
+ openmct.objects.addRoot(identifier, priority);
};
}
diff --git a/src/plugins/myItems/pluginSpec.js b/src/plugins/myItems/pluginSpec.js
index f9de48d41..b463b0a5e 100644
--- a/src/plugins/myItems/pluginSpec.js
+++ b/src/plugins/myItems/pluginSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/newFolderAction/newFolderAction.js b/src/plugins/newFolderAction/newFolderAction.js
index 0ddabec3d..2615746c5 100644
--- a/src/plugins/newFolderAction/newFolderAction.js
+++ b/src/plugins/newFolderAction/newFolderAction.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/newFolderAction/plugin.js b/src/plugins/newFolderAction/plugin.js
index 355c6ace3..76b4cf785 100644
--- a/src/plugins/newFolderAction/plugin.js
+++ b/src/plugins/newFolderAction/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/newFolderAction/pluginSpec.js b/src/plugins/newFolderAction/pluginSpec.js
index bdc7a59c9..1880b11d5 100644
--- a/src/plugins/newFolderAction/pluginSpec.js
+++ b/src/plugins/newFolderAction/pluginSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/notebook/NotebookType.js b/src/plugins/notebook/NotebookType.js
new file mode 100644
index 000000000..d112502b7
--- /dev/null
+++ b/src/plugins/notebook/NotebookType.js
@@ -0,0 +1,88 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+import { IMAGE_MIGRATION_VER } from '../notebook/utils/notebook-migration';
+
+export default class NotebookType {
+ constructor(name, description, icon) {
+ this.name = name;
+ this.description = description;
+ this.cssClass = icon;
+ this.creatable = true;
+ this.form = [
+ {
+ key: 'defaultSort',
+ name: 'Entry Sorting',
+ control: 'select',
+ options: [
+ {
+ name: 'Newest First',
+ value: "newest"
+ },
+ {
+ name: 'Oldest First',
+ value: "oldest"
+ }
+ ],
+ cssClass: 'l-inline',
+ property: [
+ "configuration",
+ "defaultSort"
+ ]
+ },
+ {
+ key: 'sectionTitle',
+ name: 'Section Title',
+ control: 'textfield',
+ cssClass: 'l-inline',
+ required: true,
+ property: [
+ "configuration",
+ "sectionTitle"
+ ]
+ },
+ {
+ key: 'pageTitle',
+ name: 'Page Title',
+ control: 'textfield',
+ cssClass: 'l-inline',
+ required: true,
+ property: [
+ "configuration",
+ "pageTitle"
+ ]
+ }
+ ];
+ }
+
+ initialize(domainObject) {
+ domainObject.configuration = {
+ defaultSort: 'oldest',
+ entries: {},
+ imageMigrationVer: IMAGE_MIGRATION_VER,
+ pageTitle: 'Page',
+ sections: [],
+ sectionTitle: 'Section',
+ type: 'General'
+ };
+ }
+}
diff --git a/src/plugins/notebook/NotebookViewProvider.js b/src/plugins/notebook/NotebookViewProvider.js
new file mode 100644
index 000000000..66617789c
--- /dev/null
+++ b/src/plugins/notebook/NotebookViewProvider.js
@@ -0,0 +1,72 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+import Vue from 'vue';
+import Notebook from './components/Notebook.vue';
+import Agent from '@/utils/agent/Agent';
+
+export default class NotebookViewProvider {
+ constructor(openmct, name, key, type, cssClass, snapshotContainer) {
+ this.openmct = openmct;
+ this.key = key;
+ this.name = `${name} View`;
+ this.type = type;
+ this.cssClass = cssClass;
+ this.snapshotContainer = snapshotContainer;
+ }
+
+ canView(domainObject) {
+ return domainObject.type === this.type;
+ }
+
+ view(domainObject) {
+ let component;
+ let openmct = this.openmct;
+ let snapshotContainer = this.snapshotContainer;
+ let agent = new Agent(window);
+
+ return {
+ show(container) {
+ component = new Vue({
+ el: container,
+ components: {
+ Notebook
+ },
+ provide: {
+ openmct,
+ snapshotContainer,
+ agent
+ },
+ data() {
+ return {
+ domainObject
+ };
+ },
+ template: '<Notebook :domain-object="domainObject"></Notebook>'
+ });
+ },
+ destroy() {
+ component.$destroy();
+ }
+ };
+ }
+}
diff --git a/src/plugins/notebook/components/Notebook.vue b/src/plugins/notebook/components/Notebook.vue
index 18542469c..4d493525a 100644
--- a/src/plugins/notebook/components/Notebook.vue
+++ b/src/plugins/notebook/components/Notebook.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -21,48 +21,58 @@
*****************************************************************************/
<template>
-<div class="c-notebook">
+<div
+ class="c-notebook"
+ :class="[{'c-notebook--restricted' : isRestricted }]"
+>
<div class="c-notebook__head">
- <Search class="c-notebook__search"
- :value="search"
- @input="search = $event"
- @clear="resetSearch()"
+ <Search
+ class="c-notebook__search"
+ :value="search"
+ @input="search = $event"
+ @clear="resetSearch()"
/>
</div>
- <SearchResults v-if="search.length"
- ref="searchResults"
- :domain-object="domainObject"
- :results="searchResults"
- @changeSectionPage="changeSelectedSection"
- @updateEntries="updateEntries"
+ <SearchResults
+ v-if="search.length"
+ ref="searchResults"
+ :domain-object="domainObject"
+ :results="searchResults"
+ @cancelEdit="cancelTransaction"
+ @editingEntry="startTransaction"
+ @changeSectionPage="changeSelectedSection"
+ @updateEntries="updateEntries"
/>
- <div v-if="!search.length"
- class="c-notebook__body"
+ <div
+ v-if="!search.length"
+ class="c-notebook__body"
>
- <Sidebar ref="sidebar"
- class="c-notebook__nav c-sidebar c-drawer c-drawer--align-left"
- :class="[{'is-expanded': showNav}, {'c-drawer--push': !sidebarCoversEntries}, {'c-drawer--overlays': sidebarCoversEntries}]"
- :default-page-id="defaultPageId"
- :selected-page-id="getSelectedPageId()"
- :default-section-id="defaultSectionId"
- :selected-section-id="getSelectedSectionId()"
- :domain-object="domainObject"
- :page-title="domainObject.configuration.pageTitle"
- :section-title="domainObject.configuration.sectionTitle"
- :sections="sections"
- :sidebar-covers-entries="sidebarCoversEntries"
- @defaultPageDeleted="cleanupDefaultNotebook"
- @defaultSectionDeleted="cleanupDefaultNotebook"
- @pagesChanged="pagesChanged"
- @selectPage="selectPage"
- @sectionsChanged="sectionsChanged"
- @selectSection="selectSection"
- @toggleNav="toggleNav"
+ <Sidebar
+ ref="sidebar"
+ class="c-notebook__nav c-sidebar c-drawer c-drawer--align-left"
+ :class="[{'is-expanded': showNav}, {'c-drawer--push': !sidebarCoversEntries}, {'c-drawer--overlays': sidebarCoversEntries}]"
+ :default-page-id="defaultPageId"
+ :selected-page-id="getSelectedPageId()"
+ :default-section-id="defaultSectionId"
+ :selected-section-id="getSelectedSectionId()"
+ :domain-object="domainObject"
+ :page-title="domainObject.configuration.pageTitle"
+ :section-title="domainObject.configuration.sectionTitle"
+ :sections="sections"
+ :sidebar-covers-entries="sidebarCoversEntries"
+ @defaultPageDeleted="cleanupDefaultNotebook"
+ @defaultSectionDeleted="cleanupDefaultNotebook"
+ @pagesChanged="pagesChanged"
+ @selectPage="selectPage"
+ @sectionsChanged="sectionsChanged"
+ @selectSection="selectSection"
+ @toggleNav="toggleNav"
/>
<div class="c-notebook__page-view">
<div class="c-notebook__page-view__header">
- <button class="c-notebook__toggle-nav-button c-icon-button c-icon-button--major icon-menu-hamburger"
- @click="toggleNav"
+ <button
+ class="c-notebook__toggle-nav-button c-icon-button c-icon-button--major icon-menu-hamburger"
+ @click="toggleNav"
></button>
<div class="c-notebook__page-view__path c-path">
<span class="c-notebook__path__section c-path__item">
@@ -73,61 +83,95 @@
</span>
</div>
<div class="c-notebook__page-view__controls">
- <select v-model="showTime"
- class="c-notebook__controls__time"
+ <select
+ v-model="showTime"
+ class="c-notebook__controls__time"
>
- <option value="0"
- :selected="showTime === 0"
+ <option
+ value="0"
+ :selected="showTime === 0"
>
Show all
</option>
- <option value="1"
- :selected="showTime === 1"
+ <option
+ value="1"
+ :selected="showTime === 1"
>Last hour</option>
- <option value="8"
- :selected="showTime === 8"
+ <option
+ value="8"
+ :selected="showTime === 8"
>Last 8 hours</option>
- <option value="24"
- :selected="showTime === 24"
+ <option
+ value="24"
+ :selected="showTime === 24"
>Last 24 hours</option>
</select>
- <select v-model="defaultSort"
- class="c-notebook__controls__time"
+ <select
+ v-model="defaultSort"
+ class="c-notebook__controls__time"
>
- <option value="newest"
- :selected="defaultSort === 'newest'"
+ <option
+ value="newest"
+ :selected="defaultSort === 'newest'"
>Newest first</option>
- <option value="oldest"
- :selected="defaultSort === 'oldest'"
+ <option
+ value="oldest"
+ :selected="defaultSort === 'oldest'"
>Oldest first</option>
</select>
</div>
</div>
- <div class="c-notebook__drag-area icon-plus"
- @click="newEntry()"
- @dragover="dragOver"
- @drop.capture="dropCapture"
- @drop="dropOnEntry($event)"
+ <div
+ v-if="selectedPage && !selectedPage.isLocked"
+ class="c-notebook__drag-area icon-plus"
+ @click="newEntry()"
+ @dragover="dragOver"
+ @drop.capture="dropCapture"
+ @drop="dropOnEntry($event)"
>
<span class="c-notebook__drag-area__label">
To start a new entry, click here or drag and drop any object
</span>
</div>
- <div v-if="selectedSection && selectedPage"
- ref="notebookEntries"
- class="c-notebook__entries"
+ <div
+ v-if="selectedPage && selectedPage.isLocked"
+ class="c-notebook__page-locked"
>
- <NotebookEntry v-for="entry in filteredAndSortedEntries"
- :key="entry.id"
- :entry="entry"
- :domain-object="domainObject"
- :selected-page="selectedPage"
- :selected-section="selectedSection"
- :read-only="false"
- @deleteEntry="deleteEntry"
- @updateEntry="updateEntry"
+ <div class="icon-lock"></div>
+ <div class="c-notebook__page-locked__message">This page has been committed and cannot be modified or removed</div>
+ </div>
+ <div
+ v-if="selectedSection && selectedPage"
+ ref="notebookEntries"
+ class="c-notebook__entries"
+ aria-label="Notebook Entries"
+ >
+ <NotebookEntry
+ v-for="entry in filteredAndSortedEntries"
+ :key="entry.id"
+ :entry="entry"
+ :domain-object="domainObject"
+ :selected-page="selectedPage"
+ :selected-section="selectedSection"
+ :read-only="false"
+ :is-locked="selectedPage.isLocked"
+ @cancelEdit="cancelTransaction"
+ @editingEntry="startTransaction"
+ @deleteEntry="deleteEntry"
+ @updateEntry="updateEntry"
/>
</div>
+ <div
+ v-if="showLockButton"
+ class="c-notebook__commit-entries-control"
+ >
+ <button
+ class="c-button c-button--major commit-button icon-lock"
+ title="Commit entries and lock this page from further changes"
+ @click="lockPage()"
+ >
+ <span class="c-button__label">Commit Entries</span>
+ </button></div>
</div>
</div>
</div>
@@ -141,7 +185,7 @@ import Sidebar from './Sidebar.vue';
import { clearDefaultNotebook, getDefaultNotebook, setDefaultNotebook, setDefaultNotebookSectionId, setDefaultNotebookPageId } from '../utils/notebook-storage';
import { addNotebookEntry, createNewEmbed, getEntryPosById, getNotebookEntries, mutateObject } from '../utils/notebook-entries';
import { saveNotebookImageDomainObject, updateNamespaceOfDomainObject } from '../utils/notebook-image';
-import { NOTEBOOK_VIEW_TYPE } from '../notebook-constants';
+import { isNotebookViewType, RESTRICTED_NOTEBOOK_TYPE } from '../notebook-constants';
import { debounce } from 'lodash';
import objectLink from '../../../ui/mixins/object-link';
@@ -157,7 +201,7 @@ export default {
SearchResults,
Sidebar
},
- inject: ['openmct', 'snapshotContainer'],
+ inject: ['agent', 'openmct', 'snapshotContainer'],
props: {
domainObject: {
type: Object,
@@ -172,27 +216,16 @@ export default {
selectedPageId: this.getSelectedPageId(),
defaultSort: this.domainObject.configuration.defaultSort,
focusEntryId: null,
+ isRestricted: this.domainObject.type === RESTRICTED_NOTEBOOK_TYPE,
search: '',
searchResults: [],
- showTime: 0,
+ showTime: this.domainObject.configuration.showTime || 0,
showNav: false,
- sidebarCoversEntries: false
+ sidebarCoversEntries: false,
+ filteredAndSortedEntries: []
};
},
computed: {
- filteredAndSortedEntries() {
- const filterTime = Date.now();
- const pageEntries = getNotebookEntries(this.domainObject, this.selectedSection, this.selectedPage) || [];
-
- const hours = parseInt(this.showTime, 10);
- const filteredPageEntriesByTime = hours
- ? pageEntries.filter(entry => (filterTime - entry.createdOn) <= hours * 60 * 60 * 1000)
- : pageEntries;
-
- return this.defaultSort === 'oldest'
- ? filteredPageEntriesByTime
- : [...filteredPageEntriesByTime].reverse();
- },
pages() {
return this.getPages() || [];
},
@@ -233,11 +266,23 @@ export default {
}
return this.sections[0];
+ },
+ showLockButton() {
+ const entries = getNotebookEntries(this.domainObject, this.selectedSection, this.selectedPage);
+
+ return entries && entries.length > 0 && this.isRestricted && !this.selectedPage.isLocked;
}
},
watch: {
search() {
this.getSearchResults();
+ },
+ defaultSort() {
+ mutateObject(this.openmct, this.domainObject, 'configuration.defaultSort', this.defaultSort);
+ this.filterAndSortEntries();
+ },
+ showTime() {
+ mutateObject(this.openmct, this.domainObject, 'configuration.showTime', this.showTime);
}
},
beforeMount() {
@@ -250,6 +295,7 @@ export default {
window.addEventListener('orientationchange', this.formatSidebar);
window.addEventListener('hashchange', this.setSectionAndPageFromUrl);
+ this.filterAndSortEntries();
},
beforeDestroy() {
if (this.unlisten) {
@@ -266,7 +312,7 @@ export default {
},
methods: {
changeSectionPage(newParams, oldParams, changedParams) {
- if (newParams.view !== NOTEBOOK_VIEW_TYPE) {
+ if (isNotebookViewType(newParams.view)) {
return;
}
@@ -287,6 +333,19 @@ export default {
}
});
},
+ filterAndSortEntries() {
+ const filterTime = Date.now();
+ const pageEntries = getNotebookEntries(this.domainObject, this.selectedSection, this.selectedPage) || [];
+
+ const hours = parseInt(this.showTime, 10);
+ const filteredPageEntriesByTime = hours
+ ? pageEntries.filter(entry => (filterTime - entry.createdOn) <= hours * 60 * 60 * 1000)
+ : pageEntries;
+
+ this.filteredAndSortedEntries = this.defaultSort === 'oldest'
+ ? filteredPageEntriesByTime
+ : [...filteredPageEntriesByTime].reverse();
+ },
changeSelectedSection({ sectionId, pageId }) {
const sections = this.sections.map(s => {
s.isSelected = false;
@@ -316,9 +375,46 @@ export default {
cleanupDefaultNotebook() {
this.defaultPageId = undefined;
this.defaultSectionId = undefined;
- this.removeDefaultClass(this.domainObject);
+ this.removeDefaultClass(this.domainObject.identifier);
clearDefaultNotebook();
},
+ lockPage() {
+ let prompt = this.openmct.overlays.dialog({
+ iconClass: 'alert',
+ message: "This action will lock this page and disallow any new entries, or editing of existing entries. Do you want to continue?",
+ buttons: [
+ {
+ label: 'Lock Page',
+ callback: () => {
+ let sections = this.getSections();
+ this.selectedPage.isLocked = true;
+
+ // cant be default if it's locked
+ if (this.selectedPage.id === this.defaultPageId) {
+ this.cleanupDefaultNotebook();
+ }
+
+ if (!this.selectedSection.isLocked) {
+ this.selectedSection.isLocked = true;
+ }
+
+ mutateObject(this.openmct, this.domainObject, 'configuration.sections', sections);
+
+ if (!this.domainObject.locked) {
+ mutateObject(this.openmct, this.domainObject, 'locked', true);
+ }
+
+ prompt.dismiss();
+ }
+ }, {
+ label: 'Cancel',
+ callback: () => {
+ prompt.dismiss();
+ }
+ }
+ ]
+ });
+ },
setSectionAndPageFromUrl() {
let sectionId = this.getSectionIdFromUrl() || this.getDefaultSectionId() || this.getSelectedSectionId();
let pageId = this.getPageIdFromUrl() || this.getDefaultPageId() || this.getSelectedPageId();
@@ -358,16 +454,40 @@ export default {
const entries = getNotebookEntries(this.domainObject, this.selectedSection, this.selectedPage);
entries.splice(entryPos, 1);
this.updateEntries(entries);
+ this.filterAndSortEntries();
+ this.removeAnnotations(entryId);
dialog.dismiss();
}
},
{
label: "Cancel",
- callback: () => dialog.dismiss()
+ callback: () => {
+ dialog.dismiss();
+ }
}
]
});
},
+ async removeAnnotations(entryId) {
+ const targetKeyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
+ const query = {
+ targetKeyString,
+ entryId
+ };
+ const existingAnnotation = await this.openmct.annotation.getAnnotation(query, this.openmct.objects.SEARCH_TYPES.NOTEBOOK_ANNOTATIONS);
+ this.openmct.annotation.removeAnnotationTags(existingAnnotation);
+ },
+ checkEntryPos(entry) {
+ const entryPos = getEntryPosById(entry.id, this.domainObject, this.selectedSection, this.selectedPage);
+ if (entryPos === -1) {
+ this.openmct.notifications.alert('Warning: unable to tag entry');
+ console.error(`unable to tag entry ${entry} from section ${this.selectedSection}, page ${this.selectedPage}`);
+
+ return false;
+ }
+
+ return true;
+ },
dragOver(event) {
event.preventDefault();
event.dataTransfer.dropEffect = "copy";
@@ -429,10 +549,9 @@ export default {
- tablet portrait
- in a layout frame (within .c-so-view)
*/
- const classList = document.querySelector('body').classList;
- const isPhone = Array.from(classList).includes('phone');
- const isTablet = Array.from(classList).includes('tablet');
- const isPortrait = window.screen.orientation.type.includes('portrait');
+ const isPhone = this.agent.isPhone();
+ const isTablet = this.agent.isTablet();
+ const isPortrait = this.agent.isPortrait();
const isInLayout = Boolean(this.$el.closest('.c-so-view'));
const sidebarCoversEntries = (isPhone || (isTablet && isPortrait) || isInLayout);
this.sidebarCoversEntries = sidebarCoversEntries;
@@ -586,12 +705,13 @@ export default {
return section.id;
},
- newEntry(embed = null) {
+ async newEntry(embed = null) {
this.resetSearch();
const notebookStorage = this.createNotebookStorageObject();
this.updateDefaultNotebook(notebookStorage);
- const id = addNotebookEntry(this.openmct, this.domainObject, notebookStorage, embed);
+ const id = await addNotebookEntry(this.openmct, this.domainObject, notebookStorage, embed);
this.focusEntryId = id;
+ this.filterAndSortEntries();
},
orientationChange() {
this.formatSidebar();
@@ -613,12 +733,8 @@ export default {
this.sectionsChanged({ sections });
},
- removeDefaultClass(defaultNotebookIdentifier) {
- if (!defaultNotebookIdentifier) {
- return;
- }
-
- this.openmct.status.delete(defaultNotebookIdentifier);
+ removeDefaultClass(identifier) {
+ this.openmct.status.delete(identifier);
},
resetSearch() {
this.search = '';
@@ -627,27 +743,25 @@ export default {
toggleNav() {
this.showNav = !this.showNav;
},
- updateDefaultNotebook(notebookStorage) {
- const defaultNotebook = getDefaultNotebook();
- const defaultNotebookIdentifier = defaultNotebook && defaultNotebook.identifier;
- const isSameNotebook = defaultNotebookIdentifier
- && this.openmct.objects.areIdsEqual(defaultNotebookIdentifier, notebookStorage.identifier);
- if (!isSameNotebook) {
- this.removeDefaultClass(defaultNotebookIdentifier);
- }
+ updateDefaultNotebook(updatedNotebookStorageObject) {
+ if (!this.isDefaultNotebook()) {
+ const persistedNotebookStorageObject = getDefaultNotebook();
+ if (persistedNotebookStorageObject
+ && persistedNotebookStorageObject.identifier !== undefined) {
+ this.removeDefaultClass(persistedNotebookStorageObject.identifier);
+ }
- if (!defaultNotebookIdentifier || !isSameNotebook) {
- setDefaultNotebook(this.openmct, notebookStorage, this.domainObject);
+ setDefaultNotebook(this.openmct, updatedNotebookStorageObject, this.domainObject);
}
- if (this.defaultSectionId !== notebookStorage.defaultSectionId) {
- setDefaultNotebookSectionId(notebookStorage.defaultSectionId);
- this.defaultSectionId = notebookStorage.defaultSectionId;
+ if (this.defaultSectionId !== updatedNotebookStorageObject.defaultSectionId) {
+ setDefaultNotebookSectionId(updatedNotebookStorageObject.defaultSectionId);
+ this.defaultSectionId = updatedNotebookStorageObject.defaultSectionId;
}
- if (this.defaultPageId !== notebookStorage.defaultPageId) {
- setDefaultNotebookPageId(notebookStorage.defaultPageId);
- this.defaultPageId = notebookStorage.defaultPageId;
+ if (this.defaultPageId !== updatedNotebookStorageObject.defaultPageId) {
+ setDefaultNotebookPageId(updatedNotebookStorageObject.defaultPageId);
+ this.defaultPageId = updatedNotebookStorageObject.defaultPageId;
}
},
updateDefaultNotebookSection(sections, id) {
@@ -665,7 +779,7 @@ export default {
if (defaultNotebookSectionId === id) {
const section = sections.find(s => s.id === id);
if (!section) {
- this.removeDefaultClass(this.domainObject);
+ this.removeDefaultClass(this.domainObject.identifier);
clearDefaultNotebook();
return;
@@ -691,6 +805,8 @@ export default {
notebookEntries[this.selectedSection.id][this.selectedPage.id] = entries;
mutateObject(this.openmct, this.domainObject, 'configuration.entries', notebookEntries);
+
+ this.saveTransaction();
},
getPageIdFromUrl() {
return this.openmct.router.getParams().pageId;
@@ -715,6 +831,7 @@ export default {
this.selectedPageId = pageId;
this.syncUrlWithPageAndSection();
+ this.filterAndSortEntries();
},
selectSection(sectionId) {
if (!sectionId) {
@@ -727,6 +844,40 @@ export default {
this.selectPage(pageId);
this.syncUrlWithPageAndSection();
+ this.filterAndSortEntries();
+ },
+ activeTransaction() {
+ return this.openmct.objects.getActiveTransaction();
+ },
+ startTransaction() {
+ if (!this.openmct.editor.isEditing()) {
+ this.openmct.objects.startTransaction();
+ }
+ },
+ saveTransaction() {
+ const transaction = this.activeTransaction();
+
+ if (!transaction || this.openmct.editor.isEditing()) {
+ return;
+ }
+
+ return transaction.commit()
+ .catch(error => {
+ throw error;
+ }).finally(() => {
+ this.openmct.objects.endTransaction();
+ });
+ },
+ cancelTransaction() {
+ if (!this.openmct.editor.isEditing()) {
+ const transaction = this.activeTransaction();
+ transaction.cancel()
+ .catch(error => {
+ throw error;
+ }).finally(() => {
+ this.openmct.objects.endTransaction();
+ });
+ }
}
}
};
diff --git a/src/plugins/notebook/components/NotebookEmbed.vue b/src/plugins/notebook/components/NotebookEmbed.vue
index 81bdef31d..49cace8f2 100644
--- a/src/plugins/notebook/components/NotebookEmbed.vue
+++ b/src/plugins/notebook/components/NotebookEmbed.vue
@@ -1,21 +1,24 @@
<template>
<div class="c-snapshot c-ne__embed">
- <div v-if="embed.snapshot"
- class="c-ne__embed__snap-thumb"
- @click="openSnapshot()"
+ <div
+ v-if="embed.snapshot"
+ class="c-ne__embed__snap-thumb"
+ @click="openSnapshot()"
>
<img :src="thumbnailImage">
</div>
<div class="c-ne__embed__info">
<div class="c-ne__embed__name">
- <a class="c-ne__embed__link"
- :class="embed.cssClass"
- @click="changeLocation"
+ <a
+ class="c-ne__embed__link"
+ :class="embed.cssClass"
+ @click="changeLocation"
>{{ embed.name }}</a>
<PopupMenu :popup-menu-items="popupMenuItems" />
</div>
- <div v-if="embed.snapshot"
- class="c-ne__embed__time"
+ <div
+ v-if="embed.snapshot"
+ class="c-ne__embed__time"
>
{{ createdOn }}
</div>
@@ -48,6 +51,12 @@ export default {
return {};
}
},
+ isLocked: {
+ type: Boolean,
+ default() {
+ return false;
+ }
+ },
isSnapshotContainer: {
type: Boolean,
default() {
@@ -76,6 +85,15 @@ export default {
: this.embed.snapshot.src;
}
},
+ watch: {
+ isLocked(value) {
+ if (value === true) {
+ let index = this.popupMenuItems.findIndex((item) => item.id === 'removeEmbed');
+
+ this.$delete(this.popupMenuItems, index);
+ }
+ }
+ },
mounted() {
this.addPopupMenuItems();
this.imageExporter = new ImageExporter(this.openmct);
@@ -83,17 +101,24 @@ export default {
methods: {
addPopupMenuItems() {
const removeEmbed = {
+ id: 'removeEmbed',
cssClass: 'icon-trash',
name: this.removeActionString,
callback: this.getRemoveDialog.bind(this)
};
const preview = {
+ id: 'preview',
cssClass: 'icon-eye-open',
name: 'Preview',
callback: this.previewEmbed.bind(this)
};
- this.popupMenuItems = [removeEmbed, preview];
+ this.popupMenuItems = [preview];
+
+ if (!this.isLocked) {
+ this.popupMenuItems.unshift(removeEmbed);
+ }
+
},
annotateSnapshot() {
const annotateVue = new Vue({
diff --git a/src/plugins/notebook/components/NotebookEntry.vue b/src/plugins/notebook/components/NotebookEntry.vue
index bfbf5831f..f90aecb03 100644
--- a/src/plugins/notebook/components/NotebookEntry.vue
+++ b/src/plugins/notebook/components/NotebookEntry.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -21,15 +21,25 @@
*****************************************************************************/
<template>
-<div class="c-notebook__entry c-ne has-local-controls"
- @dragover="changeCursor"
- @drop.capture="cancelEditMode"
- @drop.prevent="dropOnEntry"
+<div
+ class="c-notebook__entry c-ne has-local-controls has-tag-applier"
+ aria-label="Notebook Entry"
+ :class="{ 'locked': isLocked }"
+ @dragover="changeCursor"
+ @drop.capture="cancelEditMode"
+ @drop.prevent="dropOnEntry"
>
<div class="c-ne__time-and-content">
- <div class="c-ne__time">
- <span>{{ createdOnDate }}</span>
- <span>{{ createdOnTime }}</span>
+ <div class="c-ne__time-and-creator">
+ <span class="c-ne__created-date">{{ createdOnDate }}</span>
+ <span class="c-ne__created-time">{{ createdOnTime }}</span>
+
+ <span
+ v-if="entry.createdBy"
+ class="c-ne__creator"
+ >
+ <span class="icon-person"></span> {{ entry.createdBy }}
+ </span>
</div>
<div class="c-ne__content">
<template v-if="readOnly && result">
@@ -45,12 +55,14 @@
/>
</div>
</template>
- <template v-else>
+ <template v-else-if="!isLocked">
<div
:id="entry.id"
class="c-ne__text c-ne__input"
+ aria-label="Notebook Entry Input"
tabindex="0"
- contenteditable
+ contenteditable="true"
+ @focus="editingEntry()"
@blur="updateEntryValue($event)"
@keydown.enter.exact.prevent
@keyup.enter.exact.prevent="forceBlur($event)"
@@ -58,28 +70,53 @@
>
</div>
</template>
+
+ <template v-else>
+ <div
+ :id="entry.id"
+ class="c-ne__text"
+ contenteditable="false"
+ tabindex="0"
+ v-text="entry.text"
+ >
+ </div>
+ </template>
+
+ <TagEditor
+ :domain-object="domainObject"
+ :annotation-query="annotationQuery"
+ :annotation-type="openmct.annotation.ANNOTATION_TYPES.NOTEBOOK"
+ :annotation-search-type="openmct.objects.SEARCH_TYPES.NOTEBOOK_ANNOTATIONS"
+ :target-specific-details="{entryId: entry.id}"
+ />
+
<div class="c-snapshots c-ne__embeds">
- <NotebookEmbed v-for="embed in entry.embeds"
- :key="embed.id"
- :embed="embed"
- @removeEmbed="removeEmbed"
- @updateEmbed="updateEmbed"
+ <NotebookEmbed
+ v-for="embed in entry.embeds"
+ :key="embed.id"
+ :embed="embed"
+ :is-locked="isLocked"
+ @removeEmbed="removeEmbed"
+ @updateEmbed="updateEmbed"
/>
</div>
</div>
</div>
- <div v-if="!readOnly"
- class="c-ne__local-controls--hidden"
+ <div
+ v-if="!readOnly && !isLocked"
+ class="c-ne__local-controls--hidden"
>
- <button class="c-icon-button c-icon-button--major icon-trash"
- title="Delete this entry"
- tabindex="-1"
- @click="deleteEntry"
+ <button
+ class="c-icon-button c-icon-button--major icon-trash"
+ title="Delete this entry"
+ tabindex="-1"
+ @click="deleteEntry"
>
</button>
</div>
- <div v-if="readOnly"
- class="c-ne__section-and-page"
+ <div
+ v-if="readOnly"
+ class="c-ne__section-and-page"
>
<a
class="c-click-link"
@@ -102,6 +139,7 @@
<script>
import NotebookEmbed from './NotebookEmbed.vue';
+import TagEditor from '../../../ui/components/tags/TagEditor.vue';
import TextHighlight from '../../../utils/textHighlight/TextHighlight.vue';
import { createNewEmbed } from '../utils/notebook-entries';
import { saveNotebookImageDomainObject, updateNamespaceOfDomainObject } from '../utils/notebook-image';
@@ -111,7 +149,8 @@ import Moment from 'moment';
export default {
components: {
NotebookEmbed,
- TextHighlight
+ TextHighlight,
+ TagEditor
},
inject: ['openmct', 'snapshotContainer'],
props: {
@@ -150,12 +189,26 @@ export default {
default() {
return true;
}
+ },
+ isLocked: {
+ type: Boolean,
+ default() {
+ return false;
+ }
}
},
computed: {
createdOnDate() {
return this.formatTime(this.entry.createdOn, 'YYYY-MM-DD');
},
+ annotationQuery() {
+ const targetKeyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
+
+ return {
+ targetKeyString,
+ entryId: this.entry.id
+ };
+ },
createdOnTime() {
return this.formatTime(this.entry.createdOn, 'HH:mm:ss');
},
@@ -182,7 +235,7 @@ export default {
this.dropOnEntry = this.dropOnEntry.bind(this);
},
methods: {
- addNewEmbed(objectPath) {
+ async addNewEmbed(objectPath) {
const bounds = this.openmct.time.bounds();
const snapshotMeta = {
bounds,
@@ -190,7 +243,7 @@ export default {
objectPath,
openmct: this.openmct
};
- const newEmbed = createNewEmbed(snapshotMeta);
+ const newEmbed = await createNewEmbed(snapshotMeta);
this.entry.embeds.push(newEmbed);
},
cancelEditMode(event) {
@@ -199,15 +252,21 @@ export default {
this.openmct.editor.cancel();
}
},
- changeCursor() {
+ changeCursor(event) {
event.preventDefault();
- event.dataTransfer.dropEffect = "copy";
+
+ if (!this.isLocked) {
+ event.dataTransfer.dropEffect = 'copy';
+ } else {
+ event.dataTransfer.dropEffect = 'none';
+ event.dataTransfer.effectAllowed = 'none';
+ }
},
deleteEntry() {
this.$emit('deleteEntry', this.entry.id);
},
- dropOnEntry($event) {
- event.stopImmediatePropagation();
+ async dropOnEntry($event) {
+ $event.stopImmediatePropagation();
const snapshotId = $event.dataTransfer.getData('openmct/snapshot/id');
if (snapshotId.length) {
@@ -221,7 +280,7 @@ export default {
} else {
const data = $event.dataTransfer.getData('openmct/domain-object-path');
const objectPath = JSON.parse(data);
- this.addNewEmbed(objectPath);
+ await this.addNewEmbed(objectPath);
}
this.$emit('updateEntry', this.entry);
@@ -276,11 +335,16 @@ export default {
this.$emit('updateEntry', this.entry);
},
+ editingEntry() {
+ this.$emit('editingEntry');
+ },
updateEntryValue($event) {
const value = $event.target.innerText;
if (value !== this.entry.text && value.match(/\S/)) {
this.entry.text = value;
this.$emit('updateEntry', this.entry);
+ } else {
+ this.$emit('cancelEdit');
}
}
}
diff --git a/src/plugins/notebook/components/NotebookSnapshotContainer.vue b/src/plugins/notebook/components/NotebookSnapshotContainer.vue
index 96fd506ca..9f35801ce 100644
--- a/src/plugins/notebook/components/NotebookSnapshotContainer.vue
+++ b/src/plugins/notebook/components/NotebookSnapshotContainer.vue
@@ -8,39 +8,45 @@
<div class="c-object-label__name">
Notebook Snapshots
</div>
- <div v-if="snapshots.length"
- class="l-browse-bar__object-details"
+ <div
+ v-if="snapshots.length"
+ class="l-browse-bar__object-details"
>{{ snapshots.length }} of {{ getNotebookSnapshotMaxCount() }}
</div>
</div>
- <PopupMenu v-if="snapshots.length > 0"
- :popup-menu-items="popupMenuItems"
+ <PopupMenu
+ v-if="snapshots.length > 0"
+ :popup-menu-items="popupMenuItems"
/>
</div>
</div>
<div class="l-browse-bar__end">
- <button class="c-click-icon c-click-icon--major icon-x"
- @click="close"
+ <button
+ class="c-click-icon c-click-icon--major icon-x"
+ @click="close"
></button>
</div>
</div><!-- closes l-browse-bar -->
<div class="c-snapshots">
- <span v-for="snapshot in snapshots"
- :key="snapshot.embedObject.id"
- draggable="true"
- @dragstart="startEmbedDrag(snapshot, $event)"
+ <span
+ v-for="snapshot in snapshots"
+ :key="snapshot.embedObject.id"
+ draggable="true"
+ @dragstart="startEmbedDrag(snapshot, $event)"
>
- <NotebookEmbed ref="notebookEmbed"
- :key="snapshot.embedObject.id"
- :embed="snapshot.embedObject"
- :is-snapshot-container="true"
- :remove-action-string="'Delete Snapshot'"
- @removeEmbed="removeSnapshot"
+ <NotebookEmbed
+ ref="notebookEmbed"
+ :key="snapshot.embedObject.id"
+ :embed="snapshot.embedObject"
+ :is-snapshot-container="true"
+ :remove-action-string="'Delete Snapshot'"
+ @removeEmbed="removeSnapshot"
/>
</span>
- <div v-if="!snapshots.length > 0"
- class="hint"
+ <div
+ v-if="!snapshots.length > 0"
+ class="hint"
>
There are no Notebook Snapshots currently.
</div>
diff --git a/src/plugins/notebook/components/NotebookSnapshotIndicator.vue b/src/plugins/notebook/components/NotebookSnapshotIndicator.vue
index ee07777ac..2dc164b31 100644
--- a/src/plugins/notebook/components/NotebookSnapshotIndicator.vue
+++ b/src/plugins/notebook/components/NotebookSnapshotIndicator.vue
@@ -1,11 +1,12 @@
<template>
-<div class="c-indicator c-indicator--clickable icon-camera"
- :class="[
- { 's-status-off': snapshotCount === 0 },
- { 's-status-on': snapshotCount > 0 },
- { 's-status-caution': snapshotCount === snapshotMaxCount },
- { 'has-new-snapshot': flashIndicator }
- ]"
+<div
+ class="c-indicator c-indicator--clickable icon-camera"
+ :class="[
+ { 's-status-off': snapshotCount === 0 },
+ { 's-status-on': snapshotCount > 0 },
+ { 's-status-caution': snapshotCount === snapshotMaxCount },
+ { 'has-new-snapshot': flashIndicator }
+ ]"
>
<span class="label c-indicator__label">
{{ indicatorTitle }}
diff --git a/src/plugins/notebook/components/PageCollection.vue b/src/plugins/notebook/components/PageCollection.vue
index 4e4a5d78f..3d88b51f4 100644
--- a/src/plugins/notebook/components/PageCollection.vue
+++ b/src/plugins/notebook/components/PageCollection.vue
@@ -1,17 +1,19 @@
<template>
-<ul class="c-list">
- <li v-for="page in pages"
+<ul class="c-list c-notebook__pages">
+ <li
+ v-for="page in pages"
:key="page.id"
class="c-list__item-h"
>
- <Page ref="pageComponent"
- :default-page-id="defaultPageId"
- :selected-page-id="selectedPageId"
- :page="page"
- :page-title="pageTitle"
- @deletePage="deletePage"
- @renamePage="updatePage"
- @selectPage="selectPage"
+ <Page
+ ref="pageComponent"
+ :default-page-id="defaultPageId"
+ :selected-page-id="selectedPageId"
+ :page="page"
+ :page-title="pageTitle"
+ @deletePage="deletePage"
+ @renamePage="updatePage"
+ @selectPage="selectPage"
/>
</li>
</ul>
diff --git a/src/plugins/notebook/components/PageComponent.vue b/src/plugins/notebook/components/PageComponent.vue
index ed87bc2eb..950cc142b 100644
--- a/src/plugins/notebook/components/PageComponent.vue
+++ b/src/plugins/notebook/components/PageComponent.vue
@@ -1,15 +1,33 @@
<template>
-<div class="c-list__item js-list__item"
- :class="[{ 'is-selected': isSelected, 'is-notebook-default' : (defaultPageId === page.id) }]"
- :data-id="page.id"
- @click="selectPage"
+<div
+ class="c-list__item js-list__item"
+ :class="[{
+ 'is-selected': isSelected,
+ 'is-notebook-default' : (defaultPageId === page.id),
+ 'icon-lock' : page.isLocked
+ }]"
+ :data-id="page.id"
+ @click="selectPage"
>
- <span class="c-list__item__name js-list__item__name"
- :data-id="page.id"
- @keydown.enter="updateName"
- @blur="updateName"
- >{{ page.name.length ? page.name : `Unnamed ${pageTitle}` }}</span>
- <PopupMenu :popup-menu-items="popupMenuItems" />
+ <template v-if="!page.isLocked">
+ <div
+ class="c-list__item__name js-list__item__name"
+ :data-id="page.id"
+ :contenteditable="true"
+ @keydown.enter="updateName"
+ @blur="updateName"
+ >{{ pageName }}</div>
+ <PopupMenu
+ :popup-menu-items="popupMenuItems"
+ />
+ </template>
+ <template v-else>
+ <div
+ class="c-list__item__name js-list__item__name"
+ :data-id="page.id"
+ :contenteditable="false"
+ >{{ pageName }}</div>
+ </template>
</div>
</template>
@@ -53,16 +71,13 @@ export default {
computed: {
isSelected() {
return this.selectedPageId === this.page.id;
- }
- },
- watch: {
- page(newPage) {
- this.toggleContentEditable(newPage);
+ },
+ pageName() {
+ return this.page.name.length ? this.page.name : `Unnamed ${this.pageTitle}`;
}
},
mounted() {
this.addPopupMenuItems();
- this.toggleContentEditable();
},
methods: {
addPopupMenuItems() {
@@ -93,38 +108,31 @@ export default {
},
selectPage(event) {
const target = event.target;
- const page = target.closest('.js-list__item');
- const input = page.querySelector('.js-list__item__name');
+ const id = target.dataset.id;
- if (page.className.indexOf('is-selected') > -1) {
- input.contentEditable = true;
- input.classList.add('c-input-inline');
+ if (!this.page.isLocked) {
+ const page = target.closest('.js-list__item');
+ const input = page.querySelector('.js-list__item__name');
- return;
+ if (page.className.indexOf('is-selected') > -1) {
+ input.classList.add('c-input-inline');
+
+ return;
+ }
}
- const id = target.dataset.id;
if (!id) {
return;
}
this.$emit('selectPage', id);
},
- toggleContentEditable(page = this.page) {
- const pageTitle = this.$el.querySelector('span');
- pageTitle.contentEditable = page.isSelected;
- },
updateName(event) {
const target = event.target;
const name = target.textContent.toString();
- target.contentEditable = false;
target.classList.remove('c-input-inline');
- if (this.page.name === name) {
- return;
- }
-
- if (name === '') {
+ if (name === '' || this.page.name === name) {
return;
}
diff --git a/src/plugins/notebook/components/SearchResults.vue b/src/plugins/notebook/components/SearchResults.vue
index 626bbb231..91582138b 100644
--- a/src/plugins/notebook/components/SearchResults.vue
+++ b/src/plugins/notebook/components/SearchResults.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -24,16 +24,20 @@
<div class="c-notebook__search-results">
<div class="c-notebook__search-results__header">Search Results ({{ results.length }})</div>
<div class="c-notebook__entries">
- <NotebookEntry v-for="(result, index) in results"
- :key="index"
- :domain-object="domainObject"
- :result="result"
- :entry="result.entry"
- :read-only="true"
- :selected-page="result.page"
- :selected-section="result.section"
- @changeSectionPage="changeSectionPage"
- @updateEntries="updateEntries"
+ <NotebookEntry
+ v-for="(result, index) in results"
+ :key="index"
+ :domain-object="domainObject"
+ :result="result"
+ :entry="result.entry"
+ :read-only="true"
+ :selected-page="result.page"
+ :selected-section="result.section"
+ :is-locked="result.page.isLocked"
+ @editingEntry="editingEntry"
+ @cancelEdit="cancelEdit"
+ @changeSectionPage="changeSectionPage"
+ @updateEntries="updateEntries"
/>
</div>
</div>
@@ -62,6 +66,12 @@ export default {
}
},
methods: {
+ editingEntry() {
+ this.$emit('editingEntry');
+ },
+ cancelEdit() {
+ this.$emit('cancelEdit');
+ },
changeSectionPage(data) {
this.$emit('changeSectionPage', data);
},
diff --git a/src/plugins/notebook/components/SectionCollection.vue b/src/plugins/notebook/components/SectionCollection.vue
index 860cd5698..fcc279908 100644
--- a/src/plugins/notebook/components/SectionCollection.vue
+++ b/src/plugins/notebook/components/SectionCollection.vue
@@ -1,17 +1,19 @@
<template>
-<ul class="c-list">
- <li v-for="section in sections"
+<ul class="c-list c-notebook__sections">
+ <li
+ v-for="section in sections"
:key="section.id"
class="c-list__item-h"
>
- <NotebookSection ref="sectionComponent"
- :default-section-id="defaultSectionId"
- :selected-section-id="selectedSectionId"
- :section="section"
- :section-title="sectionTitle"
- @deleteSection="deleteSection"
- @renameSection="updateSection"
- @selectSection="selectSection"
+ <NotebookSection
+ ref="sectionComponent"
+ :default-section-id="defaultSectionId"
+ :selected-section-id="selectedSectionId"
+ :section="section"
+ :section-title="sectionTitle"
+ @deleteSection="deleteSection"
+ @renameSection="updateSection"
+ @selectSection="selectSection"
/>
</li>
</ul>
diff --git a/src/plugins/notebook/components/SectionComponent.vue b/src/plugins/notebook/components/SectionComponent.vue
index d3d317aa8..85036f5f9 100644
--- a/src/plugins/notebook/components/SectionComponent.vue
+++ b/src/plugins/notebook/components/SectionComponent.vue
@@ -1,15 +1,21 @@
<template>
-<div class="c-list__item js-list__item"
- :class="[{ 'is-selected': isSelected, 'is-notebook-default' : (defaultSectionId === section.id) }]"
- :data-id="section.id"
- @click="selectSection"
+<div
+ class="c-list__item js-list__item"
+ :class="[{ 'is-selected': isSelected, 'is-notebook-default' : (defaultSectionId === section.id) }]"
+ :data-id="section.id"
+ @click="selectSection"
>
- <span class="c-list__item__name js-list__item__name"
- :data-id="section.id"
- @keydown.enter="updateName"
- @blur="updateName"
- >{{ section.name.length ? section.name : `Unnamed ${sectionTitle}` }}</span>
- <PopupMenu :popup-menu-items="popupMenuItems" />
+ <span
+ class="c-list__item__name js-list__item__name"
+ :data-id="section.id"
+ contenteditable="true"
+ @keydown.enter="updateName"
+ @blur="updateName"
+ >{{ sectionName }}</span>
+ <PopupMenu
+ v-if="!section.isLocked"
+ :popup-menu-items="popupMenuItems"
+ />
</div>
</template>
@@ -53,16 +59,13 @@ export default {
computed: {
isSelected() {
return this.selectedSectionId === this.section.id;
- }
- },
- watch: {
- section(newSection) {
- this.toggleContentEditable(newSection);
+ },
+ sectionName() {
+ return this.section.name.length ? this.section.name : `Unnamed ${this.sectionTitle}`;
}
},
mounted() {
this.addPopupMenuItems();
- this.toggleContentEditable();
},
methods: {
addPopupMenuItems() {
@@ -94,17 +97,18 @@ export default {
},
selectSection(event) {
const target = event.target;
- const section = target.closest('.js-list__item');
- const input = section.querySelector('.js-list__item__name');
+ const id = target.dataset.id;
- if (section.className.indexOf('is-selected') > -1) {
- input.contentEditable = true;
- input.classList.add('c-input-inline');
+ if (!this.section.isLocked) {
+ const section = target.closest('.js-list__item');
+ const input = section.querySelector('.js-list__item__name');
- return;
- }
+ if (section.className.indexOf('is-selected') > -1) {
+ input.classList.add('c-input-inline');
- const id = target.dataset.id;
+ return;
+ }
+ }
if (!id) {
return;
@@ -112,21 +116,12 @@ export default {
this.$emit('selectSection', id);
},
- toggleContentEditable(section = this.section) {
- const sectionTitle = this.$el.querySelector('span');
- sectionTitle.contentEditable = section.isSelected;
- },
updateName(event) {
const target = event.target;
- target.contentEditable = false;
target.classList.remove('c-input-inline');
const name = target.textContent.trim();
- if (this.section.name === name) {
- return;
- }
-
- if (name === '') {
+ if (name === '' || this.section.name === name) {
return;
}
diff --git a/src/plugins/notebook/components/Sidebar.vue b/src/plugins/notebook/components/Sidebar.vue
index 26709dc53..c7476d035 100644
--- a/src/plugins/notebook/components/Sidebar.vue
+++ b/src/plugins/notebook/components/Sidebar.vue
@@ -4,24 +4,25 @@
<div class="c-sidebar__header-w">
<div class="c-sidebar__header">
<span class="c-sidebar__header-label">{{ sectionTitle }}</span>
+ <button
+ class="c-icon-button c-icon-button--major icon-plus"
+ @click="addSection"
+ >
+ <span class="c-list-button__label">Add</span>
+ </button>
</div>
</div>
<div class="c-sidebar__contents-and-controls">
- <button class="c-list-button"
- @click="addSection"
- >
- <span class="c-button c-list-button__button icon-plus"></span>
- <span class="c-list-button__label">Add {{ sectionTitle }}</span>
- </button>
- <SectionCollection class="c-sidebar__contents"
- :default-section-id="defaultSectionId"
- :selected-section-id="selectedSectionId"
- :domain-object="domainObject"
- :sections="sections"
- :section-title="sectionTitle"
- @defaultSectionDeleted="defaultSectionDeleted"
- @updateSection="sectionsChanged"
- @selectSection="selectSection"
+ <SectionCollection
+ class="c-sidebar__contents"
+ :default-section-id="defaultSectionId"
+ :selected-section-id="selectedSectionId"
+ :domain-object="domainObject"
+ :sections="sections"
+ :section-title="sectionTitle"
+ @defaultSectionDeleted="defaultSectionDeleted"
+ @updateSection="sectionsChanged"
+ @selectSection="selectSection"
/>
</div>
</div>
@@ -29,42 +30,47 @@
<div class="c-sidebar__header-w">
<div class="c-sidebar__header">
<span class="c-sidebar__header-label">{{ pageTitle }}</span>
+
+ <button
+ class="c-icon-button c-icon-button--major icon-plus"
+ @click="addPage"
+ >
+ <span class="c-icon-button__label">Add</span>
+ </button>
</div>
- <button class="c-click-icon c-click-icon--major icon-x-in-circle"
- @click="toggleNav"
- ></button>
</div>
<div class="c-sidebar__contents-and-controls">
- <button class="c-list-button"
- @click="addPage"
- >
- <span class="c-button c-list-button__button icon-plus"></span>
- <span class="c-list-button__label">Add {{ pageTitle }}</span>
- </button>
- <PageCollection ref="pageCollection"
- class="c-sidebar__contents"
- :default-page-id="defaultPageId"
- :selected-page-id="selectedPageId"
- :domain-object="domainObject"
- :pages="pages"
- :sections="sections"
- :sidebar-covers-entries="sidebarCoversEntries"
- :page-title="pageTitle"
- @defaultPageDeleted="defaultPageDeleted"
- @toggleNav="toggleNav"
- @updatePage="pagesChanged"
- @selectPage="selectPage"
+ <PageCollection
+ ref="pageCollection"
+ class="c-sidebar__contents"
+ :default-page-id="defaultPageId"
+ :selected-page-id="selectedPageId"
+ :domain-object="domainObject"
+ :pages="pages"
+ :sections="sections"
+ :sidebar-covers-entries="sidebarCoversEntries"
+ :page-title="pageTitle"
+ @defaultPageDeleted="defaultPageDeleted"
+ @toggleNav="toggleNav"
+ @updatePage="pagesChanged"
+ @selectPage="selectPage"
/>
</div>
</div>
+ <div class="c-sidebar__right-edge">
+ <button
+ class="c-icon-button c-icon-button--major icon-line-horz"
+ @click="toggleNav"
+ ></button>
+ </div>
</div>
</template>
<script>
import SectionCollection from './SectionCollection.vue';
import PageCollection from './PageCollection.vue';
-import uuid from 'uuid';
+import { v4 as uuid } from 'uuid';
export default {
components: {
diff --git a/src/plugins/notebook/components/sidebar.scss b/src/plugins/notebook/components/sidebar.scss
index e5c8a8cd0..91e8b1699 100644
--- a/src/plugins/notebook/components/sidebar.scss
+++ b/src/plugins/notebook/components/sidebar.scss
@@ -3,19 +3,18 @@
background: $sideBarBg;
display: flex;
justify-content: stretch;
- max-width: 300px;
+ max-width: 600px;
&.c-drawer--push.is-expanded {
margin-right: $interiorMargin;
- width: 50%;
+ width: 30%;
}
&.c-drawer--overlays.is-expanded {
width: 95%;
}
- > * {
- // Hardcoded for two columns
+ &__pane {
background: $sideBarBg;
display: flex;
flex: 1 1 50%;
@@ -31,32 +30,30 @@
}
}
- &__pane {
- > * + * { margin-top: $interiorMargin; }
+ &__right-edge {
+ flex: 0 0 auto;
+ padding: $interiorMarginSm;
}
&__header-w {
- // Wraps header, used for page pane with collapse button
+ // Wraps header, used for page pane with collapse buttons
display: flex;
flex: 0 0 auto;
background: $sideBarHeaderBg;
align-items: center;
-
- .c-icon-button {
- font-size: 0.8em;
- color: $colorBodyFg;
- }
}
&__header {
color: $sideBarHeaderFg;
display: flex;
+ align-items: center;
flex: 1 1 auto;
- padding: $interiorMargin;
+ padding: $interiorMarginSm $interiorMargin;
text-transform: uppercase;
- > * {
+ &-label {
@include ellipsize();
+ flex: 1 1 auto;
}
}
@@ -66,17 +63,8 @@
flex-direction: column;
flex: 1 1 auto;
- > * {
- margin: auto $interiorMargin $interiorMargin $interiorMargin;
-
- &:first-child {
- border-bottom: 1px solid $colorInteriorBorder;
- flex: 0 0 auto;
- }
-
- + * {
- margin-top: $interiorMargin;
- }
+ > * + * {
+ margin-top: $interiorMargin;
}
}
@@ -87,12 +75,6 @@
padding: auto $interiorMargin;
}
- .c-list-button {
- .c-button {
- font-size: 0.8em;
- }
- }
-
.c-list__item {
@include hover() {
[class*="__menu-indicator"] {
diff --git a/src/plugins/notebook/monkeyPatchObjectAPIForNotebooks.js b/src/plugins/notebook/monkeyPatchObjectAPIForNotebooks.js
index 43d4ca9bf..8222b5eff 100644
--- a/src/plugins/notebook/monkeyPatchObjectAPIForNotebooks.js
+++ b/src/plugins/notebook/monkeyPatchObjectAPIForNotebooks.js
@@ -1,13 +1,14 @@
-import {NOTEBOOK_TYPE} from './notebook-constants';
+import { isNotebookType } from './notebook-constants';
export default function (openmct) {
const apiSave = openmct.objects.save.bind(openmct.objects);
openmct.objects.save = async (domainObject) => {
- if (domainObject.type !== NOTEBOOK_TYPE) {
+ if (!isNotebookType(domainObject)) {
return apiSave(domainObject);
}
+ const isNewMutable = !domainObject.isMutable;
const localMutable = openmct.objects._toMutable(domainObject);
let result;
@@ -20,7 +21,9 @@ export default function (openmct) {
result = Promise.reject(error);
}
} finally {
- openmct.objects.destroyMutable(localMutable);
+ if (isNewMutable) {
+ openmct.objects.destroyMutable(localMutable);
+ }
}
return result;
@@ -28,10 +31,10 @@ export default function (openmct) {
}
function resolveConflicts(localMutable, openmct) {
+ const localEntries = JSON.parse(JSON.stringify(localMutable.configuration.entries));
+
return openmct.objects.getMutable(localMutable.identifier).then((remoteMutable) => {
- const localEntries = localMutable.configuration.entries;
- remoteMutable.$refresh(remoteMutable);
- applyLocalEntries(remoteMutable, localEntries);
+ applyLocalEntries(remoteMutable, localEntries, openmct);
openmct.objects.destroyMutable(remoteMutable);
@@ -39,7 +42,7 @@ function resolveConflicts(localMutable, openmct) {
});
}
-function applyLocalEntries(mutable, entries) {
+function applyLocalEntries(mutable, entries, openmct) {
Object.entries(entries).forEach(([sectionKey, pagesInSection]) => {
Object.entries(pagesInSection).forEach(([pageKey, localEntries]) => {
const remoteEntries = mutable.configuration.entries[sectionKey][pageKey];
@@ -58,14 +61,15 @@ function applyLocalEntries(mutable, entries) {
locallyModifiedEntries.forEach((locallyModifiedEntry) => {
let mergedEntry = mergedEntries.find(entry => entry.id === locallyModifiedEntry.id);
- if (mergedEntry !== undefined) {
+ if (mergedEntry !== undefined
+ && locallyModifiedEntry.text.match(/\S/)) {
mergedEntry.text = locallyModifiedEntry.text;
shouldMutate = true;
}
});
if (shouldMutate) {
- mutable.$set(`configuration.entries.${sectionKey}.${pageKey}`, mergedEntries);
+ openmct.objects.mutate(mutable, `configuration.entries.${sectionKey}.${pageKey}`, mergedEntries);
}
});
});
diff --git a/src/plugins/notebook/notebook-constants.js b/src/plugins/notebook/notebook-constants.js
index d5163f797..6f2e5af3e 100644
--- a/src/plugins/notebook/notebook-constants.js
+++ b/src/plugins/notebook/notebook-constants.js
@@ -1,5 +1,18 @@
export const NOTEBOOK_TYPE = 'notebook';
+export const RESTRICTED_NOTEBOOK_TYPE = 'restricted-notebook';
export const EVENT_SNAPSHOTS_UPDATED = 'SNAPSHOTS_UPDATED';
export const NOTEBOOK_DEFAULT = 'DEFAULT';
export const NOTEBOOK_SNAPSHOT = 'SNAPSHOT';
export const NOTEBOOK_VIEW_TYPE = 'notebook-vue';
+export const RESTRICTED_NOTEBOOK_VIEW_TYPE = 'restricted-notebook-vue';
+export const NOTEBOOK_INSTALLED_KEY = '_NOTEBOOK_PLUGIN_INSTALLED';
+export const RESTRICTED_NOTEBOOK_INSTALLED_KEY = '_RESTRICTED_NOTEBOOK_PLUGIN_INSTALLED';
+
+// these only deals with constants, figured this could skip going into a utils file
+export function isNotebookType(domainObject) {
+ return [NOTEBOOK_TYPE, RESTRICTED_NOTEBOOK_TYPE].includes(domainObject.type);
+}
+
+export function isNotebookViewType(view) {
+ return [NOTEBOOK_VIEW_TYPE, RESTRICTED_NOTEBOOK_VIEW_TYPE].includes(view);
+}
diff --git a/src/plugins/notebook/plugin.js b/src/plugins/notebook/plugin.js
index 033b04f8f..7fcabbe74 100644
--- a/src/plugins/notebook/plugin.js
+++ b/src/plugins/notebook/plugin.js
@@ -1,176 +1,155 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
import CopyToNotebookAction from './actions/CopyToNotebookAction';
-import Notebook from './components/Notebook.vue';
import NotebookSnapshotIndicator from './components/NotebookSnapshotIndicator.vue';
+import NotebookViewProvider from './NotebookViewProvider';
+import NotebookType from './NotebookType';
import SnapshotContainer from './snapshot-container';
import monkeyPatchObjectAPIForNotebooks from './monkeyPatchObjectAPIForNotebooks.js';
-import { notebookImageMigration, IMAGE_MIGRATION_VER } from '../notebook/utils/notebook-migration';
-import { NOTEBOOK_TYPE } from './notebook-constants';
+import { notebookImageMigration } from '../notebook/utils/notebook-migration';
+import {
+ NOTEBOOK_TYPE,
+ RESTRICTED_NOTEBOOK_TYPE,
+ NOTEBOOK_VIEW_TYPE,
+ RESTRICTED_NOTEBOOK_VIEW_TYPE,
+ NOTEBOOK_INSTALLED_KEY,
+ RESTRICTED_NOTEBOOK_INSTALLED_KEY
+} from './notebook-constants';
import Vue from 'vue';
-export default function NotebookPlugin() {
+let notebookSnapshotContainer;
+function getSnapshotContainer(openmct) {
+ if (!notebookSnapshotContainer) {
+ notebookSnapshotContainer = new SnapshotContainer(openmct);
+ }
+
+ return notebookSnapshotContainer;
+}
+
+function addLegacyNotebookGetInterceptor(openmct) {
+ openmct.objects.addGetInterceptor({
+ appliesTo: (identifier, domainObject) => {
+ return domainObject && domainObject.type === NOTEBOOK_TYPE;
+ },
+ invoke: (identifier, domainObject) => {
+ notebookImageMigration(openmct, domainObject);
+
+ return domainObject;
+ }
+ });
+}
+
+function installBaseNotebookFunctionality(openmct) {
+ // only need to do this once
+ if (openmct[NOTEBOOK_INSTALLED_KEY] || openmct[RESTRICTED_NOTEBOOK_INSTALLED_KEY]) {
+ return;
+ }
+
+ const snapshotContainer = getSnapshotContainer(openmct);
+ const notebookSnapshotImageType = {
+ name: 'Notebook Snapshot Image Storage',
+ description: 'Notebook Snapshot Image Storage object',
+ creatable: false,
+ initialize: domainObject => {
+ domainObject.configuration = {
+ fullSizeImageURL: undefined,
+ thumbnailImageURL: undefined
+ };
+ }
+ };
+ openmct.types.addType('notebookSnapshotImage', notebookSnapshotImageType);
+ openmct.actions.register(new CopyToNotebookAction(openmct));
+
+ const notebookSnapshotIndicator = new Vue ({
+ components: {
+ NotebookSnapshotIndicator
+ },
+ provide: {
+ openmct,
+ snapshotContainer
+ },
+ template: '<NotebookSnapshotIndicator></NotebookSnapshotIndicator>'
+ });
+ const indicator = {
+ element: notebookSnapshotIndicator.$mount().$el,
+ key: 'notebook-snapshot-indicator',
+ priority: openmct.priority.DEFAULT
+ };
+
+ openmct.indicators.add(indicator);
+
+ monkeyPatchObjectAPIForNotebooks(openmct);
+}
+
+function NotebookPlugin(name = 'Notebook') {
return function install(openmct) {
- if (openmct._NOTEBOOK_PLUGIN_INSTALLED) {
+ if (openmct[NOTEBOOK_INSTALLED_KEY]) {
return;
- } else {
- openmct._NOTEBOOK_PLUGIN_INSTALLED = true;
}
- openmct.actions.register(new CopyToNotebookAction(openmct));
-
- const notebookType = {
- name: 'Notebook',
- description: 'Create and save timestamped notes with embedded object snapshots.',
- creatable: true,
- cssClass: 'icon-notebook',
- initialize: domainObject => {
- domainObject.configuration = {
- defaultSort: 'oldest',
- entries: {},
- imageMigrationVer: IMAGE_MIGRATION_VER,
- pageTitle: 'Page',
- sections: [],
- sectionTitle: 'Section',
- type: 'General'
- };
- },
- form: [
- {
- key: 'defaultSort',
- name: 'Entry Sorting',
- control: 'select',
- options: [
- {
- name: 'Newest First',
- value: "newest"
- },
- {
- name: 'Oldest First',
- value: "oldest"
- }
- ],
- cssClass: 'l-inline',
- property: [
- "configuration",
- "defaultSort"
- ]
- },
- {
- key: 'type',
- name: 'Note book Type',
- control: 'textfield',
- cssClass: 'l-inline',
- property: [
- "configuration",
- "type"
- ]
- },
- {
- key: 'sectionTitle',
- name: 'Section Title',
- control: 'textfield',
- cssClass: 'l-inline',
- required: true,
- property: [
- "configuration",
- "sectionTitle"
- ]
- },
- {
- key: 'pageTitle',
- name: 'Page Title',
- control: 'textfield',
- cssClass: 'l-inline',
- required: true,
- property: [
- "configuration",
- "pageTitle"
- ]
- }
- ]
- };
+ const icon = 'icon-notebook';
+ const description = 'Create and save timestamped notes with embedded object snapshots.';
+ const snapshotContainer = getSnapshotContainer(openmct);
+
+ addLegacyNotebookGetInterceptor(openmct);
+
+ const notebookType = new NotebookType(name, description, icon);
openmct.types.addType(NOTEBOOK_TYPE, notebookType);
- const notebookSnapshotImageType = {
- name: 'Notebook Snapshot Image Storage',
- description: 'Notebook Snapshot Image Storage object',
- creatable: false,
- initialize: domainObject => {
- domainObject.configuration = {
- fullSizeImageURL: undefined,
- thumbnailImageURL: undefined
- };
- }
- };
- openmct.types.addType('notebookSnapshotImage', notebookSnapshotImageType);
-
- const snapshotContainer = new SnapshotContainer(openmct);
- const notebookSnapshotIndicator = new Vue ({
- components: {
- NotebookSnapshotIndicator
- },
- provide: {
- openmct,
- snapshotContainer
- },
- template: '<NotebookSnapshotIndicator></NotebookSnapshotIndicator>'
- });
- const indicator = {
- element: notebookSnapshotIndicator.$mount().$el,
- key: 'notebook-snapshot-indicator',
- priority: openmct.priority.DEFAULT
- };
-
- openmct.indicators.add(indicator);
-
- openmct.objectViews.addProvider({
- key: 'notebook-vue',
- name: 'Notebook View',
- cssClass: 'icon-notebook',
- canView: function (domainObject) {
- return domainObject.type === 'notebook';
- },
- view: function (domainObject) {
- let component;
-
- return {
- show(container) {
- component = new Vue({
- el: container,
- components: {
- Notebook
- },
- provide: {
- openmct,
- snapshotContainer
- },
- data() {
- return {
- domainObject
- };
- },
- template: '<Notebook :domain-object="domainObject"></Notebook>'
- });
- },
- destroy() {
- component.$destroy();
- }
- };
- }
- });
-
- openmct.objects.addGetInterceptor({
- appliesTo: (identifier, domainObject) => {
- return domainObject && domainObject.type === 'notebook';
- },
- invoke: (identifier, domainObject) => {
- notebookImageMigration(openmct, domainObject);
-
- return domainObject;
- }
- });
-
- monkeyPatchObjectAPIForNotebooks(openmct);
+ const notebookView = new NotebookViewProvider(openmct, name, NOTEBOOK_VIEW_TYPE, NOTEBOOK_TYPE, icon, snapshotContainer);
+ openmct.objectViews.addProvider(notebookView);
+
+ installBaseNotebookFunctionality(openmct);
+
+ openmct[NOTEBOOK_INSTALLED_KEY] = true;
+ };
+}
+
+function RestrictedNotebookPlugin(name = 'Notebook Shift Log') {
+ return function install(openmct) {
+ if (openmct[RESTRICTED_NOTEBOOK_INSTALLED_KEY]) {
+ return;
+ }
+
+ const icon = 'icon-notebook-shift-log';
+ const description = 'Create and save timestamped notes with embedded object snapshots with the ability to commit and lock pages.';
+ const snapshotContainer = getSnapshotContainer(openmct);
+
+ const notebookType = new NotebookType(name, description, icon);
+ openmct.types.addType(RESTRICTED_NOTEBOOK_TYPE, notebookType);
+
+ const notebookView = new NotebookViewProvider(openmct, name, RESTRICTED_NOTEBOOK_VIEW_TYPE, RESTRICTED_NOTEBOOK_TYPE, icon, snapshotContainer);
+ openmct.objectViews.addProvider(notebookView);
+
+ installBaseNotebookFunctionality(openmct);
+
+ openmct[RESTRICTED_NOTEBOOK_INSTALLED_KEY] = true;
};
}
+
+export {
+ NotebookPlugin,
+ RestrictedNotebookPlugin
+};
diff --git a/src/plugins/notebook/pluginSpec.js b/src/plugins/notebook/pluginSpec.js
index 94b6eedfd..ba83a4f4b 100644
--- a/src/plugins/notebook/pluginSpec.js
+++ b/src/plugins/notebook/pluginSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -21,7 +21,7 @@
*****************************************************************************/
import { createOpenMct, createMouseEvent, resetApplicationState } from 'utils/testing';
-import notebookPlugin from './plugin';
+import { NotebookPlugin } from './plugin';
import Vue from 'vue';
describe("Notebook plugin:", () => {
@@ -33,6 +33,7 @@ describe("Notebook plugin:", () => {
let objectProviderObserver;
let notebookDomainObject;
+ let originalAnnotations;
beforeEach((done) => {
notebookDomainObject = {
@@ -54,7 +55,12 @@ describe("Notebook plugin:", () => {
child = document.createElement('div');
element.appendChild(child);
- openmct.install(notebookPlugin());
+ openmct.install(NotebookPlugin());
+ originalAnnotations = openmct.annotation.getNotebookAnnotation;
+ // eslint-disable-next-line require-await
+ openmct.annotation.getNotebookAnnotation = async function () {
+ return null;
+ };
notebookDefinition = openmct.types.get('notebook').definition;
notebookDefinition.initialize(notebookDomainObject);
@@ -65,6 +71,7 @@ describe("Notebook plugin:", () => {
afterEach(() => {
appHolder.remove();
+ openmct.annotation.getNotebookAnnotation = originalAnnotations;
return resetApplicationState(openmct);
});
@@ -83,7 +90,7 @@ describe("Notebook plugin:", () => {
let notebookViewObject;
let mutableNotebookObject;
- beforeEach(() => {
+ beforeEach(async () => {
notebookViewObject = {
...notebookDomainObject,
id: "test-object",
@@ -161,16 +168,14 @@ describe("Notebook plugin:", () => {
testObjectProvider.create.and.returnValue(Promise.resolve(true));
testObjectProvider.update.and.returnValue(Promise.resolve(true));
- return openmct.objects.getMutable(notebookViewObject.identifier).then((mutableObject) => {
- mutableNotebookObject = mutableObject;
- objectProviderObserver = testObjectProvider.observe.calls.mostRecent().args[1];
+ const mutableObject = await openmct.objects.getMutable(notebookViewObject.identifier);
+ mutableNotebookObject = mutableObject;
+ objectProviderObserver = testObjectProvider.observe.calls.mostRecent().args[1];
- notebookView = notebookViewProvider.view(mutableNotebookObject);
- notebookView.show(child);
-
- return Vue.nextTick();
- });
+ notebookView = notebookViewProvider.view(mutableNotebookObject);
+ notebookView.show(child);
+ await Vue.nextTick();
});
afterEach(() => {
diff --git a/src/plugins/notebook/snapshot.js b/src/plugins/notebook/snapshot.js
index b3762da79..c7a726acc 100644
--- a/src/plugins/notebook/snapshot.js
+++ b/src/plugins/notebook/snapshot.js
@@ -41,16 +41,17 @@ export default class Snapshot {
fullSizeImageObjectIdentifier: object.identifier,
thumbnailImage
};
- const embed = createNewEmbed(snapshotMeta, snapshot);
- if (notebookType === NOTEBOOK_DEFAULT) {
- const notebookStorage = getDefaultNotebook();
-
- this._saveToDefaultNoteBook(notebookStorage, embed);
- const notebookImageDomainObject = updateNamespaceOfDomainObject(object, notebookStorage.identifier.namespace);
- saveNotebookImageDomainObject(this.openmct, notebookImageDomainObject);
- } else {
- this._saveToNotebookSnapshots(object, embed);
- }
+ createNewEmbed(snapshotMeta, snapshot).then(embed => {
+ if (notebookType === NOTEBOOK_DEFAULT) {
+ const notebookStorage = getDefaultNotebook();
+
+ this._saveToDefaultNoteBook(notebookStorage, embed);
+ const notebookImageDomainObject = updateNamespaceOfDomainObject(object, notebookStorage.identifier.namespace);
+ saveNotebookImageDomainObject(this.openmct, notebookImageDomainObject);
+ } else {
+ this._saveToNotebookSnapshots(object, embed);
+ }
+ });
}
/**
@@ -58,26 +59,26 @@ export default class Snapshot {
*/
_saveToDefaultNoteBook(notebookStorage, embed) {
this.openmct.objects.get(notebookStorage.identifier)
- .then(async (domainObject) => {
- addNotebookEntry(this.openmct, domainObject, notebookStorage, embed);
-
- let link = notebookStorage.link;
-
- // Backwards compatibility fix (old notebook model without link)
- if (!link) {
- link = await getDefaultNotebookLink(this.openmct, domainObject);
- notebookStorage.link = link;
- setDefaultNotebook(this.openmct, notebookStorage);
- }
-
- const { section, page } = getNotebookSectionAndPage(domainObject, notebookStorage.defaultSectionId, notebookStorage.defaultPageId);
- if (!section || !page) {
- return;
- }
-
- const defaultPath = `${domainObject.name} - ${section.name} - ${page.name}`;
- const msg = `Saved to Notebook ${defaultPath}`;
- this._showNotification(msg, link);
+ .then((domainObject) => {
+ return addNotebookEntry(this.openmct, domainObject, notebookStorage, embed).then(async () => {
+ let link = notebookStorage.link;
+
+ // Backwards compatibility fix (old notebook model without link)
+ if (!link) {
+ link = await getDefaultNotebookLink(this.openmct, domainObject);
+ notebookStorage.link = link;
+ setDefaultNotebook(this.openmct, notebookStorage);
+ }
+
+ const { section, page } = getNotebookSectionAndPage(domainObject, notebookStorage.defaultSectionId, notebookStorage.defaultPageId);
+ if (!section || !page) {
+ return;
+ }
+
+ const defaultPath = `${domainObject.name} - ${section.name} - ${page.name}`;
+ const msg = `Saved to Notebook ${defaultPath}`;
+ this._showNotification(msg, link);
+ });
});
}
@@ -110,8 +111,7 @@ export default class Snapshot {
}
return () => {
- const path = window.location.href.split('#');
- window.location.href = path[0] + url;
+ location.hash = url;
};
}
}
diff --git a/src/plugins/notebook/utils/notebook-entries.js b/src/plugins/notebook/utils/notebook-entries.js
index 4ce990141..42995c013 100644
--- a/src/plugins/notebook/utils/notebook-entries.js
+++ b/src/plugins/notebook/utils/notebook-entries.js
@@ -1,4 +1,17 @@
import objectLink from '../../../ui/mixins/object-link';
+import { v4 as uuid } from 'uuid';
+
+async function getUsername(openmct) {
+ let username = '';
+
+ if (openmct.user.hasProvider()) {
+ const user = await openmct.user.getCurrentUser();
+ username = user.getName();
+ }
+
+ return username;
+
+}
export const DEFAULT_CLASS = 'notebook-default';
const TIME_BOUNDS = {
@@ -61,7 +74,7 @@ export function getHistoricLinkInFixedMode(openmct, bounds, historicLink) {
return params.join('&');
}
-export function createNewEmbed(snapshotMeta, snapshot = '') {
+export async function createNewEmbed(snapshotMeta, snapshot = '') {
const {
bounds,
link,
@@ -83,10 +96,12 @@ export function createNewEmbed(snapshotMeta, snapshot = '') {
});
const name = domainObject.name;
const type = domainObject.identifier.key;
+ const createdBy = await getUsername(openmct);
return {
bounds,
createdOn: date,
+ createdBy,
cssClass,
domainObject,
historicLink,
@@ -97,7 +112,7 @@ export function createNewEmbed(snapshotMeta, snapshot = '') {
};
}
-export function addNotebookEntry(openmct, domainObject, notebookStorage, embed = null, entryText = '') {
+export async function addNotebookEntry(openmct, domainObject, notebookStorage, embed = null, entryText = '') {
if (!openmct || !domainObject || !notebookStorage) {
return;
}
@@ -109,10 +124,12 @@ export function addNotebookEntry(openmct, domainObject, notebookStorage, embed =
? [embed]
: [];
- const id = `entry-${date}`;
+ const id = `entry-${uuid()}`;
+ const createdBy = await getUsername(openmct);
const entry = {
id,
createdOn: date,
+ createdBy,
text: entryText,
embeds
};
@@ -120,14 +137,13 @@ export function addNotebookEntry(openmct, domainObject, notebookStorage, embed =
const newEntries = addEntryIntoPage(notebookStorage, entries, entry);
addDefaultClass(domainObject, openmct);
-
mutateObject(openmct, domainObject, 'configuration.entries', newEntries);
return id;
}
export function getNotebookEntries(domainObject, selectedSection, selectedPage) {
- if (!domainObject || !selectedSection || !selectedPage) {
+ if (!domainObject || !selectedSection || !selectedPage || !domainObject.configuration) {
return;
}
@@ -144,7 +160,9 @@ export function getNotebookEntries(domainObject, selectedSection, selectedPage)
return;
}
- return entries[selectedSection.id][selectedPage.id];
+ const specificEntries = entries[selectedSection.id][selectedPage.id];
+
+ return specificEntries;
}
export function getEntryPosById(entryId, domainObject, selectedSection, selectedPage) {
diff --git a/src/plugins/notebook/utils/notebook-entriesSpec.js b/src/plugins/notebook/utils/notebook-entriesSpec.js
index 3bac23fb6..c5cd8241a 100644
--- a/src/plugins/notebook/utils/notebook-entriesSpec.js
+++ b/src/plugins/notebook/utils/notebook-entriesSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -127,7 +127,7 @@ describe('Notebook Entries:', () => {
expect(entries.length).toEqual(0);
});
- it('addNotebookEntry adds entry', () => {
+ it('addNotebookEntry adds entry', async () => {
const unlisten = openmct.objects.observe(notebookDomainObject, '*', (object) => {
const entries = NotebookEntries.getNotebookEntries(notebookDomainObject, selectedSection, selectedPage);
@@ -135,17 +135,38 @@ describe('Notebook Entries:', () => {
unlisten();
});
- NotebookEntries.addNotebookEntry(openmct, notebookDomainObject, notebookStorage);
+ await NotebookEntries.addNotebookEntry(openmct, notebookDomainObject, notebookStorage);
});
- it('getEntryPosById returns valid position', () => {
- const entryId1 = NotebookEntries.addNotebookEntry(openmct, notebookDomainObject, notebookStorage);
+ it('addNotebookEntry adds active user to entry', async () => {
+ const USER = 'Timmy';
+ openmct.user.hasProvider = () => true;
+ openmct.user.getCurrentUser = () => {
+ return Promise.resolve({
+ getName: () => {
+ return USER;
+ }
+ });
+ };
+
+ const unlisten = openmct.objects.observe(notebookDomainObject, '*', (object) => {
+ const entries = NotebookEntries.getNotebookEntries(notebookDomainObject, selectedSection, selectedPage);
+
+ expect(entries[0].createdBy).toEqual(USER);
+ unlisten();
+ });
+
+ await NotebookEntries.addNotebookEntry(openmct, notebookDomainObject, notebookStorage);
+ });
+
+ it('getEntryPosById returns valid position', async () => {
+ const entryId1 = await NotebookEntries.addNotebookEntry(openmct, notebookDomainObject, notebookStorage);
const position1 = NotebookEntries.getEntryPosById(entryId1, notebookDomainObject, selectedSection, selectedPage);
- const entryId2 = NotebookEntries.addNotebookEntry(openmct, notebookDomainObject, notebookStorage);
+ const entryId2 = await NotebookEntries.addNotebookEntry(openmct, notebookDomainObject, notebookStorage);
const position2 = NotebookEntries.getEntryPosById(entryId2, notebookDomainObject, selectedSection, selectedPage);
- const entryId3 = NotebookEntries.addNotebookEntry(openmct, notebookDomainObject, notebookStorage);
+ const entryId3 = await NotebookEntries.addNotebookEntry(openmct, notebookDomainObject, notebookStorage);
const position3 = NotebookEntries.getEntryPosById(entryId3, notebookDomainObject, selectedSection, selectedPage);
const success = position1 === 0
@@ -155,9 +176,9 @@ describe('Notebook Entries:', () => {
expect(success).toBe(true);
});
- it('deleteNotebookEntries deletes correct page entries', () => {
- NotebookEntries.addNotebookEntry(openmct, notebookDomainObject, notebookStorage);
- NotebookEntries.addNotebookEntry(openmct, notebookDomainObject, notebookStorage);
+ it('deleteNotebookEntries deletes correct page entries', async () => {
+ await NotebookEntries.addNotebookEntry(openmct, notebookDomainObject, notebookStorage);
+ await NotebookEntries.addNotebookEntry(openmct, notebookDomainObject, notebookStorage);
NotebookEntries.deleteNotebookEntries(openmct, notebookDomainObject, selectedSection, selectedPage);
const afterEntries = NotebookEntries.getNotebookEntries(notebookDomainObject, selectedSection, selectedPage);
diff --git a/src/plugins/notebook/utils/notebook-image.js b/src/plugins/notebook/utils/notebook-image.js
index cde3ae6d7..0f7e4ab30 100644
--- a/src/plugins/notebook/utils/notebook-image.js
+++ b/src/plugins/notebook/utils/notebook-image.js
@@ -1,4 +1,4 @@
-import uuid from 'uuid';
+import { v4 as uuid } from 'uuid';
export const DEFAULT_SIZE = {
width: 30,
diff --git a/src/plugins/notebook/utils/notebook-storageSpec.js b/src/plugins/notebook/utils/notebook-storageSpec.js
index 77659a9f5..c48a0f751 100644
--- a/src/plugins/notebook/utils/notebook-storageSpec.js
+++ b/src/plugins/notebook/utils/notebook-storageSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/notificationIndicator/plugin.js b/src/plugins/notificationIndicator/plugin.js
index 629fcc434..5035ee8f3 100644
--- a/src/plugins/notificationIndicator/plugin.js
+++ b/src/plugins/notificationIndicator/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/notificationIndicator/pluginSpec.js b/src/plugins/notificationIndicator/pluginSpec.js
index 69b9ad074..e4a6f4717 100644
--- a/src/plugins/notificationIndicator/pluginSpec.js
+++ b/src/plugins/notificationIndicator/pluginSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/objectMigration/Migrations.js b/src/plugins/objectMigration/Migrations.js
index ccb349bd9..493845e1f 100644
--- a/src/plugins/objectMigration/Migrations.js
+++ b/src/plugins/objectMigration/Migrations.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -23,7 +23,7 @@
define([
'uuid'
], function (
- uuid
+ { v4: uuid }
) {
return function Migrations(openmct) {
function getColumnNameKeyMap(domainObject) {
@@ -145,7 +145,7 @@ define([
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();
+ 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;
diff --git a/src/plugins/objectMigration/plugin.js b/src/plugins/objectMigration/plugin.js
index f481f7a9e..715d70418 100644
--- a/src/plugins/objectMigration/plugin.js
+++ b/src/plugins/objectMigration/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/openInNewTabAction/openInNewTabAction.js b/src/plugins/openInNewTabAction/openInNewTabAction.js
index d5453688f..7d245943d 100644
--- a/src/plugins/openInNewTabAction/openInNewTabAction.js
+++ b/src/plugins/openInNewTabAction/openInNewTabAction.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/openInNewTabAction/plugin.js b/src/plugins/openInNewTabAction/plugin.js
index 5435f8288..355206bd6 100644
--- a/src/plugins/openInNewTabAction/plugin.js
+++ b/src/plugins/openInNewTabAction/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/openInNewTabAction/pluginSpec.js b/src/plugins/openInNewTabAction/pluginSpec.js
index c1144857f..8155d1ed7 100644
--- a/src/plugins/openInNewTabAction/pluginSpec.js
+++ b/src/plugins/openInNewTabAction/pluginSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/operatorStatus/AbstractStatusIndicator.js b/src/plugins/operatorStatus/AbstractStatusIndicator.js
new file mode 100644
index 000000000..7d2a01293
--- /dev/null
+++ b/src/plugins/operatorStatus/AbstractStatusIndicator.js
@@ -0,0 +1,106 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+import raf from '@/utils/raf';
+
+export default class AbstractStatusIndicator {
+ #popupComponent;
+ #indicator;
+ #configuration;
+
+ /**
+ * @param {*} openmct the Open MCT API (proper jsdoc to come)
+ * @param {import('@/api/user/UserAPI').UserAPIConfiguration} configuration Per-deployment status styling. See the type definition in UserAPI
+ */
+ constructor(openmct, configuration) {
+ this.openmct = openmct;
+ this.#configuration = configuration;
+
+ this.showPopup = this.showPopup.bind(this);
+ this.clearPopup = this.clearPopup.bind(this);
+ this.positionBox = this.positionBox.bind(this);
+ this.positionBox = raf(this.positionBox);
+
+ this.#indicator = this.createIndicator();
+ this.#popupComponent = this.createPopupComponent();
+ }
+
+ install() {
+ this.openmct.indicators.add(this.#indicator);
+ }
+
+ showPopup() {
+ const popupElement = this.getPopupElement();
+
+ document.body.appendChild(popupElement.$el);
+ //Use capture so we don't trigger immediately on the same iteration of the event loop
+ document.addEventListener('click', this.clearPopup, {
+ capture: true
+ });
+
+ this.positionBox();
+
+ window.addEventListener('resize', this.positionBox);
+ }
+
+ positionBox() {
+ const popupElement = this.getPopupElement();
+ const indicator = this.getIndicator();
+
+ let indicatorBox = indicator.element.getBoundingClientRect();
+ popupElement.positionX = indicatorBox.left;
+ popupElement.positionY = indicatorBox.bottom;
+
+ const popupRight = popupElement.positionX + popupElement.$el.clientWidth;
+ const offsetLeft = Math.min(window.innerWidth - popupRight, 0);
+ popupElement.positionX = popupElement.positionX + offsetLeft;
+ }
+
+ clearPopup(clickAwayEvent) {
+ const popupElement = this.getPopupElement();
+
+ if (!popupElement.$el.contains(clickAwayEvent.target)) {
+ popupElement.$el.remove();
+ document.removeEventListener('click', this.clearPopup);
+ window.removeEventListener('resize', this.positionBox);
+ }
+ }
+
+ createPopupComponent() {
+ throw new Error('Must override createPopupElement method');
+ }
+
+ getPopupElement() {
+ return this.#popupComponent;
+ }
+
+ createIndicator() {
+ throw new Error('Must override createIndicator method');
+ }
+
+ getIndicator() {
+ return this.#indicator;
+ }
+
+ getConfiguration() {
+ return this.#configuration;
+ }
+}
diff --git a/src/plugins/operatorStatus/operator-status.scss b/src/plugins/operatorStatus/operator-status.scss
new file mode 100644
index 000000000..9482c650f
--- /dev/null
+++ b/src/plugins/operatorStatus/operator-status.scss
@@ -0,0 +1,142 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+ $statusCountWidth: 30px;
+
+.c-status-poll-panel {
+ @include menuOuter();
+ display: flex;
+ flex-direction: column;
+ padding: $interiorMarginLg;
+ min-width: 350px;
+ max-width: 35%;
+
+ > * + * {
+ margin-top: $interiorMarginLg;
+ }
+
+ *:before {
+ font-size: 0.8em;
+ margin-right: $interiorMarginSm;
+ }
+
+ &__section {
+ display: flex;
+ align-items: center;
+ flex-direction: row;
+
+ > * + * {
+ margin-left: $interiorMarginLg;
+ }
+ }
+
+ &__top {
+ text-transform: uppercase;
+ }
+
+ &__user-role,
+ &__updated {
+ opacity: 50%;
+ }
+
+ &__updated {
+ flex: 1 1 auto;
+ text-align: right;
+ }
+
+ &__poll-question {
+ background: $colorBodyFg;
+ color: $colorBodyBg;
+ border-radius: $controlCr;
+ font-weight: bold;
+ padding: $interiorMarginSm $interiorMargin;
+
+ .c-status-poll-panel--admin & {
+ background: rgba($colorBodyFg, 0.1);
+ color: $colorBodyFg;
+ }
+ }
+
+ /****** Admin interface */
+ &__content {
+ $m: $interiorMargin;
+ display: grid;
+ grid-template-columns: max-content 1fr;
+ grid-column-gap: $m;
+ grid-row-gap: $m;
+
+ [class*='__label'] {
+ padding: 3px 0;
+ }
+
+ [class*='new-question'] {
+ align-items: center;
+ display: flex;
+ flex-direction: row;
+ > * + * { margin-left: $interiorMargin; }
+
+ input {
+ flex: 1 1 auto;
+ height: $btnStdH;
+ }
+
+ button { flex: 0 0 auto; }
+ }
+ }
+}
+
+.c-status-poll-report {
+ display: flex;
+ flex-direction: row;
+ > * + * { margin-left: $interiorMargin; }
+
+ &__count {
+ background: rgba($colorBodyFg, 0.2);
+ border-radius: $controlCr;
+ display: flex;
+ flex-direction: row;
+ font-size: 1.25em;
+ align-items: center;
+ padding: $interiorMarginSm $interiorMarginLg;
+
+ &-type {
+ line-height: 1em;
+ opacity: 0.6;
+ }
+ }
+}
+
+.c-indicator {
+ &:before {
+ // Indicator icon
+ color: $colorKey;
+ }
+
+ &--operator-status {
+ cursor: pointer;
+ max-width: 150px;
+
+ @include hover() {
+ background: $colorIndicatorBgHov;
+ }
+ }
+}
diff --git a/src/plugins/operatorStatus/operatorStatus/OperatorStatus.vue b/src/plugins/operatorStatus/operatorStatus/OperatorStatus.vue
new file mode 100644
index 000000000..9b7d411a1
--- /dev/null
+++ b/src/plugins/operatorStatus/operatorStatus/OperatorStatus.vue
@@ -0,0 +1,188 @@
+<!--
+ Open MCT, Copyright (c) 2014-2022, United States Government
+ as represented by the Administrator of the National Aeronautics and Space
+ Administration. All rights reserved.
+
+ Open MCT is licensed under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0.
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ License for the specific language governing permissions and limitations
+ under the License.
+
+ Open MCT includes source code licensed under additional open source
+ licenses. See the Open Source Licenses file (LICENSES.md) included with
+ this source code distribution or the Licensing information page available
+ at runtime from the About dialog for additional information.
+-->
+<template>
+<div
+ :style="position"
+ class="c-status-poll-panel c-status-poll-panel--operator"
+ @click.stop="noop"
+>
+ <div class="c-status-poll-panel__section c-status-poll-panel__top">
+ <div
+ class="c-status-poll-panel__title"
+ >Status Poll</div>
+ <div class="c-status-poll-panel__user-role icon-person">{{ role }}</div>
+ <div class="c-status-poll-panel__updated">{{ pollQuestionUpdated }}</div>
+ </div>
+
+ <div class="c-status-poll-panel__section c-status-poll-panel__poll-question">
+ {{ currentPollQuestion }}
+ </div>
+
+ <div class="c-status-poll-panel__section c-status-poll-panel__bottom">
+ <div class="c-status-poll-panel__set-status-label">My status:</div>
+ <select
+ v-model="selectedStatus"
+ name="setStatus"
+ @change="changeStatus"
+ >
+ <option
+ v-for="status in allStatuses"
+ :key="status.key"
+ :value="status.key"
+ >
+ {{ status.label }}
+ </option>
+ </select>
+ </div>
+</div>
+</template>
+
+<script>
+const DEFAULT_POLL_QUESTION = 'NO POLL QUESTION';
+
+export default {
+ inject: ['openmct', 'indicator', 'configuration'],
+ props: {
+ positionX: {
+ type: Number,
+ required: true
+ },
+ positionY: {
+ type: Number,
+ required: true
+ }
+ },
+ data() {
+ return {
+ allRoles: [],
+ role: '--',
+ pollQuestionUpdated: '--',
+ currentPollQuestion: DEFAULT_POLL_QUESTION,
+ selectedStatus: undefined,
+ allStatuses: []
+ };
+ },
+ computed: {
+ position() {
+ return {
+ position: 'absolute',
+ left: `${this.positionX}px`,
+ top: `${this.positionY}px`
+ };
+ }
+ },
+ beforeDestroy() {
+ this.openmct.user.status.off('statusChange', this.setStatus);
+ this.openmct.user.status.off('pollQuestionChange', this.setPollQuestion);
+ },
+ async mounted() {
+ this.unsubscribe = [];
+ await this.fetchUser();
+ await this.findFirstApplicableRole();
+ this.fetchPossibleStatusesForUser();
+ this.fetchCurrentPoll();
+ this.fetchMyStatus();
+ this.subscribeToMyStatus();
+ this.subscribeToPollQuestion();
+ },
+ methods: {
+ async findFirstApplicableRole() {
+ this.role = await this.openmct.user.status.getStatusRoleForCurrentUser();
+ },
+ async fetchUser() {
+ this.user = await this.openmct.user.getCurrentUser();
+ },
+ async fetchCurrentPoll() {
+ const pollQuestion = await this.openmct.user.status.getPollQuestion();
+ if (pollQuestion !== undefined) {
+ this.setPollQuestion(pollQuestion);
+ }
+ },
+ async fetchPossibleStatusesForUser() {
+ this.allStatuses = await this.openmct.user.status.getPossibleStatuses();
+ },
+ setPollQuestion(pollQuestion) {
+ this.currentPollQuestion = pollQuestion.question;
+ this.pollQuestionUpdated = new Date(pollQuestion.timestamp).toISOString();
+
+ this.indicator.text(pollQuestion?.question || '');
+ },
+ async fetchMyStatus() {
+ const activeStatusRole = await this.openmct.user.status.getStatusRoleForCurrentUser();
+ const status = await this.openmct.user.status.getStatusForRole(activeStatusRole);
+
+ if (status !== undefined) {
+ this.setStatus({status});
+ }
+ },
+ subscribeToMyStatus() {
+ this.openmct.user.status.on('statusChange', this.setStatus);
+ },
+ subscribeToPollQuestion() {
+ this.openmct.user.status.on('pollQuestionChange', this.setPollQuestion);
+ },
+ setStatus({role, status}) {
+ status = this.applyStyling(status);
+ this.selectedStatus = status.key;
+ this.indicator.iconClass(status.iconClassPoll);
+ this.indicator.statusClass(status.statusClass);
+ if (this.isDefaultStatus(status)) {
+ this.indicator.text(this.currentPollQuestion);
+ } else {
+ this.indicator.text(status.label);
+ }
+ },
+ isDefaultStatus(status) {
+ return status.key === this.allStatuses[0].key;
+ },
+ findStatusByKey(statusKey) {
+ return this.allStatuses.find(possibleMatch => possibleMatch.key === statusKey);
+ },
+ async changeStatus() {
+ if (this.selectedStatus !== undefined) {
+ const statusObject = this.findStatusByKey(this.selectedStatus);
+
+ const result = await this.openmct.user.status.setStatusForRole(this.role, statusObject);
+
+ if (result === true) {
+ this.openmct.notifications.info("Successfully set operator status");
+ } else {
+ this.openmct.notifications.error("Unable to set operator status");
+ }
+ }
+ },
+ applyStyling(status) {
+ const stylesForStatus = this.configuration?.statusStyles?.[status.label];
+
+ if (stylesForStatus !== undefined) {
+ return {
+ ...status,
+ ...stylesForStatus
+ };
+ } else {
+ return status;
+ }
+ },
+ noop() {}
+ }
+};
+</script>
diff --git a/src/plugins/operatorStatus/operatorStatus/OperatorStatusIndicator.js b/src/plugins/operatorStatus/operatorStatus/OperatorStatusIndicator.js
new file mode 100644
index 000000000..9eb96e938
--- /dev/null
+++ b/src/plugins/operatorStatus/operatorStatus/OperatorStatusIndicator.js
@@ -0,0 +1,63 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+import Vue from 'vue';
+
+import AbstractStatusIndicator from '../AbstractStatusIndicator';
+import OperatorStatusComponent from './OperatorStatus.vue';
+
+export default class OperatorStatusIndicator extends AbstractStatusIndicator {
+ createPopupComponent() {
+ const indicator = this.getIndicator();
+ const popupElement = new Vue({
+ components: {
+ OperatorStatus: OperatorStatusComponent
+ },
+ provide: {
+ openmct: this.openmct,
+ indicator: indicator,
+ configuration: this.getConfiguration()
+ },
+ data() {
+ return {
+ positionX: 0,
+ positionY: 0
+ };
+ },
+ template: '<operator-status :positionX="positionX" :positionY="positionY" />'
+ }).$mount();
+
+ return popupElement;
+ }
+
+ createIndicator() {
+ const operatorIndicator = this.openmct.indicators.simpleIndicator();
+
+ operatorIndicator.text("My Operator Status");
+ operatorIndicator.description("Set my operator status");
+ operatorIndicator.iconClass('icon-status-poll-question-mark');
+ operatorIndicator.element.classList.add("c-indicator--operator-status");
+ operatorIndicator.element.classList.add("no-minify");
+ operatorIndicator.on('click', this.showPopup);
+
+ return operatorIndicator;
+ }
+}
diff --git a/platform/identity/src/IdentityIndicator.js b/src/plugins/operatorStatus/plugin.js
index db1c8bebb..3d449d1eb 100644
--- a/platform/identity/src/IdentityIndicator.js
+++ b/src/plugins/operatorStatus/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -19,42 +19,32 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
+import OperatorStatusIndicator from './operatorStatus/OperatorStatusIndicator';
+import PollQuestionIndicator from './pollQuestion/PollQuestionIndicator';
-define(
- [],
- function () {
+/**
+ * @param {import('@/api/user/UserAPI').UserAPIConfiguration} configuration
+ * @returns {function} The plugin install function
+ */
+export default function operatorStatusPlugin(configuration) {
+ return function install(openmct) {
- /**
- * Indicator showing the currently logged-in user.
- * @constructor
- * @memberof platform/identity
- * @implements {Indicator}
- * @param {IdentityService} identityService the identity service
- */
- function IdentityIndicator(identityService) {
- // Track the current connection state
- var self = this;
+ if (openmct.user.hasProvider()) {
+ openmct.user.status.canProvideStatusForCurrentUser().then(canProvideStatus => {
+ if (canProvideStatus) {
+ const operatorStatusIndicator = new OperatorStatusIndicator(openmct, configuration);
- identityService.getUser().then(function (user) {
- if (user && user.key) {
- self.text = user.name || user.key;
- self.description = "Logged in as " + user.key;
+ operatorStatusIndicator.install();
}
});
- }
-
- IdentityIndicator.prototype.getCssClass = function () {
- return this.text && "icon-person";
- };
-
- IdentityIndicator.prototype.getText = function () {
- return this.text;
- };
- IdentityIndicator.prototype.getDescription = function () {
- return this.description;
- };
+ openmct.user.status.canSetPollQuestion().then(canSetPollQuestion => {
+ if (canSetPollQuestion) {
+ const pollQuestionIndicator = new PollQuestionIndicator(openmct, configuration);
- return IdentityIndicator;
- }
-);
+ pollQuestionIndicator.install();
+ }
+ });
+ }
+ };
+}
diff --git a/src/plugins/operatorStatus/pollQuestion/PollQuestion.vue b/src/plugins/operatorStatus/pollQuestion/PollQuestion.vue
new file mode 100644
index 000000000..ff8443cf9
--- /dev/null
+++ b/src/plugins/operatorStatus/pollQuestion/PollQuestion.vue
@@ -0,0 +1,190 @@
+<!--
+ Open MCT, Copyright (c) 2014-2022, United States Government
+ as represented by the Administrator of the National Aeronautics and Space
+ Administration. All rights reserved.
+
+ Open MCT is licensed under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0.
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ License for the specific language governing permissions and limitations
+ under the License.
+
+ Open MCT includes source code licensed under additional open source
+ licenses. See the Open Source Licenses file (LICENSES.md) included with
+ this source code distribution or the Licensing information page available
+ at runtime from the About dialog for additional information.
+-->
+<template>
+<div
+ :style="position"
+ class="c-status-poll-panel c-status-poll-panel--admin"
+ @click.stop="noop"
+>
+ <div class="c-status-poll-panel__section c-status-poll-panel__top">
+ <div
+ class="c-status-poll-panel__title"
+ >Manage Status Poll</div>
+ <div class="c-status-poll-panel__updated">Last updated: {{ pollQuestionUpdated }}</div>
+ </div>
+
+ <div class="c-status-poll__section c-status-poll-panel__content c-spq">
+ <!-- Grid layout -->
+ <div class="c-spq__label">Current poll:</div>
+ <div class="c-spq__value c-status-poll-panel__poll-question">{{ currentPollQuestion }}</div>
+
+ <template v-if="statusCountViewModel.length > 0">
+ <div class="c-spq__label">Reporting:</div>
+ <div class="c-spq__value c-status-poll-panel__poll-reporting c-status-poll-report">
+ <div
+ v-for="entry in statusCountViewModel"
+ :key="entry.status.key"
+ :title="entry.status.label"
+ class="c-status-poll-report__count"
+ :style="[{
+ background: entry.status.statusBgColor,
+ color: entry.status.statusFgColor
+ }]"
+ >
+ <div
+ class="c-status-poll-report__count-type"
+ :class="entry.status.iconClass"
+ ></div>
+ <div class="c-status-poll-report__count-value">
+ {{ entry.roleCount }}
+ </div>
+ </div>
+ </div>
+ </template>
+
+ <div class="c-spq__label">New poll:</div>
+ <div class="c-spq__value c-status-poll-panel__poll-new-question">
+ <input
+ v-model="newPollQuestion"
+ type="text"
+ name="newPollQuestion"
+ >
+ <button
+ class="c-button"
+ title="Publish a new poll question and reset previous responses"
+ @click="updatePollQuestion"
+ >Update</button>
+ </div>
+ </div>
+
+</div>
+</template>
+
+<script>
+import _ from 'lodash';
+
+export default {
+ inject: ['openmct', 'indicator', 'configuration'],
+ props: {
+ positionX: {
+ type: Number,
+ required: true
+ },
+ positionY: {
+ type: Number,
+ required: true
+ }
+ },
+ data() {
+ return {
+ pollQuestionUpdated: '--',
+ currentPollQuestion: '--',
+ newPollQuestion: undefined,
+ statusCountViewModel: []
+ };
+ },
+ computed: {
+ position() {
+ return {
+ position: 'absolute',
+ left: `${this.positionX}px`,
+ top: `${this.positionY}px`
+ };
+ }
+ },
+ mounted() {
+ this.fetchCurrentPoll();
+ this.subscribeToPollQuestion();
+ this.fetchStatusSummary();
+ this.openmct.user.status.on('statusChange', this.fetchStatusSummary);
+ },
+ beforeDestroy() {
+ this.openmct.user.status.off('statusChange', this.fetchStatusSummary);
+ this.openmct.user.status.off('pollQuestionChange', this.setPollQuestion);
+ },
+ created() {
+ this.fetchStatusSummary = _.debounce(this.fetchStatusSummary);
+ },
+ methods: {
+ async fetchCurrentPoll() {
+ const pollQuestion = await this.openmct.user.status.getPollQuestion();
+ if (pollQuestion !== undefined) {
+ this.setPollQuestion(pollQuestion);
+ }
+ },
+ subscribeToPollQuestion() {
+ this.openmct.user.status.on('pollQuestionChange', this.setPollQuestion);
+ },
+ setPollQuestion(pollQuestion) {
+ this.currentPollQuestion = pollQuestion.question;
+ this.pollQuestionUpdated = new Date(pollQuestion.timestamp).toISOString();
+ this.indicator.text(pollQuestion.question);
+ },
+ async updatePollQuestion() {
+ const result = await this.openmct.user.status.setPollQuestion(this.newPollQuestion);
+ if (result === true) {
+ this.openmct.notifications.info("Successfully set new poll question");
+ } else {
+ this.openmct.notifications.error("Unable to set new poll question.");
+ }
+
+ this.newPollQuestion = undefined;
+ },
+ async fetchStatusSummary() {
+ const allStatuses = await this.openmct.user.status.getPossibleStatuses();
+ const statusCountMap = allStatuses.reduce((statusToCountMap, status) => {
+ statusToCountMap[status.key] = 0;
+
+ return statusToCountMap;
+ }, {});
+ const allStatusRoles = await this.openmct.user.status.getAllStatusRoles();
+ const statusesForRoles = await Promise.all(allStatusRoles.map(role => this.openmct.user.status.getStatusForRole(role)));
+
+ statusesForRoles.forEach((status, i) => {
+ const currentCount = statusCountMap[status.key];
+ statusCountMap[status.key] = currentCount + 1;
+ });
+
+ this.statusCountViewModel = allStatuses.map((status) => {
+ return {
+ status: this.applyStyling(status),
+ roleCount: statusCountMap[status.key]
+ };
+ });
+ },
+ applyStyling(status) {
+ const stylesForStatus = this.configuration?.statusStyles?.[status.label];
+
+ if (stylesForStatus !== undefined) {
+ return {
+ ...status,
+ ...stylesForStatus
+ };
+ } else {
+ return status;
+ }
+ },
+ noop() {}
+ }
+
+};
+</script>
diff --git a/src/plugins/operatorStatus/pollQuestion/PollQuestionIndicator.js b/src/plugins/operatorStatus/pollQuestion/PollQuestionIndicator.js
new file mode 100644
index 000000000..ea85d5905
--- /dev/null
+++ b/src/plugins/operatorStatus/pollQuestion/PollQuestionIndicator.js
@@ -0,0 +1,63 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+import Vue from 'vue';
+
+import AbstractStatusIndicator from '../AbstractStatusIndicator';
+import PollQuestionComponent from './PollQuestion.vue';
+
+export default class PollQuestionIndicator extends AbstractStatusIndicator {
+ createPopupComponent() {
+ const indicator = this.getIndicator();
+ const pollQuestionElement = new Vue({
+ components: {
+ PollQuestion: PollQuestionComponent
+ },
+ provide: {
+ openmct: this.openmct,
+ indicator: indicator,
+ configuration: this.getConfiguration()
+ },
+ data() {
+ return {
+ positionX: 0,
+ positionY: 0
+ };
+ },
+ template: '<poll-question :positionX="positionX" :positionY="positionY" />'
+ }).$mount();
+
+ return pollQuestionElement;
+ }
+
+ createIndicator() {
+ const pollQuestionIndicator = this.openmct.indicators.simpleIndicator();
+
+ pollQuestionIndicator.text("Poll Question");
+ pollQuestionIndicator.description("Set the current poll question");
+ pollQuestionIndicator.iconClass('icon-status-poll-edit');
+ pollQuestionIndicator.element.classList.add("c-indicator--operator-status");
+ pollQuestionIndicator.element.classList.add("no-minify");
+ pollQuestionIndicator.on('click', this.showPopup);
+
+ return pollQuestionIndicator;
+ }
+}
diff --git a/src/plugins/performanceIndicator/plugin.js b/src/plugins/performanceIndicator/plugin.js
index 6285ce421..e982ac408 100644
--- a/src/plugins/performanceIndicator/plugin.js
+++ b/src/plugins/performanceIndicator/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/performanceIndicator/pluginSpec.js b/src/plugins/performanceIndicator/pluginSpec.js
index 380da0ac0..edeff1add 100644
--- a/src/plugins/performanceIndicator/pluginSpec.js
+++ b/src/plugins/performanceIndicator/pluginSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -20,10 +20,7 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
import PerformancePlugin from './plugin.js';
-import {
- createOpenMct,
- resetApplicationState
-} from 'utils/testing';
+import { createOpenMct, resetApplicationState } from 'utils/testing';
describe('the plugin', () => {
let openmct;
@@ -31,9 +28,8 @@ describe('the plugin', () => {
let child;
let performanceIndicator;
- let countFramesPromise;
- beforeEach((done) => {
+ beforeEach(done => {
openmct = createOpenMct();
element = document.createElement('div');
@@ -42,11 +38,9 @@ describe('the plugin', () => {
openmct.install(new PerformancePlugin());
- countFramesPromise = countFrames();
-
openmct.on('start', done);
- performanceIndicator = openmct.indicators.indicatorObjects.find((indicator) => {
+ performanceIndicator = openmct.indicators.indicatorObjects.find(indicator => {
return indicator.text && indicator.text() === '~ fps';
});
@@ -61,25 +55,21 @@ describe('the plugin', () => {
expect(performanceIndicator).toBeDefined();
});
- it('correctly calculates fps', () => {
- return countFramesPromise.then((frames) => {
- expect(performanceIndicator.text()).toEqual(`${frames} fps`);
- });
+ it('calculates an fps value', async () => {
+ await loopForABit();
+ // eslint-disable-next-line
+ expect(parseInt(performanceIndicator.text().split(' fps')[0])).toBeGreaterThan(0);
});
- function countFrames() {
- let startTime = performance.now();
+ function loopForABit() {
let frames = 0;
- return new Promise((resolve) => {
- requestAnimationFrame(function incrementCount() {
- let now = performance.now();
-
- if ((now - startTime) < 1000) {
- frames++;
- requestAnimationFrame(incrementCount);
+ return new Promise(resolve => {
+ requestAnimationFrame(function loop() {
+ if (++frames === 240) {
+ resolve();
} else {
- resolve(frames);
+ requestAnimationFrame(loop);
}
});
});
diff --git a/src/plugins/persistence/couch/CouchChangesFeed.js b/src/plugins/persistence/couch/CouchChangesFeed.js
index 508ada46b..3c1445cec 100644
--- a/src/plugins/persistence/couch/CouchChangesFeed.js
+++ b/src/plugins/persistence/couch/CouchChangesFeed.js
@@ -43,11 +43,18 @@
};
self.onerror = function (error) {
+ self.updateCouchStateIndicator();
console.error('🚨 Error on CouchDB feed 🚨', error);
};
+ self.onopen = function () {
+ self.updateCouchStateIndicator();
+ };
+
self.onCouchMessage = function (event) {
+ self.updateCouchStateIndicator();
console.debug('📩 Received message from CouchDB 📩');
+
const objectChanges = JSON.parse(event.data);
connections.forEach(function (connection) {
connection.postMessage({
@@ -61,10 +68,38 @@
couchEventSource = new EventSource(url);
couchEventSource.onerror = self.onerror;
+ couchEventSource.onopen = self.onopen;
// start listening for events
couchEventSource.addEventListener('message', self.onCouchMessage);
connected = true;
console.debug('⇿ Opened connection ⇿');
};
+
+ self.updateCouchStateIndicator = function () {
+ const { readyState } = couchEventSource;
+ let message = {
+ type: 'state',
+ state: 'pending'
+ };
+ switch (readyState) {
+ case EventSource.CONNECTING:
+ message.state = 'pending';
+ break;
+ case EventSource.OPEN:
+ message.state = 'open';
+ break;
+ case EventSource.CLOSED:
+ message.state = 'close';
+ break;
+ default:
+ message.state = 'unknown';
+ console.error('🚨 Received unexpected readyState value from CouchDB EventSource feed: 🚨', readyState);
+ break;
+ }
+
+ connections.forEach(function (connection) {
+ connection.postMessage(message);
+ });
+ };
}());
diff --git a/src/plugins/persistence/couch/CouchDocument.js b/src/plugins/persistence/couch/CouchDocument.js
index 4e09012aa..920793c6d 100644
--- a/src/plugins/persistence/couch/CouchDocument.js
+++ b/src/plugins/persistence/couch/CouchDocument.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -45,8 +45,7 @@ export default function CouchDocument(id, model, rev, markDeleted) {
"category": "domain object",
"type": model.type,
"owner": "admin",
- "name": model.name,
- "created": Date.now()
+ "name": model.name
},
"model": model
};
diff --git a/src/plugins/persistence/couch/CouchObjectProvider.js b/src/plugins/persistence/couch/CouchObjectProvider.js
index e9a7dc644..754fb6d5d 100644
--- a/src/plugins/persistence/couch/CouchObjectProvider.js
+++ b/src/plugins/persistence/couch/CouchObjectProvider.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -22,7 +22,8 @@
import CouchDocument from "./CouchDocument";
import CouchObjectQueue from "./CouchObjectQueue";
-import { NOTEBOOK_TYPE } from '../../notebook/notebook-constants.js';
+import { PENDING, CONNECTED, DISCONNECTED, UNKNOWN } from "./CouchStatusIndicator";
+import { isNotebookType } from '../../notebook/notebook-constants.js';
const REV = "_rev";
const ID = "_id";
@@ -30,9 +31,10 @@ const HEARTBEAT = 50000;
const ALL_DOCS = "_all_docs?include_docs=true";
class CouchObjectProvider {
- constructor(openmct, options, namespace) {
- options = this._normalize(options);
+ constructor(openmct, options, namespace, indicator) {
+ options = this.#normalize(options);
this.openmct = openmct;
+ this.indicator = indicator;
this.url = options.url;
this.namespace = namespace;
this.objectQueue = {};
@@ -45,7 +47,7 @@ class CouchObjectProvider {
/**
* @private
*/
- startSharedWorker() {
+ #startSharedWorker() {
let provider = this;
let sharedWorker;
@@ -82,6 +84,9 @@ class CouchObjectProvider {
onSharedWorkerMessage(event) {
if (event.data.type === 'connection') {
this.changesFeedSharedWorkerConnectionId = event.data.connectionId;
+ } else if (event.data.type === 'state') {
+ const state = this.#messageToIndicatorState(event.data.state);
+ this.indicator.setIndicatorToState(state);
} else {
let objectChanges = event.data.objectChanges;
const objectIdentifier = {
@@ -103,8 +108,72 @@ class CouchObjectProvider {
}
}
+ /**
+ * Takes in a state message from the CouchDB SharedWorker and returns an IndicatorState.
+ * @private
+ * @param {'open'|'close'|'pending'} message
+ * @returns {import('./CouchStatusIndicator').IndicatorState}
+ */
+ #messageToIndicatorState(message) {
+ let state;
+ switch (message) {
+ case 'open':
+ state = CONNECTED;
+ break;
+ case 'close':
+ state = DISCONNECTED;
+ break;
+ case 'pending':
+ state = PENDING;
+ break;
+ case 'unknown':
+ state = UNKNOWN;
+ break;
+ }
+
+ return state;
+ }
+
+ /**
+ * Takes an HTTP status code and returns an IndicatorState
+ * @private
+ * @param {number} statusCode
+ * @returns {import("./CouchStatusIndicator").IndicatorState}
+ */
+ #statusCodeToIndicatorState(statusCode) {
+ let state;
+ switch (statusCode) {
+ case CouchObjectProvider.HTTP_OK:
+ case CouchObjectProvider.HTTP_CREATED:
+ case CouchObjectProvider.HTTP_ACCEPTED:
+ case CouchObjectProvider.HTTP_NOT_MODIFIED:
+ case CouchObjectProvider.HTTP_BAD_REQUEST:
+ case CouchObjectProvider.HTTP_UNAUTHORIZED:
+ case CouchObjectProvider.HTTP_FORBIDDEN:
+ case CouchObjectProvider.HTTP_NOT_FOUND:
+ case CouchObjectProvider.HTTP_METHOD_NOT_ALLOWED:
+ case CouchObjectProvider.HTTP_NOT_ACCEPTABLE:
+ case CouchObjectProvider.HTTP_CONFLICT:
+ case CouchObjectProvider.HTTP_PRECONDITION_FAILED:
+ case CouchObjectProvider.HTTP_REQUEST_ENTITY_TOO_LARGE:
+ case CouchObjectProvider.HTTP_UNSUPPORTED_MEDIA_TYPE:
+ case CouchObjectProvider.HTTP_REQUESTED_RANGE_NOT_SATISFIABLE:
+ case CouchObjectProvider.HTTP_EXPECTATION_FAILED:
+ case CouchObjectProvider.HTTP_SERVER_ERROR:
+ state = CONNECTED;
+ break;
+ case CouchObjectProvider.HTTP_SERVICE_UNAVAILABLE:
+ state = DISCONNECTED;
+ break;
+ default:
+ state = UNKNOWN;
+ }
+
+ return state;
+ }
+
//backwards compatibility, options used to be a url. Now it's an object
- _normalize(options) {
+ #normalize(options) {
if (typeof options === 'string') {
return {
url: options
@@ -114,7 +183,7 @@ class CouchObjectProvider {
return options;
}
- request(subPath, method, body, signal) {
+ async request(subPath, method, body, signal) {
let fetchOptions = {
method,
body,
@@ -129,14 +198,47 @@ class CouchObjectProvider {
};
}
- return fetch(this.url + '/' + subPath, fetchOptions)
- .then((response) => {
- if (response.status === CouchObjectProvider.HTTP_CONFLICT) {
- throw new this.openmct.objects.errors.Conflict(`Conflict persisting ${fetchOptions.body.name}`);
- }
+ let response = null;
- return response.json();
- });
+ if (!this.isObservingObjectChanges()) {
+ this.#observeObjectChanges();
+ }
+
+ try {
+ response = await fetch(this.url + '/' + subPath, fetchOptions);
+ const { status } = response;
+ const json = await response.json();
+ this.#handleResponseCode(status, json, fetchOptions);
+
+ return json;
+ } catch (error) {
+ // Network error, CouchDB unreachable.
+ if (response === null) {
+ this.indicator.setIndicatorToState(DISCONNECTED);
+ console.error(error.message);
+ throw new Error(`CouchDB Error - No response"`);
+ }
+
+ console.error(error.message);
+ }
+ }
+
+ /**
+ * Handle the response code from a CouchDB request.
+ * Sets the CouchDB indicator status and throws an error if needed.
+ * @private
+ */
+ #handleResponseCode(status, json, fetchOptions) {
+ this.indicator.setIndicatorToState(this.#statusCodeToIndicatorState(status));
+ if (status === CouchObjectProvider.HTTP_CONFLICT) {
+ throw new this.openmct.objects.errors.Conflict(`Conflict persisting ${fetchOptions.body.name}`);
+ } else if (status >= CouchObjectProvider.HTTP_BAD_REQUEST) {
+ if (!json.error || !json.reason) {
+ throw new Error(`CouchDB Error ${status}`);
+ }
+
+ throw new Error(`CouchDB Error ${status}: "${json.error} - ${json.reason}"`);
+ }
}
/**
@@ -146,7 +248,7 @@ class CouchObjectProvider {
* persist any queued objects
* @private
*/
- checkResponse(response, intermediateResponse, key) {
+ #checkResponse(response, intermediateResponse, key) {
let requestSuccess = false;
const id = response ? response.id : undefined;
let rev;
@@ -166,7 +268,7 @@ class CouchObjectProvider {
this.objectQueue[id].updateRevision(rev);
this.objectQueue[id].pending = false;
if (this.objectQueue[id].hasNext()) {
- this.updateQueued(id);
+ this.#updateQueued(id);
}
} else {
this.objectQueue[key].pending = false;
@@ -176,7 +278,7 @@ class CouchObjectProvider {
/**
* @private
*/
- getModel(response) {
+ #getModel(response) {
if (response && response.model) {
let key = response[ID];
let object = this.fromPersistedModel(response.model, key);
@@ -185,7 +287,7 @@ class CouchObjectProvider {
this.objectQueue[key] = new CouchObjectQueue(undefined, response[REV]);
}
- if (object.type === NOTEBOOK_TYPE) {
+ if (isNotebookType(object)) {
//Temporary measure until object sync is supported for all object types
//Always update notebook revision number because we have realtime sync, so always assume it's the latest.
this.objectQueue[key].updateRevision(response[REV]);
@@ -204,7 +306,7 @@ class CouchObjectProvider {
this.batchIds.push(identifier.key);
if (this.bulkPromise === undefined) {
- this.bulkPromise = this.deferBatchedGet(abortSignal);
+ this.bulkPromise = this.#deferBatchedGet(abortSignal);
}
return this.bulkPromise
@@ -216,23 +318,23 @@ class CouchObjectProvider {
/**
* @private
*/
- deferBatchedGet(abortSignal) {
+ #deferBatchedGet(abortSignal) {
// We until the next event loop cycle to "collect" all of the get
// requests triggered in this iteration of the event loop
- return this.waitOneEventCycle().then(() => {
+ return this.#waitOneEventCycle().then(() => {
let batchIds = this.batchIds;
- this.clearBatch();
+ this.#clearBatch();
if (batchIds.length === 1) {
let objectKey = batchIds[0];
//If there's only one request, just do a regular get
return this.request(objectKey, "GET", undefined, abortSignal)
- .then(this.returnAsMap(objectKey));
+ .then(this.#returnAsMap(objectKey));
} else {
- return this.bulkGet(batchIds, abortSignal);
+ return this.#bulkGet(batchIds, abortSignal);
}
});
}
@@ -240,10 +342,10 @@ class CouchObjectProvider {
/**
* @private
*/
- returnAsMap(objectKey) {
+ #returnAsMap(objectKey) {
return (result) => {
let objectMap = {};
- objectMap[objectKey] = this.getModel(result);
+ objectMap[objectKey] = this.#getModel(result);
return objectMap;
};
@@ -252,7 +354,7 @@ class CouchObjectProvider {
/**
* @private
*/
- clearBatch() {
+ #clearBatch() {
this.batchIds = [];
delete this.bulkPromise;
}
@@ -260,7 +362,7 @@ class CouchObjectProvider {
/**
* @private
*/
- waitOneEventCycle() {
+ #waitOneEventCycle() {
return new Promise((resolve) => {
setTimeout(resolve);
});
@@ -269,7 +371,7 @@ class CouchObjectProvider {
/**
* @private
*/
- bulkGet(ids, signal) {
+ #bulkGet(ids, signal) {
ids = this.removeDuplicates(ids);
const query = {
@@ -279,8 +381,10 @@ class CouchObjectProvider {
return this.request(ALL_DOCS, 'POST', query, signal).then((response) => {
if (response && response.rows !== undefined) {
return response.rows.reduce((map, row) => {
+ //row.doc === null if the document does not exist.
+ //row.doc === undefined if the document is not found.
if (row.doc !== undefined) {
- map[row.key] = this.getModel(row.doc);
+ map[row.key] = this.#getModel(row.doc);
}
return map;
@@ -298,18 +402,12 @@ class CouchObjectProvider {
return Array.from(new Set(array));
}
- search(query, abortSignal) {
- const filter = {
- "selector": {
- "model": {
- "name": {
- "$regex": `(?i)${query}`
- }
- }
- }
- };
-
- return this.getObjectsByFilter(filter, abortSignal);
+ search() {
+ // Dummy search function. It has to appear to support search,
+ // otherwise the in-memory indexer will index all of its objects,
+ // but actually search results will be provided by a separate search provider
+ // see CoucheSearchProvider.js
+ return Promise.resolve([]);
}
async getObjectsByFilter(filter, abortSignal) {
@@ -355,7 +453,7 @@ class CouchObjectProvider {
if (json) {
let docs = json.docs;
docs.forEach(doc => {
- let object = this.getModel(doc);
+ let object = this.#getModel(doc);
if (object) {
objects.push(object);
}
@@ -374,7 +472,7 @@ class CouchObjectProvider {
this.observers[keyString].push(callback);
if (!this.isObservingObjectChanges()) {
- this.observeObjectChanges();
+ this.#observeObjectChanges();
}
return () => {
@@ -382,9 +480,6 @@ class CouchObjectProvider {
this.observers[keyString] = this.observers[keyString].filter(observer => observer !== callback);
if (this.observers[keyString].length === 0) {
delete this.observers[keyString];
- if (Object.keys(this.observers).length === 0 && this.isObservingObjectChanges()) {
- this.stopObservingObjectChanges();
- }
}
}
};
@@ -397,7 +492,7 @@ class CouchObjectProvider {
/**
* @private
*/
- observeObjectChanges() {
+ #observeObjectChanges() {
const sseChangesPath = `${this.url}/_changes`;
const sseURL = new URL(sseChangesPath);
sseURL.searchParams.append('feed', 'eventsource');
@@ -407,17 +502,16 @@ class CouchObjectProvider {
if (typeof SharedWorker === 'undefined') {
this.fetchChanges(sseURL.toString());
} else {
- this.initiateSharedWorkerFetchChanges(sseURL.toString());
+ this.#initiateSharedWorkerFetchChanges(sseURL.toString());
}
-
}
/**
* @private
*/
- initiateSharedWorkerFetchChanges(url) {
+ #initiateSharedWorkerFetchChanges(url) {
if (!this.changesFeedSharedWorker) {
- this.changesFeedSharedWorker = this.startSharedWorker();
+ this.changesFeedSharedWorker = this.#startSharedWorker();
if (this.isObservingObjectChanges()) {
this.stopObservingObjectChanges();
@@ -436,18 +530,24 @@ class CouchObjectProvider {
onEventError(error) {
console.error('Error on feed', error);
- if (Object.keys(this.observers).length > 0) {
- this.observeObjectChanges();
- }
+ const { readyState } = error.target;
+ this.#updateIndicatorStatus(readyState);
+ }
+
+ onEventOpen(event) {
+ const { readyState } = event.target;
+ this.#updateIndicatorStatus(readyState);
}
onEventMessage(event) {
+ const { readyState } = event.target;
const eventData = JSON.parse(event.data);
const identifier = {
namespace: this.namespace,
key: eventData.id
};
const keyString = this.openmct.objects.makeKeyString(identifier);
+ this.#updateIndicatorStatus(readyState);
let observersForObject = this.observers[keyString];
if (observersForObject) {
@@ -470,24 +570,26 @@ class CouchObjectProvider {
this.stopObservingObjectChanges = () => {
controller.abort();
- couchEventSource.removeEventListener('message', this.onEventMessage);
+ couchEventSource.removeEventListener('message', this.onEventMessage.bind(this));
delete this.stopObservingObjectChanges;
};
console.debug('⇿ Opening CouchDB change feed connection ⇿');
couchEventSource = new EventSource(url);
- couchEventSource.onerror = this.onEventError;
+ couchEventSource.onerror = this.onEventError.bind(this);
+ couchEventSource.onopen = this.onEventOpen.bind(this);
// start listening for events
- couchEventSource.addEventListener('message', this.onEventMessage);
+ couchEventSource.addEventListener('message', this.onEventMessage.bind(this));
+
console.debug('⇿ Opened connection ⇿');
}
/**
* @private
*/
- getIntermediateResponse() {
+ #getIntermediateResponse() {
let intermediateResponse = {};
intermediateResponse.promise = new Promise(function (resolve, reject) {
intermediateResponse.resolve = resolve;
@@ -498,6 +600,31 @@ class CouchObjectProvider {
}
/**
+ * Update the indicator status based on the readyState of the EventSource
+ * @private
+ */
+ #updateIndicatorStatus(readyState) {
+ let message;
+ switch (readyState) {
+ case EventSource.CONNECTING:
+ message = 'pending';
+ break;
+ case EventSource.OPEN:
+ message = 'open';
+ break;
+ case EventSource.CLOSED:
+ message = 'close';
+ break;
+ default:
+ message = 'unknown';
+ break;
+ }
+
+ const indicatorState = this.#messageToIndicatorState(message);
+ this.indicator.setIndicatorToState(indicatorState);
+ }
+
+ /**
* @private
*/
enqueueObject(key, model, intermediateResponse) {
@@ -515,7 +642,7 @@ class CouchObjectProvider {
}
create(model) {
- let intermediateResponse = this.getIntermediateResponse();
+ let intermediateResponse = this.#getIntermediateResponse();
const key = model.identifier.key;
model = this.toPersistableModel(model);
this.enqueueObject(key, model, intermediateResponse);
@@ -524,9 +651,10 @@ class CouchObjectProvider {
this.objectQueue[key].pending = true;
const queued = this.objectQueue[key].dequeue();
let document = new CouchDocument(key, queued.model);
+ document.metadata.created = Date.now();
this.request(key, "PUT", document).then((response) => {
console.log('create check response', key);
- this.checkResponse(response, queued.intermediateResponse, key);
+ this.#checkResponse(response, queued.intermediateResponse, key);
}).catch(error => {
queued.intermediateResponse.reject(error);
this.objectQueue[key].pending = false;
@@ -539,13 +667,13 @@ class CouchObjectProvider {
/**
* @private
*/
- updateQueued(key) {
+ #updateQueued(key) {
if (!this.objectQueue[key].pending) {
this.objectQueue[key].pending = true;
const queued = this.objectQueue[key].dequeue();
let document = new CouchDocument(key, queued.model, this.objectQueue[key].rev);
this.request(key, "PUT", document).then((response) => {
- this.checkResponse(response, queued.intermediateResponse, key);
+ this.#checkResponse(response, queued.intermediateResponse, key);
}).catch((error) => {
queued.intermediateResponse.reject(error);
this.objectQueue[key].pending = false;
@@ -554,12 +682,12 @@ class CouchObjectProvider {
}
update(model) {
- let intermediateResponse = this.getIntermediateResponse();
+ let intermediateResponse = this.#getIntermediateResponse();
const key = model.identifier.key;
model = this.toPersistableModel(model);
this.enqueueObject(key, model, intermediateResponse);
- this.updateQueued(key);
+ this.#updateQueued(key);
return intermediateResponse.promise;
}
@@ -583,6 +711,25 @@ class CouchObjectProvider {
}
}
+// https://docs.couchdb.org/en/3.2.0/api/basics.html
+CouchObjectProvider.HTTP_OK = 200;
+CouchObjectProvider.HTTP_CREATED = 201;
+CouchObjectProvider.HTTP_ACCEPTED = 202;
+CouchObjectProvider.HTTP_NOT_MODIFIED = 304;
+CouchObjectProvider.HTTP_BAD_REQUEST = 400;
+CouchObjectProvider.HTTP_UNAUTHORIZED = 401;
+CouchObjectProvider.HTTP_FORBIDDEN = 403;
+CouchObjectProvider.HTTP_NOT_FOUND = 404;
+CouchObjectProvider.HTTP_METHOD_NOT_ALLOWED = 404;
+CouchObjectProvider.HTTP_NOT_ACCEPTABLE = 406;
CouchObjectProvider.HTTP_CONFLICT = 409;
+CouchObjectProvider.HTTP_PRECONDITION_FAILED = 412;
+CouchObjectProvider.HTTP_REQUEST_ENTITY_TOO_LARGE = 413;
+CouchObjectProvider.HTTP_UNSUPPORTED_MEDIA_TYPE = 415;
+CouchObjectProvider.HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
+CouchObjectProvider.HTTP_EXPECTATION_FAILED = 417;
+CouchObjectProvider.HTTP_SERVER_ERROR = 500;
+// If CouchDB is containerized via Docker it will return 503 if service is unavailable.
+CouchObjectProvider.HTTP_SERVICE_UNAVAILABLE = 503;
export default CouchObjectProvider;
diff --git a/src/plugins/persistence/couch/CouchObjectQueue.js b/src/plugins/persistence/couch/CouchObjectQueue.js
index 52a712970..a38da8ac8 100644
--- a/src/plugins/persistence/couch/CouchObjectQueue.js
+++ b/src/plugins/persistence/couch/CouchObjectQueue.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/persistence/couch/CouchSearchProvider.js b/src/plugins/persistence/couch/CouchSearchProvider.js
new file mode 100644
index 000000000..3b2295e99
--- /dev/null
+++ b/src/plugins/persistence/couch/CouchSearchProvider.js
@@ -0,0 +1,150 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+// This provider exists because due to legacy reasons, we need to install
+// two plugins for two namespaces for CouchDB: one for "mct", and one for "".
+// Because of this, we need to separate out the search provider from the object
+// provider so we don't return two results for each found object.
+// If the above namespace is ever resolved, we can fold this search provider
+// back into the object provider.
+
+class CouchSearchProvider {
+ constructor(couchObjectProvider) {
+ this.couchObjectProvider = couchObjectProvider;
+ this.searchTypes = couchObjectProvider.openmct.objects.SEARCH_TYPES;
+ this.supportedSearchTypes = [this.searchTypes.OBJECTS, this.searchTypes.ANNOTATIONS, this.searchTypes.NOTEBOOK_ANNOTATIONS, this.searchTypes.TAGS];
+ }
+
+ supportsSearchType(searchType) {
+ return this.supportedSearchTypes.includes(searchType);
+ }
+
+ search(query, abortSignal, searchType) {
+ if (searchType === this.searchTypes.OBJECTS) {
+ return this.searchForObjects(query, abortSignal);
+ } else if (searchType === this.searchTypes.ANNOTATIONS) {
+ return this.searchForAnnotations(query, abortSignal);
+ } else if (searchType === this.searchTypes.NOTEBOOK_ANNOTATIONS) {
+ return this.searchForNotebookAnnotations(query, abortSignal);
+ } else if (searchType === this.searchTypes.TAGS) {
+ return this.searchForTags(query, abortSignal);
+ } else {
+ throw new Error(`🤷‍♂️ Unknown search type passed: ${searchType}`);
+ }
+ }
+
+ searchForObjects(query, abortSignal) {
+ const filter = {
+ "selector": {
+ "model": {
+ "name": {
+ "$regex": `(?i)${query}`
+ }
+ }
+ }
+ };
+
+ return this.couchObjectProvider.getObjectsByFilter(filter, abortSignal);
+ }
+
+ searchForAnnotations(keyString, abortSignal) {
+ const filter = {
+ "selector": {
+ "$and": [
+ {
+ "model": {
+ "targets": {
+ }
+ }
+ },
+ {
+ "model.type": {
+ "$eq": "annotation"
+ }
+ }
+ ]
+ }
+ };
+ filter.selector.$and[0].model.targets[keyString] = {
+ "$exists": true
+ };
+
+ return this.couchObjectProvider.getObjectsByFilter(filter, abortSignal);
+ }
+
+ searchForNotebookAnnotations({targetKeyString, entryId}, abortSignal) {
+ const filter = {
+ "selector": {
+ "$and": [
+ {
+ "model.type": {
+ "$eq": "annotation"
+ }
+ },
+ {
+ "model.annotationType": {
+ "$eq": "NOTEBOOK"
+ }
+ },
+ {
+ "model": {
+ "targets": {
+ }
+ }
+ }
+ ]
+ }
+ };
+ filter.selector.$and[2].model.targets[targetKeyString] = {
+ "entryId": {
+ "$eq": entryId
+ }
+ };
+
+ return this.couchObjectProvider.getObjectsByFilter(filter, abortSignal);
+ }
+
+ searchForTags(tagsArray, abortSignal) {
+ const filter = {
+ "selector": {
+ "$and": [
+ {
+ "model.tags": {
+ "$elemMatch": {
+ "$eq": `${tagsArray[0]}`
+ }
+ }
+ },
+ {
+ "model.type": {
+ "$eq": "annotation"
+ }
+ }
+ ]
+ }
+ };
+
+ return this.couchObjectProvider.getObjectsByFilter(filter, abortSignal);
+ }
+
+}
+export default CouchSearchProvider;
diff --git a/src/plugins/persistence/couch/CouchStatusIndicator.js b/src/plugins/persistence/couch/CouchStatusIndicator.js
new file mode 100644
index 000000000..069a59e22
--- /dev/null
+++ b/src/plugins/persistence/couch/CouchStatusIndicator.js
@@ -0,0 +1,88 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+/**
+ * @typedef {Object} IndicatorState
+ * An object defining the visible state of the indicator.
+ * @property {string} statusClass - The class to apply to the indicator.
+ * @property {string} text - The text to display in the indicator.
+ * @property {string} description - The description to display in the indicator.
+ */
+
+/**
+ * Set of CouchDB connection states; changes among these states will be
+ * reflected in the indicator's appearance.
+ * CONNECTED: Everything nominal, expect to be able to read/write.
+ * DISCONNECTED: HTTP request failed (network error). Unable to reach server at all.
+ * PENDING: Still trying to connect, and haven't failed yet.
+ * MAINTENANCE: CouchDB is connected but not accepting requests.
+ */
+
+/** @type {IndicatorState} */
+export const CONNECTED = {
+ statusClass: "s-status-on",
+ text: "CouchDB is connected",
+ description: "CouchDB is online and accepting requests."
+};
+/** @type {IndicatorState} */
+export const PENDING = {
+ statusClass: "s-status-warning-lo",
+ text: "Attempting to connect to CouchDB...",
+ description: "Checking status of CouchDB, please stand by..."
+};
+/** @type {IndicatorState} */
+export const DISCONNECTED = {
+ statusClass: "s-status-warning-hi",
+ text: "CouchDB is offline",
+ description: "CouchDB is offline and unavailable for requests."
+};
+/** @type {IndicatorState} */
+export const UNKNOWN = {
+ statusClass: "s-status-info",
+ text: "CouchDB connectivity unknown",
+ description: "CouchDB is in an unknown state of connectivity."
+};
+
+export default class CouchStatusIndicator {
+ constructor(simpleIndicator) {
+ this.indicator = simpleIndicator;
+ this.#setDefaults();
+ }
+
+ /**
+ * Set the default values for the indicator.
+ * @private
+ */
+ #setDefaults() {
+ this.setIndicatorToState(PENDING);
+ }
+
+ /**
+ * Set the indicator to the given state.
+ * @param {IndicatorState} state
+ */
+ setIndicatorToState(state) {
+ this.indicator.text(state.text);
+ this.indicator.description(state.description);
+ this.indicator.statusClass(state.statusClass);
+ }
+}
diff --git a/src/plugins/persistence/couch/README.md b/src/plugins/persistence/couch/README.md
index b27cb5df4..fc5cf795b 100644
--- a/src/plugins/persistence/couch/README.md
+++ b/src/plugins/persistence/couch/README.md
@@ -1,11 +1,12 @@
# Introduction
-These instructions are for setting up CouchDB for a **development** environment. For a production environment, we recommend running OpenMCT behind a proxy server (e.g., Nginx or Apache), and securing the CouchDB server properly:
+These instructions are for setting up CouchDB for a **development** environment. For a production environment, we recommend running Open MCT behind a proxy server (e.g., Nginx or Apache), and securing the CouchDB server properly:
https://docs.couchdb.org/en/main/intro/security.html
# Installing CouchDB
-## OSX
-1. Install CouchDB using: `brew install couchdb`
-2. Edit `/usr/local/etc/local.ini` and add and admin password:
+## macOS
+### Installing with admin privileges to your computer
+1. Install CouchDB using: `brew install couchdb`.
+2. Edit `/usr/local/etc/local.ini` and add the following settings:
```
[admins]
admin = youradminpassword
@@ -15,34 +16,37 @@ https://docs.couchdb.org/en/main/intro/security.html
[couchdb]
single_node=true
```
-
-3. Start CouchDB by running: `couchdb`
-4. Add the `_global_changes` database using `curl` (note the `youradminpassword` should be changed to what you set above 👆): `curl -X PUT http://admin:youradminpassword@127.0.0.1:5984/_global_changes`
+ Enable CORS
+ ```
+ [chttpd]
+ enable_cors = true
+ [cors]
+ origins = http://localhost:8080
+ ```
+### Installing without admin privileges to your computer
+1. Install CouchDB following these instructions: https://docs.brew.sh/Installation#untar-anywhere.
+1. Edit `local.ini` in Homebrew's `/etc/` directory as directed above in the 'Installing with admin privileges to your computer' section.
## Other Operating Systems
Follow the installation instructions from the CouchDB installation guide: https://docs.couchdb.org/en/stable/install/index.html
+# Configuring CouchDB
+1. Start CouchDB by running: `couchdb`.
+2. Add the `_global_changes` database using `curl` (note the `youradminpassword` should be changed to what you set above 👆): `curl -X PUT http://admin:youradminpassword@127.0.0.1:5984/_global_changes`
+3. Navigate to http://localhost:5984/_utils
+4. Create a database called `openmct`
+5. Navigate to http://127.0.0.1:5984/_utils/#/database/openmct/permissions
+6. Remove permission restrictions in CouchDB from Open MCT by deleting `_admin` roles for both `Admin` and `Member`.
-# Configuring OpenMCT
-1. Navigate to http://localhost:5984/_utils
-2. Create a database called `openmct`
-3. In your OpenMCT directory, edit `openmct/index.html`, and comment out:
+# Configuring Open MCT
+1. Edit `openmct/index.html` comment out the following line:
```
openmct.install(openmct.plugins.LocalStorage());
```
-Add a line to install the CouchDB plugin for OpenMCT:
+Add a line to install the CouchDB plugin for Open MCT:
```
openmct.install(openmct.plugins.CouchDB("http://localhost:5984/openmct"));
```
-6. Enable cors in CouchDB by editing `/usr/local/etc/local.ini` and add: `
-```
-[chttpd]
-enable_cors = true
-
-[cors]
-origins = http://localhost:8080
-```
-7. Remove permission restrictions in CouchDB from OpenMCT by navigating to http://127.0.0.1:5984/_utils/#/database/openmct/permissions and deleting `_admin` roles for both `Admin` and `Member`.
-8. Start openmct by running `npm start` in the OpenMCT directory.
-9. Navigate to http://localhost:8080/ and create a random object in OpenMCT (e.g., a `Clock`) and save. You may get an error saying that the objects failed to persist. This is a known error that you can ignore, and will only happen the first time you save.
-10. Navigate to: http://127.0.0.1:5984/_utils/#database/openmct/_all_docs
-11. Look at the `JSON` tab and ensure you can see the `Clock` object you created above.
-12. All done! 🏆 \ No newline at end of file
+2. Start Open MCT by running `npm start` in the `openmct` path.
+3. Navigate to http://localhost:8080/ and create a random object in Open MCT (e.g., a 'Clock') and save. You may get an error saying that the object failed to persist - this is a known error that you can ignore, and will only happen the first time you save - just try again.
+4. Navigate to: http://127.0.0.1:5984/_utils/#database/openmct/_all_docs
+5. Look at the 'JSON' tab and ensure you can see the specific object you created above.
+6. All done! 🏆
diff --git a/src/plugins/persistence/couch/plugin.js b/src/plugins/persistence/couch/plugin.js
index f6e967096..eaaed0444 100644
--- a/src/plugins/persistence/couch/plugin.js
+++ b/src/plugins/persistence/couch/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -21,16 +21,24 @@
*****************************************************************************/
import CouchObjectProvider from './CouchObjectProvider';
+import CouchSearchProvider from './CouchSearchProvider';
+import CouchStatusIndicator from './CouchStatusIndicator';
+
const NAMESPACE = '';
const LEGACY_SPACE = 'mct';
+const COUCH_SEARCH_ONLY_NAMESPACE = `COUCH_SEARCH_${Date.now()}`;
export default function CouchPlugin(options) {
return function install(openmct) {
- install.couchProvider = new CouchObjectProvider(openmct, options, NAMESPACE);
+ const simpleIndicator = openmct.indicators.simpleIndicator();
+ openmct.indicators.add(simpleIndicator);
+ const couchStatusIndicator = new CouchStatusIndicator(simpleIndicator);
+ install.couchProvider = new CouchObjectProvider(openmct, options, NAMESPACE, couchStatusIndicator);
// Unfortunately, for historical reasons, Couch DB produces objects with a mix of namepaces (alternately "mct", and "")
// Installing the same provider under both namespaces means that it can respond to object gets for both namespaces.
openmct.objects.addProvider(LEGACY_SPACE, install.couchProvider);
openmct.objects.addProvider(NAMESPACE, install.couchProvider);
+ openmct.objects.addProvider(COUCH_SEARCH_ONLY_NAMESPACE, new CouchSearchProvider(install.couchProvider));
};
}
diff --git a/src/plugins/persistence/couch/pluginSpec.js b/src/plugins/persistence/couch/pluginSpec.js
index 4c1647044..7810f6f3e 100644
--- a/src/plugins/persistence/couch/pluginSpec.js
+++ b/src/plugins/persistence/couch/pluginSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -19,11 +19,13 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
+import Vue from 'vue';
import CouchPlugin from './plugin.js';
import {
createOpenMct,
resetApplicationState, spyOnBuiltins
} from 'utils/testing';
+import { CONNECTED, DISCONNECTED, PENDING, UNKNOWN } from './CouchStatusIndicator';
describe('the plugin', () => {
let openmct;
@@ -74,10 +76,11 @@ describe('the plugin', () => {
spyOn(provider, 'get').and.callThrough();
spyOn(provider, 'create').and.callThrough();
spyOn(provider, 'update').and.callThrough();
- spyOn(provider, 'startSharedWorker').and.callThrough();
+ spyOn(provider, 'observe').and.callThrough();
spyOn(provider, 'fetchChanges').and.callThrough();
spyOn(provider, 'onSharedWorkerMessage').and.callThrough();
spyOn(provider, 'onEventMessage').and.callThrough();
+ spyOn(provider, 'isObservingObjectChanges').and.callThrough();
});
afterEach(() => {
@@ -109,7 +112,7 @@ describe('the plugin', () => {
it('creates an object and starts shared worker', async () => {
const result = await openmct.objects.save(mockDomainObject);
expect(provider.create).toHaveBeenCalled();
- expect(provider.startSharedWorker).toHaveBeenCalled();
+ expect(provider.observe).toHaveBeenCalled();
expect(result).toBeTrue();
});
@@ -149,7 +152,10 @@ describe('the plugin', () => {
mockDomainObject.id = mockDomainObject.identifier.key;
const fakeUpdateEvent = {
- data: JSON.stringify(mockDomainObject)
+ data: JSON.stringify(mockDomainObject),
+ target: {
+ readyState: EventSource.CONNECTED
+ }
};
// eslint-disable-next-line require-await
@@ -165,7 +171,8 @@ describe('the plugin', () => {
const result = await openmct.objects.save(mockDomainObject);
expect(result).toBeTrue();
expect(provider.create).toHaveBeenCalled();
- expect(provider.startSharedWorker).not.toHaveBeenCalled();
+ expect(provider.observe).toHaveBeenCalled();
+ expect(provider.isObservingObjectChanges).toHaveBeenCalled();
//Set modified timestamp it detects a change and persists the updated model.
mockDomainObject.modified = mockDomainObject.persisted + 1;
@@ -176,6 +183,7 @@ describe('the plugin', () => {
expect(updatedResult).toBeTrue();
expect(provider.update).toHaveBeenCalled();
expect(provider.fetchChanges).toHaveBeenCalled();
+ expect(provider.isObservingObjectChanges.calls.mostRecent().returnValue).toBe(true);
sharedWorkerCallback(fakeUpdateEvent);
expect(provider.onEventMessage).toHaveBeenCalled();
@@ -262,7 +270,9 @@ describe('the plugin', () => {
await Promise.all(openmct.objects.search('test'));
const requestUrl = fetch.calls.mostRecent().args[0];
- expect(fetch).toHaveBeenCalled();
+ // we only want one call to fetch, not 2!
+ // see https://github.com/nasa/openmct/issues/4667
+ expect(fetch).toHaveBeenCalledTimes(1);
expect(requestUrl.endsWith('_find')).toBeTrue();
});
@@ -275,3 +285,153 @@ describe('the plugin', () => {
});
});
});
+
+describe('the view', () => {
+ let openmct;
+ let options;
+ let appHolder;
+ let testPath = 'http://localhost:9990/openmct';
+ let provider;
+ let mockDomainObject;
+ beforeEach((done) => {
+ openmct = createOpenMct();
+ spyOnBuiltins(['fetch'], window);
+ options = {
+ url: testPath,
+ filter: {}
+ };
+ mockDomainObject = {
+ identifier: {
+ namespace: '',
+ key: 'some-value'
+ },
+ type: 'notebook',
+ modified: 0
+ };
+ openmct.install(new CouchPlugin(options));
+ appHolder = document.createElement('div');
+ document.body.appendChild(appHolder);
+ openmct.on('start', done);
+ openmct.start(appHolder);
+ provider = openmct.objects.getProvider(mockDomainObject.identifier);
+ spyOn(provider, 'onSharedWorkerMessage').and.callThrough();
+ });
+
+ afterEach(() => {
+ return resetApplicationState(openmct);
+ });
+
+ describe('updates CouchDB status indicator', () => {
+ let mockPromise;
+
+ function assertCouchIndicatorStatus(status) {
+ const indicator = appHolder.querySelector('.c-indicator--simple');
+ expect(indicator).not.toBeNull();
+ expect(indicator).toHaveClass(status.statusClass);
+ expect(indicator.textContent).toMatch(new RegExp(status.text, 'i'));
+ expect(indicator.title).toMatch(new RegExp(status.title, 'i'));
+ }
+
+ it("to 'connected' on successful request", async () => {
+ mockPromise = Promise.resolve({
+ status: 200,
+ json: () => {
+ return {
+ ok: true,
+ _id: 'some-value',
+ id: 'some-value',
+ _rev: 1,
+ model: {}
+ };
+ }
+ });
+ fetch.and.returnValue(mockPromise);
+
+ await openmct.objects.get({
+ namespace: '',
+ key: 'object-1'
+ });
+ await Vue.nextTick();
+
+ assertCouchIndicatorStatus(CONNECTED);
+ });
+
+ it("to 'disconnected' on failed request", async () => {
+ fetch.and.throwError(new TypeError('ERR_CONNECTION_REFUSED'));
+
+ await openmct.objects.get({
+ namespace: '',
+ key: 'object-1'
+ });
+ await Vue.nextTick();
+
+ assertCouchIndicatorStatus(DISCONNECTED);
+ });
+
+ it("to 'pending'", async () => {
+ const workerMessage = {
+ data: {
+ type: 'state',
+ state: 'pending'
+ }
+ };
+ mockPromise = Promise.resolve({
+ status: 200,
+ json: () => {
+ return {
+ ok: true,
+ _id: 'some-value',
+ id: 'some-value',
+ _rev: 1,
+ model: {}
+ };
+ }
+ });
+ fetch.and.returnValue(mockPromise);
+
+ await openmct.objects.get({
+ namespace: '',
+ key: 'object-1'
+ });
+
+ // Simulate 'pending' state from worker message
+ provider.onSharedWorkerMessage(workerMessage);
+ await Vue.nextTick();
+
+ assertCouchIndicatorStatus(PENDING);
+ });
+
+ it("to 'unknown'", async () => {
+ const workerMessage = {
+ data: {
+ type: 'state',
+ state: 'unknown'
+ }
+ };
+ mockPromise = Promise.resolve({
+ status: 200,
+ json: () => {
+ return {
+ ok: true,
+ _id: 'some-value',
+ id: 'some-value',
+ _rev: 1,
+ model: {}
+ };
+ }
+ });
+ fetch.and.returnValue(mockPromise);
+
+ await openmct.objects.get({
+ namespace: '',
+ key: 'object-1'
+ });
+
+ // Simulate 'pending' state from worker message
+ provider.onSharedWorkerMessage(workerMessage);
+ await Vue.nextTick();
+
+ assertCouchIndicatorStatus(UNKNOWN);
+ });
+ });
+});
diff --git a/src/plugins/plan/Plan.vue b/src/plugins/plan/Plan.vue
index 12f68467f..89ad45bdd 100644
--- a/src/plugins/plan/Plan.vue
+++ b/src/plugins/plan/Plan.vue
@@ -1,5 +1,5 @@
<!--
- Open MCT, Copyright (c) 2014-2020, United States Government
+ Open MCT, Copyright (c) 2014-2022, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
@@ -21,8 +21,9 @@
-->
<template>
-<div ref="plan"
- class="c-plan c-timeline-holder"
+<div
+ ref="plan"
+ class="c-plan c-timeline-holder"
>
<template v-if="viewBounds && !options.compact">
<swim-lane>
@@ -36,8 +37,9 @@
/>
</swim-lane>
</template>
- <div ref="planHolder"
- class="c-plan__contents u-contents"
+ <div
+ ref="planHolder"
+ class="c-plan__contents u-contents"
>
</div>
</div>
@@ -47,7 +49,7 @@
import * as d3Scale from 'd3-scale';
import TimelineAxis from "../../ui/components/TimeSystemAxis.vue";
import SwimLane from "@/ui/components/swim-lane/SwimLane.vue";
-import { getValidatedPlan } from "./util";
+import { getValidatedData } from "./util";
import Vue from "vue";
const PADDING = 1;
@@ -159,7 +161,7 @@ export default {
return clientWidth - 200;
},
getPlanData(domainObject) {
- this.planData = getValidatedPlan(domainObject);
+ this.planData = getValidatedData(domainObject);
},
updateViewBounds(bounds) {
if (bounds) {
diff --git a/src/plugins/plan/PlanViewProvider.js b/src/plugins/plan/PlanViewProvider.js
index 485a0fef1..fbfd6a538 100644
--- a/src/plugins/plan/PlanViewProvider.js
+++ b/src/plugins/plan/PlanViewProvider.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -39,7 +39,7 @@ export default function PlanViewProvider(openmct) {
},
canEdit(domainObject) {
- return domainObject.type === 'plan';
+ return false;
},
view: function (domainObject, objectPath) {
diff --git a/src/plugins/plan/inspector/ActivityProperty.vue b/src/plugins/plan/inspector/ActivityProperty.vue
index c21681039..437e249fd 100644
--- a/src/plugins/plan/inspector/ActivityProperty.vue
+++ b/src/plugins/plan/inspector/ActivityProperty.vue
@@ -1,5 +1,5 @@
<!--
- Open MCT, Copyright (c) 2014-2020, United States Government
+ Open MCT, Copyright (c) 2014-2022, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
diff --git a/src/plugins/plan/inspector/PlanActivitiesView.vue b/src/plugins/plan/inspector/PlanActivitiesView.vue
index 1ed17fa6b..394e29b94 100644
--- a/src/plugins/plan/inspector/PlanActivitiesView.vue
+++ b/src/plugins/plan/inspector/PlanActivitiesView.vue
@@ -1,5 +1,5 @@
<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
+ Open MCT, Copyright (c) 2014-2022, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
@@ -21,10 +21,11 @@
-->
<template>
<div class="c-inspector__properties c-inspect-properties">
- <plan-activity-view v-for="activity in activities"
- :key="activity.id"
- :activity="activity"
- :heading="heading"
+ <plan-activity-view
+ v-for="activity in activities"
+ :key="activity.id"
+ :activity="activity"
+ :heading="heading"
/>
</div>
</template>
@@ -32,7 +33,7 @@
<script>
import PlanActivityView from "./PlanActivityView.vue";
import { getPreciseDuration } from "utils/duration";
-import uuid from 'uuid';
+import { v4 as uuid } from 'uuid';
const propertyLabels = {
'start': 'Start DateTime',
diff --git a/src/plugins/plan/inspector/PlanActivityView.vue b/src/plugins/plan/inspector/PlanActivityView.vue
index c28eb8aad..4ae4537ef 100644
--- a/src/plugins/plan/inspector/PlanActivityView.vue
+++ b/src/plugins/plan/inspector/PlanActivityView.vue
@@ -1,5 +1,5 @@
<!--
- Open MCT, Copyright (c) 2014-2020, United States Government
+ Open MCT, Copyright (c) 2014-2022, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
@@ -21,18 +21,21 @@
-->
<template>
-<div v-if="timeProperties.length"
- class="u-contents"
+<div
+ v-if="timeProperties.length"
+ class="u-contents"
>
<div class="c-inspect-properties__header">
{{ heading }}
</div>
- <ul v-for="timeProperty in timeProperties"
+ <ul
+ v-for="timeProperty in timeProperties"
:key="timeProperty.id"
class="c-inspect-properties__section"
>
- <activity-property :label="timeProperty.label"
- :value="timeProperty.value"
+ <activity-property
+ :label="timeProperty.label"
+ :value="timeProperty.value"
/>
</ul>
</div>
@@ -40,7 +43,7 @@
<script>
import ActivityProperty from './ActivityProperty.vue';
-import uuid from 'uuid';
+import { v4 as uuid } from 'uuid';
export default {
components: {
diff --git a/src/plugins/plan/inspector/PlanInspectorViewProvider.js b/src/plugins/plan/inspector/PlanInspectorViewProvider.js
index 34156bbca..d0f118fd4 100644
--- a/src/plugins/plan/inspector/PlanInspectorViewProvider.js
+++ b/src/plugins/plan/inspector/PlanInspectorViewProvider.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/plan/plan.scss b/src/plugins/plan/plan.scss
index fe0bec78f..30de20814 100644
--- a/src/plugins/plan/plan.scss
+++ b/src/plugins/plan/plan.scss
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/plan/plugin.js b/src/plugins/plan/plugin.js
index 4b31d45cd..df418f0e3 100644
--- a/src/plugins/plan/plugin.js
+++ b/src/plugins/plan/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/plan/pluginSpec.js b/src/plugins/plan/pluginSpec.js
index 043a184a8..235901bf5 100644
--- a/src/plugins/plan/pluginSpec.js
+++ b/src/plugins/plan/pluginSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -96,6 +96,18 @@ describe('the plugin', function () {
let planView = applicableViews.find((viewProvider) => viewProvider.key === 'plan.view');
expect(planView).toBeDefined();
});
+
+ it('is not an editable view', () => {
+ const testViewObject = {
+ id: "test-object",
+ type: "plan"
+ };
+ openmct.router.path = [testViewObject];
+
+ const applicableViews = openmct.objectViews.get(testViewObject, [testViewObject]);
+ let planView = applicableViews.find((viewProvider) => viewProvider.key === 'plan.view');
+ expect(planView.canEdit()).toBeFalse();
+ });
});
describe('the plan view displays activities', () => {
diff --git a/src/plugins/plan/util.js b/src/plugins/plan/util.js
index fde072e57..4854f11b4 100644
--- a/src/plugins/plan/util.js
+++ b/src/plugins/plan/util.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -20,8 +20,9 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
-export function getValidatedPlan(domainObject) {
- let body = domainObject.selectFile.body;
+export function getValidatedData(domainObject) {
+ let sourceMap = domainObject.sourceMap;
+ let body = domainObject.selectFile?.body;
let json = {};
if (typeof body === 'string') {
try {
@@ -29,9 +30,37 @@ export function getValidatedPlan(domainObject) {
} catch (e) {
return json;
}
- } else {
+ } else if (body !== undefined) {
json = body;
}
- return json;
+ if (sourceMap !== undefined && sourceMap.activities !== undefined && sourceMap.groupId !== undefined) {
+ let mappedJson = {};
+ json[sourceMap.activities].forEach((activity) => {
+ if (activity[sourceMap.groupId]) {
+ const groupIdKey = activity[sourceMap.groupId];
+ let groupActivity = {
+ ...activity
+ };
+
+ if (sourceMap.start) {
+ groupActivity.start = activity[sourceMap.start];
+ }
+
+ if (sourceMap.end) {
+ groupActivity.end = activity[sourceMap.end];
+ }
+
+ if (!mappedJson[groupIdKey]) {
+ mappedJson[groupIdKey] = [];
+ }
+
+ mappedJson[groupIdKey].push(groupActivity);
+ }
+ });
+
+ return mappedJson;
+ } else {
+ return json;
+ }
}
diff --git a/src/plugins/plot/LinearScale.js b/src/plugins/plot/LinearScale.js
index d151291de..fd0d1aa4f 100644
--- a/src/plugins/plot/LinearScale.js
+++ b/src/plugins/plot/LinearScale.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/plot/MctPlot.vue b/src/plugins/plot/MctPlot.vue
index 1a2314392..81ee4e5dc 100644
--- a/src/plugins/plot/MctPlot.vue
+++ b/src/plugins/plot/MctPlot.vue
@@ -1,5 +1,5 @@
<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
+ Open MCT, Copyright (c) 2014-2022, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
@@ -20,132 +20,176 @@
at runtime from the About dialog for additional information.
-->
<template>
-<div v-if="loaded"
- class="gl-plot"
- :class="[plotLegendExpandedStateClass, plotLegendPositionClass]"
+<div
+ v-if="loaded"
+ class="gl-plot"
+ :class="[plotLegendExpandedStateClass, plotLegendPositionClass]"
>
- <plot-legend :cursor-locked="!!lockHighlightPoint"
- :series="seriesModels"
- :highlights="highlights"
- :legend="legend"
- @legendHoverChanged="legendHoverChanged"
+ <plot-legend
+ v-if="!isNestedWithinAStackedPlot"
+ :cursor-locked="!!lockHighlightPoint"
+ :series="seriesModels"
+ :highlights="highlights"
+ :legend="legend"
+ @legendHoverChanged="legendHoverChanged"
/>
<div class="plot-wrapper-axis-and-display-area flex-elem grows">
- <y-axis v-if="seriesModels.length > 0"
- :tick-width="tickWidth"
- :single-series="seriesModels.length === 1"
- :series-model="seriesModels[0]"
- :style="{
- left: (plotWidth - tickWidth) + 'px'
- }"
- @yKeyChanged="setYAxisKey"
- @tickWidthChanged="onTickWidthChange"
+ <y-axis
+ v-if="seriesModels.length > 0"
+ :tick-width="tickWidth"
+ :single-series="seriesModels.length === 1"
+ :has-same-range-value="hasSameRangeValue"
+ :series-model="seriesModels[0]"
+ :style="{
+ left: (plotWidth - tickWidth) + 'px'
+ }"
+ @yKeyChanged="setYAxisKey"
+ @tickWidthChanged="onTickWidthChange"
/>
- <div class="gl-plot-wrapper-display-area-and-x-axis"
- :style="{
- left: (plotWidth + 20) + 'px'
- }"
+ <div
+ class="gl-plot-wrapper-display-area-and-x-axis"
+ :style="{
+ left: (plotWidth + 20) + 'px'
+ }"
>
<div class="gl-plot-display-area has-local-controls has-cursor-guides">
<div class="l-state-indicators">
- <span class="l-state-indicators__alert-no-lad t-object-alert t-alert-unsynced icon-alert-triangle"
- title="This plot is not currently displaying the latest data. Reset pan/zoom to view latest data."
+ <span
+ class="l-state-indicators__alert-no-lad t-object-alert t-alert-unsynced icon-alert-triangle"
+ title="This plot is not currently displaying the latest data. Reset pan/zoom to view latest data."
></span>
</div>
- <mct-ticks v-show="gridLines && !options.compact"
- :axis-type="'xAxis'"
- :position="'right'"
- @plotTickWidth="onTickWidthChange"
+ <mct-ticks
+ v-show="gridLines && !options.compact"
+ :axis-type="'xAxis'"
+ :position="'right'"
+ @plotTickWidth="onTickWidthChange"
/>
- <mct-ticks v-show="gridLines"
- :axis-type="'yAxis'"
- :position="'bottom'"
- @plotTickWidth="onTickWidthChange"
+ <mct-ticks
+ v-show="gridLines"
+ :axis-type="'yAxis'"
+ :position="'bottom'"
+ @plotTickWidth="onTickWidthChange"
/>
- <div ref="chartContainer"
- class="gl-plot-chart-wrapper"
+ <div
+ ref="chartContainer"
+ class="gl-plot-chart-wrapper"
+ :class="[
+ { 'alt-pressed': altPressed },
+ ]"
>
- <mct-chart :rectangles="rectangles"
- :highlights="highlights"
- :show-limit-line-labels="showLimitLineLabels"
- @plotReinitializeCanvas="initCanvas"
+ <mct-chart
+ :rectangles="rectangles"
+ :highlights="highlights"
+ :show-limit-line-labels="showLimitLineLabels"
+ @plotReinitializeCanvas="initCanvas"
/>
</div>
<div class="gl-plot__local-controls h-local-controls h-local-controls--overlay-content c-local-controls--show-on-hover">
- <div v-if="!options.compact"
- class="c-button-set c-button-set--strip-h js-zoom"
+ <div
+ v-if="!options.compact"
+ class="c-button-set c-button-set--strip-h js-zoom"
>
- <button class="c-button icon-minus"
- title="Zoom out"
- @click="zoom('out', 0.2)"
+ <button
+ class="c-button icon-minus"
+ title="Zoom out"
+ @click="zoom('out', 0.2)"
>
</button>
- <button class="c-button icon-plus"
- title="Zoom in"
- @click="zoom('in', 0.2)"
+ <button
+ class="c-button icon-plus"
+ title="Zoom in"
+ @click="zoom('in', 0.2)"
>
</button>
</div>
- <div v-if="plotHistory.length && !options.compact"
- class="c-button-set c-button-set--strip-h js-pan"
+ <div
+ v-if="plotHistory.length && !options.compact"
+ class="c-button-set c-button-set--strip-h js-pan"
>
- <button class="c-button icon-arrow-left"
- title="Restore previous pan/zoom"
- @click="back()"
+ <button
+ class="c-button icon-arrow-left"
+ title="Restore previous pan/zoom"
+ @click="back()"
>
</button>
- <button class="c-button icon-reset"
- title="Reset pan/zoom"
- @click="clear()"
+ <button
+ class="c-button icon-reset"
+ title="Reset pan/zoom"
+ @click="clear()"
>
</button>
</div>
- <div v-if="isRealTime && !options.compact"
- class="c-button-set c-button-set--strip-h js-pause"
+ <div
+ v-if="isRealTime && !options.compact"
+ class="c-button-set c-button-set--strip-h js-pause"
>
- <button v-if="!isFrozen"
- class="c-button icon-pause"
- title="Pause incoming real-time data"
- @click="pause()"
+ <button
+ v-if="!isFrozen"
+ class="c-button icon-pause"
+ title="Pause incoming real-time data"
+ @click="pause()"
>
</button>
- <button v-if="isFrozen"
- class="c-button icon-arrow-right pause-play is-paused"
- title="Resume displaying real-time data"
- @click="play()"
+ <button
+ v-if="isFrozen"
+ class="c-button icon-arrow-right pause-play is-paused"
+ title="Resume displaying real-time data"
+ @click="play()"
>
</button>
</div>
- <div v-if="isTimeOutOfSync || isFrozen"
- class="c-button-set c-button-set--strip-h"
+ <div
+ v-if="isTimeOutOfSync || isFrozen"
+ class="c-button-set c-button-set--strip-h"
>
- <button class="c-button icon-clock"
- title="Synchronize Time Conductor"
- @click="showSynchronizeDialog()"
+ <button
+ class="c-button icon-clock"
+ title="Synchronize Time Conductor"
+ @click="showSynchronizeDialog()"
+ >
+ </button>
+ </div>
+ <div class="c-button-set c-button-set--strip-h">
+ <button
+ class="c-button icon-crosshair"
+ :class="{ 'is-active': cursorGuide }"
+ title="Toggle cursor guides"
+ @click="toggleCursorGuide"
+ >
+ </button>
+ <button
+ class="c-button"
+ :class="{ 'icon-grid-on': gridLines, 'icon-grid-off': !gridLines }"
+ title="Toggle grid lines"
+ @click="toggleGridLines"
>
</button>
</div>
</div>
<!--Cursor guides-->
- <div v-show="cursorGuide"
- ref="cursorGuideVertical"
- class="c-cursor-guide--v js-cursor-guide--v"
+ <div
+ v-show="cursorGuide"
+ ref="cursorGuideVertical"
+ class="c-cursor-guide--v js-cursor-guide--v"
>
</div>
- <div v-show="cursorGuide"
- ref="cursorGuideHorizontal"
- class="c-cursor-guide--h js-cursor-guide--h"
+ <div
+ v-show="cursorGuide"
+ ref="cursorGuideHorizontal"
+ class="c-cursor-guide--h js-cursor-guide--h"
>
</div>
</div>
- <x-axis v-if="seriesModels.length > 0 && !options.compact"
- :series-model="seriesModels[0]"
+ <x-axis
+ v-if="seriesModels.length > 0 && !options.compact"
+ :series-model="seriesModels[0]"
/>
</div>
@@ -186,16 +230,16 @@ export default {
};
}
},
- gridLines: {
+ initGridLines: {
type: Boolean,
default() {
return true;
}
},
- cursorGuide: {
+ initCursorGuide: {
type: Boolean,
default() {
- return true;
+ return false;
}
},
plotTickWidth: {
@@ -203,10 +247,23 @@ export default {
default() {
return 0;
}
+ },
+ limitLineLabels: {
+ type: Object,
+ default() {
+ return {};
+ }
+ },
+ colorPalette: {
+ type: Object,
+ default() {
+ return undefined;
+ }
}
},
data() {
return {
+ altPressed: false,
highlights: [],
lockHighlightPoint: false,
tickWidth: 0,
@@ -222,19 +279,30 @@ export default {
isRealTime: this.openmct.time.clock() !== undefined,
loaded: false,
isTimeOutOfSync: false,
- showLimitLineLabels: undefined,
+ showLimitLineLabels: this.limitLineLabels,
isFrozenOnMouseDown: false,
- hasSameRangeValue: true
+ hasSameRangeValue: true,
+ cursorGuide: this.initCursorGuide,
+ gridLines: this.initGridLines
};
},
computed: {
+ isNestedWithinAStackedPlot() {
+ const isNavigatedObject = this.openmct.router.isNavigatedObject([this.domainObject].concat(this.path));
+
+ return !isNavigatedObject && this.path.find((pathObject, pathObjIndex) => pathObject.type === 'telemetry.plot.stacked');
+ },
isFrozen() {
return this.config.xAxis.get('frozen') === true && this.config.yAxis.get('frozen') === true;
},
plotLegendPositionClass() {
- return `plot-legend-${this.config.legend.get('position')}`;
+ return !this.isNestedWithinAStackedPlot ? `plot-legend-${this.config.legend.get('position')}` : '';
},
plotLegendExpandedStateClass() {
+ if (this.isNestedWithinAStackedPlot) {
+ return '';
+ }
+
if (this.config.legend.get('expanded')) {
return 'plot-legend-expanded';
} else {
@@ -245,7 +313,23 @@ export default {
return this.plotTickWidth || this.tickWidth;
}
},
+ watch: {
+ limitLineLabels: {
+ handler(limitLineLabels) {
+ this.legendHoverChanged(limitLineLabels);
+ },
+ deep: true
+ },
+ initGridLines(newGridLines) {
+ this.gridLines = newGridLines;
+ },
+ initCursorGuide(newCursorGuide) {
+ this.cursorGuide = newCursorGuide;
+ }
+ },
mounted() {
+ document.addEventListener('keydown', this.handleKeyDown);
+ document.addEventListener('keyup', this.handleKeyUp);
eventHelpers.extend(this);
this.updateRealTime = this.updateRealTime.bind(this);
this.updateDisplayBounds = this.updateDisplayBounds.bind(this);
@@ -254,6 +338,11 @@ export default {
this.config = this.getConfig();
this.legend = this.config.legend;
+ if (this.isNestedWithinAStackedPlot) {
+ const configId = this.openmct.objects.makeKeyString(this.domainObject.identifier);
+ this.$emit('configLoaded', configId);
+ }
+
this.listenTo(this.config.series, 'add', this.addSeries, this);
this.listenTo(this.config.series, 'remove', this.removeSeries, this);
@@ -277,9 +366,21 @@ export default {
},
beforeDestroy() {
+ document.removeEventListener('keydown', this.handleKeyDown);
+ document.removeEventListener('keyup', this.handleKeyUp);
this.destroy();
},
methods: {
+ handleKeyDown(event) {
+ if (event.key === 'Alt') {
+ this.altPressed = true;
+ }
+ },
+ handleKeyUp(event) {
+ if (event.key === 'Alt') {
+ this.altPressed = false;
+ }
+ },
setTimeContext() {
this.stopFollowingTimeContext();
@@ -307,6 +408,7 @@ export default {
id: configId,
domainObject: this.domainObject,
openmct: this.openmct,
+ palette: this.colorPalette,
callback: (data) => {
this.data = data;
}
@@ -382,7 +484,7 @@ export default {
end: range.max,
domain: this.config.xAxis.get('key')
})
- .then(this.stopLoading());
+ .then(this.stopLoading.bind(this));
if (purge) {
plotSeries.purgeRecordsOutsideRange(range);
}
@@ -457,7 +559,7 @@ export default {
},
setDisplayRange(series, xKey) {
- if (this.config.series.model.length !== 1) {
+ if (this.config.series.models.length !== 1) {
return;
}
@@ -630,7 +732,7 @@ export default {
this.positionOverElement = {
x: event.clientX - this.chartElementBounds.left,
y: this.chartElementBounds.height
- - (event.clientY - this.chartElementBounds.top)
+ - (event.clientY - this.chartElementBounds.top)
};
this.positionOverPlot = {
@@ -690,6 +792,8 @@ export default {
};
});
}
+
+ this.$emit('highlights', this.highlights);
},
untrackMousePosition() {
@@ -724,6 +828,7 @@ export default {
if (this.isMouseClick()) {
this.lockHighlightPoint = !this.lockHighlightPoint;
+ this.$emit('lockHighlightPoint', this.lockHighlightPoint);
}
if (this.pan) {
@@ -1088,6 +1193,14 @@ export default {
},
legendHoverChanged(data) {
this.showLimitLineLabels = data;
+ },
+ toggleCursorGuide() {
+ this.cursorGuide = !this.cursorGuide;
+ this.$emit('cursorGuide', this.cursorGuide);
+ },
+ toggleGridLines() {
+ this.gridLines = !this.gridLines;
+ this.$emit('gridLines', this.gridLines);
}
}
};
diff --git a/src/plugins/plot/MctTicks.vue b/src/plugins/plot/MctTicks.vue
index fa1a0f398..ab09cb6d1 100644
--- a/src/plugins/plot/MctTicks.vue
+++ b/src/plugins/plot/MctTicks.vue
@@ -1,5 +1,5 @@
<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
+ Open MCT, Copyright (c) 2014-2022, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
@@ -21,53 +21,60 @@
-->
<template>
-<div ref="tickContainer"
- class="u-contents js-ticks"
+<div
+ ref="tickContainer"
+ class="u-contents js-ticks"
>
- <div v-if="position === 'left'"
- class="gl-plot-tick-wrapper"
+ <div
+ v-if="position === 'left'"
+ class="gl-plot-tick-wrapper"
>
- <div v-for="tick in ticks"
- :key="tick.value"
- class="gl-plot-tick gl-plot-x-tick-label"
- :style="{
- left: (100 * (tick.value - min) / interval) + '%'
- }"
- :title="tick.fullText || tick.text"
+ <div
+ v-for="(tick, i) in ticks"
+ :key="'tick-left' + i"
+ class="gl-plot-tick gl-plot-x-tick-label"
+ :style="{
+ left: (100 * (tick.value - min) / interval) + '%'
+ }"
+ :title="tick.fullText || tick.text"
>
{{ tick.text }}
</div>
</div>
- <div v-if="position === 'top'"
- class="gl-plot-tick-wrapper"
+ <div
+ v-if="position === 'top'"
+ class="gl-plot-tick-wrapper"
>
- <div v-for="tick in ticks"
- :key="tick.value"
- class="gl-plot-tick gl-plot-y-tick-label"
- :style="{ top: (100 * (max - tick.value) / interval) + '%' }"
- :title="tick.fullText || tick.text"
- style="margin-top: -0.50em; direction: ltr;"
+ <div
+ v-for="(tick, i) in ticks"
+ :key="'tick-top' + i"
+ class="gl-plot-tick gl-plot-y-tick-label"
+ :style="{ top: (100 * (max - tick.value) / interval) + '%' }"
+ :title="tick.fullText || tick.text"
+ style="margin-top: -0.50em; direction: ltr;"
>
<span>{{ tick.text }}</span>
</div>
</div>
<!-- grid lines follow -->
<template v-if="position === 'right'">
- <div v-for="tick in ticks"
- :key="tick.value"
- class="gl-plot-hash hash-v"
- :style="{
- right: (100 * (max - tick.value) / interval) + '%',
- height: '100%'
- }"
+ <div
+ v-for="(tick, i) in ticks"
+ :key="'tick-right' + i"
+ class="gl-plot-hash hash-v"
+ :style="{
+ right: (100 * (max - tick.value) / interval) + '%',
+ height: '100%'
+ }"
>
</div>
</template>
<template v-if="position === 'bottom'">
- <div v-for="tick in ticks"
- :key="tick.value"
- class="gl-plot-hash hash-h"
- :style="{ bottom: (100 * (tick.value - min) / interval) + '%', width: '100%' }"
+ <div
+ v-for="(tick, i) in ticks"
+ :key="'tick-bottom' + i"
+ class="gl-plot-hash hash-h"
+ :style="{ bottom: (100 * (tick.value - min) / interval) + '%', width: '100%' }"
>
</div>
</template>
@@ -76,7 +83,7 @@
<script>
import eventHelpers from "./lib/eventHelpers";
-import { ticks, getFormattedTicks } from "./tickUtils";
+import { ticks, getLogTicks, getFormattedTicks } from "./tickUtils";
import configStore from "./configuration/ConfigStore";
export default {
@@ -89,6 +96,13 @@ export default {
},
required: true
},
+ // Make it a prop, then later we can allow user to change it via UI input
+ tickCount: {
+ type: Number,
+ default() {
+ return 6;
+ }
+ },
position: {
required: true,
type: String,
@@ -105,9 +119,12 @@ export default {
mounted() {
eventHelpers.extend(this);
+ if (!this.axisType) {
+ throw new Error("axis-type prop expected");
+ }
+
this.axis = this.getAxisFromConfig();
- this.tickCount = 4;
this.tickUpdate = false;
this.listenTo(this.axis, 'change:displayRange', this.updateTicks, this);
this.listenTo(this.axis, 'change:format', this.updateTicks, this);
@@ -119,15 +136,16 @@ export default {
},
methods: {
getAxisFromConfig() {
- if (!this.axisType) {
- return;
- }
-
const configId = this.openmct.objects.makeKeyString(this.domainObject.identifier);
+
+ /** @type {import('./configuration/PlotConfigurationModel').default} */
let config = configStore.get(configId);
- if (config) {
- return config[this.axisType];
+
+ if (!config) {
+ throw new Error('config is missing');
}
+
+ return config[this.axisType];
},
/**
* Determine whether ticks should be regenerated for a given range.
@@ -172,7 +190,11 @@ export default {
}, this);
}
- return ticks(range.min, range.max, number);
+ if (this.axisType === 'yAxis' && this.axis.get('logMode')) {
+ return getLogTicks(range.min, range.max, number, 4);
+ } else {
+ return ticks(range.min, range.max, number);
+ }
},
updateTicksForceRegeneration() {
@@ -181,6 +203,7 @@ export default {
updateTicks(forceRegeneration = false) {
const range = this.axis.get('displayRange');
+
if (!range) {
delete this.min;
delete this.max;
@@ -203,8 +226,8 @@ export default {
if (this.shouldRegenerateTicks(range, forceRegeneration)) {
let newTicks = this.getTicks();
this.tickRange = {
- min: Math.min.apply(Math, newTicks),
- max: Math.max.apply(Math, newTicks),
+ min: Math.min(...newTicks),
+ max: Math.max(...newTicks),
step: newTicks[1] - newTicks[0]
};
diff --git a/src/plugins/plot/Plot.vue b/src/plugins/plot/Plot.vue
index 79ee10bbf..06fb9da3d 100644
--- a/src/plugins/plot/Plot.vue
+++ b/src/plugins/plot/Plot.vue
@@ -1,5 +1,5 @@
<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
+ Open MCT, Copyright (c) 2014-2022, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
@@ -20,52 +20,26 @@
at runtime from the About dialog for additional information.
-->
<template>
-<div ref="plotWrapper"
- class="c-plot holder holder-plot has-control-bar"
+<div
+ ref="plotWrapper"
+ class="c-plot holder holder-plot has-control-bar"
>
- <div v-if="!options.compact"
- class="c-control-bar"
+ <div
+ ref="plotContainer"
+ class="l-view-section u-style-receiver js-style-receiver"
+ :class="{'s-status-timeconductor-unsynced': status && status === 'timeconductor-unsynced'}"
>
- <span class="c-button-set c-button-set--strip-h">
- <button class="c-button icon-download"
- title="Export This View's Data as PNG"
- @click="exportPNG()"
- >
- <span class="c-button__label">PNG</span>
- </button>
- <button class="c-button"
- title="Export This View's Data as JPG"
- @click="exportJPG()"
- >
- <span class="c-button__label">JPG</span>
- </button>
- </span>
- <button class="c-button icon-crosshair"
- :class="{ 'is-active': cursorGuide }"
- title="Toggle cursor guides"
- @click="toggleCursorGuide"
- >
- </button>
- <button class="c-button"
- :class="{ 'icon-grid-on': gridLines, 'icon-grid-off': !gridLines }"
- title="Toggle grid lines"
- @click="toggleGridLines"
- >
- </button>
- </div>
-
- <div ref="plotContainer"
- class="l-view-section u-style-receiver js-style-receiver"
- :class="{'s-status-timeconductor-unsynced': status && status === 'timeconductor-unsynced'}"
- >
- <div v-show="!!loading"
- class="c-loading--overlay loading"
- ></div>
- <mct-plot :grid-lines="gridLines"
- :cursor-guide="cursorGuide"
- :options="options"
- @loadingUpdated="loadingUpdated"
- @statusUpdated="setStatus"
+ <progress-bar
+ v-show="!!loading"
+ class="c-telemetry-table__progress-bar"
+ :model="{progressPerc: undefined}"
+ />
+ <mct-plot
+ :init-grid-lines="gridLines"
+ :init-cursor-guide="cursorGuide"
+ :options="options"
+ @loadingUpdated="loadingUpdated"
+ @statusUpdated="setStatus"
/>
</div>
</div>
@@ -75,10 +49,12 @@
import eventHelpers from './lib/eventHelpers';
import ImageExporter from '../../exporters/ImageExporter';
import MctPlot from './MctPlot.vue';
+import ProgressBar from "../../ui/components/ProgressBar.vue";
export default {
components: {
- MctPlot
+ MctPlot,
+ ProgressBar
},
inject: ['openmct', 'domainObject', 'path'],
props: {
@@ -115,26 +91,22 @@ export default {
destroy() {
this.stopListening();
},
-
exportJPG() {
const plotElement = this.$refs.plotContainer;
this.imageExporter.exportJPG(plotElement, 'plot.jpg', 'export-plot');
},
-
exportPNG() {
const plotElement = this.$refs.plotContainer;
this.imageExporter.exportPNG(plotElement, 'plot.png', 'export-plot');
},
-
- toggleCursorGuide() {
- this.cursorGuide = !this.cursorGuide;
- },
-
- toggleGridLines() {
- this.gridLines = !this.gridLines;
- },
setStatus(status) {
this.status = status;
+ },
+ getViewContext() {
+ return {
+ exportPNG: this.exportPNG,
+ exportJPG: this.exportJPG
+ };
}
}
};
diff --git a/src/plugins/plot/PlotViewProvider.js b/src/plugins/plot/PlotViewProvider.js
index d92975d36..8d0d862a2 100644
--- a/src/plugins/plot/PlotViewProvider.js
+++ b/src/plugins/plot/PlotViewProvider.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -80,9 +80,16 @@ export default function PlotViewProvider(openmct) {
}
};
},
- template: '<plot :options="options"></plot>'
+ template: '<plot ref="plotComponent" :options="options"></plot>'
});
},
+ getViewContext() {
+ if (!component) {
+ return {};
+ }
+
+ return component.$refs.plotComponent.getViewContext();
+ },
destroy: function () {
component.$destroy();
component = undefined;
diff --git a/platform/entanglement/src/policies/CopyPolicy.js b/src/plugins/plot/actions/ViewActions.js
index 4013863ab..d8dbd4b0a 100644
--- a/platform/entanglement/src/policies/CopyPolicy.js
+++ b/src/plugins/plot/actions/ViewActions.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -19,38 +19,39 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
-
-define([], function () {
-
- /**
- * Disallow duplication when the object to be duplicated is not
- * creatable.
- * @constructor
- * @implements {Policy}
- * @memberof platform/entanglement
- */
- function CopyPolicy() {
+import {isPlotView} from "@/plugins/plot/actions/utils";
+
+const exportPNG = {
+ name: 'Export as PNG',
+ key: 'export-as-png',
+ description: 'Export This View\'s Data as PNG',
+ cssClass: 'icon-download',
+ group: 'view',
+ invoke(objectPath, view) {
+ view.getViewContext().exportPNG();
}
-
- function allowCreation(domainObject) {
- var type = domainObject && domainObject.getCapability('type');
-
- return Boolean(type && type.hasFeature('creation'));
+};
+
+const exportJPG = {
+ name: 'Export as JPG',
+ key: 'export-as-jpg',
+ description: 'Export This View\'s Data as JPG',
+ cssClass: 'icon-download',
+ group: 'view',
+ invoke(objectPath, view) {
+ view.getViewContext().exportJPG();
}
+};
- function selectedObject(context) {
- return context.selectedObject || context.domainObject;
- }
+const viewActions = [
+ exportPNG,
+ exportJPG
+];
- CopyPolicy.prototype.allow = function (action, context) {
- var key = action.getMetadata().key;
-
- if (key === 'copy') {
- return allowCreation(selectedObject(context));
- }
-
- return true;
+viewActions.forEach(action => {
+ action.appliesTo = (objectPath, view = {}) => {
+ return isPlotView(view);
};
-
- return CopyPolicy;
});
+
+export default viewActions;
diff --git a/src/plugins/plot/actions/utils.js b/src/plugins/plot/actions/utils.js
new file mode 100644
index 000000000..2bebbecf4
--- /dev/null
+++ b/src/plugins/plot/actions/utils.js
@@ -0,0 +1,3 @@
+export function isPlotView(view) {
+ return view.key === 'plot-single' || view.key === 'plot-overlay' || view.key === 'plot-stacked';
+}
diff --git a/src/plugins/plot/axis/XAxis.vue b/src/plugins/plot/axis/XAxis.vue
index 93bcdddcc..a6bc176aa 100644
--- a/src/plugins/plot/axis/XAxis.vue
+++ b/src/plugins/plot/axis/XAxis.vue
@@ -1,5 +1,5 @@
<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
+ Open MCT, Copyright (c) 2014-2022, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
@@ -21,12 +21,14 @@
-->
<template>
-<div v-if="loaded"
- class="gl-plot-axis-area gl-plot-x has-local-controls"
+<div
+ v-if="loaded"
+ class="gl-plot-axis-area gl-plot-x has-local-controls"
>
- <mct-ticks :axis-type="'xAxis'"
- :position="'left'"
- @plotTickWidth="onTickWidthChange"
+ <mct-ticks
+ :axis-type="'xAxis'"
+ :position="'left'"
+ @plotTickWidth="onTickWidthChange"
/>
<div
@@ -42,9 +44,10 @@
class="gl-plot-x-label__select local-controls--hidden"
@change="toggleXKeyOption()"
>
- <option v-for="option in xKeyOptions"
- :key="option.key"
- :value="option.key"
+ <option
+ v-for="option in xKeyOptions"
+ :key="option.key"
+ :value="option.key"
>{{ option.name }}
</option>
</select>
@@ -132,17 +135,21 @@ export default {
},
setUpXAxisOptions() {
const xAxisKey = this.xAxis.get('key');
+ this.xKeyOptions = [];
+
+ if (this.seriesModel.metadata) {
+ this.xKeyOptions = this.seriesModel.metadata
+ .valuesForHints(['domain'])
+ .map(function (o) {
+ return {
+ name: o.name,
+ key: o.key
+ };
+ });
+ }
- this.xKeyOptions = this.seriesModel.metadata
- .valuesForHints(['domain'])
- .map(function (o) {
- return {
- name: o.name,
- key: o.key
- };
- });
this.xAxisLabel = this.xAxis.get('label');
- this.selectedXKeyOptionKey = this.getXKeyOption(xAxisKey).key;
+ this.selectedXKeyOptionKey = this.xKeyOptions.length > 0 ? this.getXKeyOption(xAxisKey).key : xAxisKey;
},
onTickWidthChange(width) {
this.$emit('tickWidthChanged', width);
diff --git a/src/plugins/plot/axis/YAxis.vue b/src/plugins/plot/axis/YAxis.vue
index 09840c704..6e170fbd6 100644
--- a/src/plugins/plot/axis/YAxis.vue
+++ b/src/plugins/plot/axis/YAxis.vue
@@ -1,5 +1,5 @@
<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
+ Open MCT, Copyright (c) 2014-2022, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
@@ -20,11 +20,12 @@
at runtime from the About dialog for additional information.
-->
<template>
-<div v-if="loaded"
- class="gl-plot-axis-area gl-plot-y has-local-controls"
- :style="{
- width: (tickWidth + 20) + 'px'
- }"
+<div
+ v-if="loaded"
+ class="gl-plot-axis-area gl-plot-y has-local-controls"
+ :style="{
+ width: (tickWidth + 20) + 'px'
+ }"
>
<div
@@ -34,24 +35,27 @@
>{{ yAxisLabel }}
</div>
- <select v-if="yKeyOptions.length > 1 && singleSeries"
- v-model="yAxisLabel"
- class="gl-plot-y-label__select local-controls--hidden"
- @change="toggleYAxisLabel"
+ <select
+ v-if="yKeyOptions.length > 1 && singleSeries"
+ v-model="yAxisLabel"
+ class="gl-plot-y-label__select local-controls--hidden"
+ @change="toggleYAxisLabel"
>
- <option v-for="(option, index) in yKeyOptions"
- :key="index"
- :value="option.name"
- :selected="option.name === yAxisLabel"
+ <option
+ v-for="(option, index) in yKeyOptions"
+ :key="index"
+ :value="option.name"
+ :selected="option.name === yAxisLabel"
>
{{ option.name }}
</option>
</select>
- <mct-ticks :axis-type="'yAxis'"
- class="gl-plot-ticks"
- :position="'top'"
- @plotTickWidth="onTickWidthChange"
+ <mct-ticks
+ :axis-type="'yAxis'"
+ class="gl-plot-ticks"
+ :position="'top'"
+ @plotTickWidth="onTickWidthChange"
/>
</div>
</template>
@@ -116,21 +120,25 @@ export default {
}
},
setUpYAxisOptions() {
- this.yKeyOptions = this.seriesModel.metadata
- .valuesForHints(['range'])
- .map(function (o) {
- return {
- name: o.name,
- key: o.key
- };
- });
+ this.yKeyOptions = [];
+
+ if (this.seriesModel.metadata) {
+ this.yKeyOptions = this.seriesModel.metadata
+ .valuesForHints(['range'])
+ .map(function (o) {
+ return {
+ name: o.name,
+ key: o.key
+ };
+ });
+ }
// set yAxisLabel if none is set yet
if (this.yAxisLabel === 'none') {
let yKey = this.seriesModel.model.yKey;
let yKeyModel = this.yKeyOptions.filter(o => o.key === yKey)[0];
- this.yAxisLabel = yKeyModel.name;
+ this.yAxisLabel = yKeyModel ? yKeyModel.name : '';
}
},
toggleYAxisLabel() {
diff --git a/src/plugins/plot/chart/LimitLabel.vue b/src/plugins/plot/chart/LimitLabel.vue
index dff4429b3..2bb2c2f24 100644
--- a/src/plugins/plot/chart/LimitLabel.vue
+++ b/src/plugins/plot/chart/LimitLabel.vue
@@ -1,14 +1,16 @@
<template>
-<div class="c-plot-limit"
- :style="styleObj"
- :class="limitClass"
+<div
+ class="c-plot-limit"
+ :style="styleObj"
+ :class="limitClass"
>
<div class="c-plot-limit__label">
<span class="c-plot-limit__direction-icon"></span>
<span class="c-plot-limit__severity-icon"></span>
<span class="c-plot-limit__limit-value">{{ limit.value }}</span>
- <span class="c-plot-limit__series-color-swatch"
- :style="{ 'background-color': limit.seriesColor }"
+ <span
+ class="c-plot-limit__series-color-swatch"
+ :style="{ 'background-color': limit.seriesColor }"
></span>
<span class="c-plot-limit__series-name">{{ limit.name }}</span>
</div>
diff --git a/src/plugins/plot/chart/LimitLine.vue b/src/plugins/plot/chart/LimitLine.vue
index 7fe45be29..1d1118924 100644
--- a/src/plugins/plot/chart/LimitLine.vue
+++ b/src/plugins/plot/chart/LimitLine.vue
@@ -1,7 +1,8 @@
<template>
-<div :style="styleObj"
- class="c-plot-limit-line js-limit-line"
- :class="limitClass"
+<div
+ :style="styleObj"
+ class="c-plot-limit-line js-limit-line"
+ :class="limitClass"
></div>
</template>
diff --git a/src/plugins/plot/chart/MCTChartAlarmLineSet.js b/src/plugins/plot/chart/MCTChartAlarmLineSet.js
index 918833e73..351494184 100644
--- a/src/plugins/plot/chart/MCTChartAlarmLineSet.js
+++ b/src/plugins/plot/chart/MCTChartAlarmLineSet.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -23,6 +23,9 @@
import eventHelpers from '../lib/eventHelpers';
export default class MCTChartAlarmLineSet {
+ /**
+ * @param {Bounds} bounds
+ */
constructor(series, chart, offset, bounds) {
this.series = series;
this.chart = chart;
@@ -40,6 +43,9 @@ export default class MCTChartAlarmLineSet {
}
}
+ /**
+ * @param {Bounds} bounds
+ */
updateBounds(bounds) {
this.bounds = bounds;
this.getLimitPoints(this.series);
@@ -106,3 +112,7 @@ export default class MCTChartAlarmLineSet {
}
}
+
+/**
+@typedef {import('@/api/time/TimeContext').Bounds} Bounds
+*/
diff --git a/src/plugins/plot/chart/MCTChartAlarmPointSet.js b/src/plugins/plot/chart/MCTChartAlarmPointSet.js
index 58a6a24b1..c4c206750 100644
--- a/src/plugins/plot/chart/MCTChartAlarmPointSet.js
+++ b/src/plugins/plot/chart/MCTChartAlarmPointSet.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/plot/chart/MCTChartLineLinear.js b/src/plugins/plot/chart/MCTChartLineLinear.js
index bc57d8465..7226abaa6 100644
--- a/src/plugins/plot/chart/MCTChartLineLinear.js
+++ b/src/plugins/plot/chart/MCTChartLineLinear.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -23,7 +23,7 @@
import MCTChartSeriesElement from './MCTChartSeriesElement';
export default class MCTChartLineLinear extends MCTChartSeriesElement {
- addPoint(point, start, count) {
+ addPoint(point, start) {
this.buffer[start] = point.x;
this.buffer[start + 1] = point.y;
}
diff --git a/src/plugins/plot/chart/MCTChartLineStepAfter.js b/src/plugins/plot/chart/MCTChartLineStepAfter.js
index d872c1d8a..e9111c56a 100644
--- a/src/plugins/plot/chart/MCTChartLineStepAfter.js
+++ b/src/plugins/plot/chart/MCTChartLineStepAfter.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -23,7 +23,7 @@
import MCTChartSeriesElement from './MCTChartSeriesElement';
export default class MCTChartLineStepAfter extends MCTChartSeriesElement {
- removePoint(point, index, count) {
+ removePoint(index) {
if (index > 0 && index / 2 < this.count) {
this.buffer[index + 1] = this.buffer[index - 1];
}
@@ -45,7 +45,7 @@ export default class MCTChartLineStepAfter extends MCTChartSeriesElement {
return 2 + ((index - 1) * 4);
}
- addPoint(point, start, count) {
+ addPoint(point, start) {
if (start === 0 && this.count === 0) {
// First point is easy.
this.buffer[start] = point.x;
diff --git a/src/plugins/plot/chart/MCTChartPointSet.js b/src/plugins/plot/chart/MCTChartPointSet.js
index 190075ee0..b7bb40ec9 100644
--- a/src/plugins/plot/chart/MCTChartPointSet.js
+++ b/src/plugins/plot/chart/MCTChartPointSet.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -24,7 +24,7 @@ import MCTChartSeriesElement from './MCTChartSeriesElement';
// TODO: Is this needed? This is identical to MCTChartLineLinear. Why is it a different class?
export default class MCTChartPointSet extends MCTChartSeriesElement {
- addPoint(point, start, count) {
+ addPoint(point, start) {
this.buffer[start] = point.x;
this.buffer[start + 1] = point.y;
}
diff --git a/src/plugins/plot/chart/MCTChartSeriesElement.js b/src/plugins/plot/chart/MCTChartSeriesElement.js
index 116a3ea74..20a2189ef 100644
--- a/src/plugins/plot/chart/MCTChartSeriesElement.js
+++ b/src/plugins/plot/chart/MCTChartSeriesElement.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -22,6 +22,7 @@
import eventHelpers from '../lib/eventHelpers';
+/** @abstract */
export default class MCTChartSeriesElement {
constructor(series, chart, offset) {
this.series = series;
@@ -72,9 +73,11 @@ export default class MCTChartSeriesElement {
}
}
- removePoint(point, index, count) {
- // by default, do nothing.
- }
+ /** @abstract */
+ removePoint(index) {}
+
+ /** @abstract */
+ addPoint(point, index) {}
remove(point, index, series) {
const vertexCount = this.vertexCountForPointAtIndex(index);
@@ -82,11 +85,10 @@ export default class MCTChartSeriesElement {
this.removeSegments(removalPoint, vertexCount);
- this.removePoint(
- this.makePoint(point, series),
- removalPoint,
- vertexCount
- );
+ // TODO useless makePoint call?
+ this.makePoint(point, series);
+ this.removePoint(removalPoint);
+
this.count -= (vertexCount / 2);
}
@@ -106,11 +108,7 @@ export default class MCTChartSeriesElement {
const insertionPoint = this.startIndexForPointAtIndex(index);
this.growIfNeeded(pointsRequired);
this.makeInsertionPoint(insertionPoint, pointsRequired);
- this.addPoint(
- this.makePoint(point, series),
- insertionPoint,
- pointsRequired
- );
+ this.addPoint(this.makePoint(point, series), insertionPoint);
this.count += (pointsRequired / 2);
}
@@ -155,3 +153,18 @@ export default class MCTChartSeriesElement {
}
}
+
+/** @typedef {any} TODO */
+
+/** @typedef {import('../configuration/PlotSeries').default} PlotSeries */
+
+/**
+@typedef {{
+ x: (x: number) => number
+ y: (y: number) => number
+ xVal: (point: Point, pSeries: PlotSeries) => number
+ yVal: (point: Point, pSeries: PlotSeries) => number
+ xKey: TODO
+ yKey: TODO
+}} Offset
+*/
diff --git a/src/plugins/plot/chart/MctChart.vue b/src/plugins/plot/chart/MctChart.vue
index 06f5672d0..2ec6d74f5 100644
--- a/src/plugins/plot/chart/MctChart.vue
+++ b/src/plugins/plot/chart/MctChart.vue
@@ -1,5 +1,5 @@
<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
+ Open MCT, Copyright (c) 2014-2022, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
@@ -26,8 +26,9 @@
<div class="gl-plot-chart-area">
<span v-html="canvasTemplate"></span>
<span v-html="canvasTemplate"></span>
- <div ref="limitArea"
- class="js-limit-area"
+ <div
+ ref="limitArea"
+ class="js-limit-area"
></div>
</div>
</template>
diff --git a/src/plugins/plot/configuration/Collection.js b/src/plugins/plot/configuration/Collection.js
index 98f91a92c..57101ff2d 100644
--- a/src/plugins/plot/configuration/Collection.js
+++ b/src/plugins/plot/configuration/Collection.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -21,11 +21,17 @@
*****************************************************************************/
import Model from './Model';
+/**
+ * @template {object} T
+ * @template {object} O
+ * @extends {Model<T, O>}
+ */
export default class Collection extends Model {
+ /** @type {Constructor} */
+ modelClass = Model;
initialize(options) {
super.initialize(options);
- this.modelClass = Model;
if (options.models) {
this.models = options.models.map(this.modelFn, this);
} else {
@@ -107,3 +113,7 @@ export default class Collection extends Model {
this.stopListening();
}
}
+
+/** @typedef {any} TODO */
+
+/** @typedef {new (...args: any[]) => object} Constructor */
diff --git a/src/plugins/plot/configuration/ConfigStore.js b/src/plugins/plot/configuration/ConfigStore.js
index c7e07a82e..fe1a1a127 100644
--- a/src/plugins/plot/configuration/ConfigStore.js
+++ b/src/plugins/plot/configuration/ConfigStore.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -19,32 +19,47 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
-function ConfigStore() {
- this.store = {};
-}
+class ConfigStore {
+ /** @type {Record<string, Destroyable>} */
+ store = {};
-ConfigStore.prototype.deleteStore = function (id) {
- if (this.store[id]) {
- if (this.store[id].destroy) {
- this.store[id].destroy();
- }
+ /**
+ @param {string} id
+ */
+ deleteStore(id) {
+ const obj = this.store[id];
+
+ if (obj) {
+ if (obj.destroy) {
+ obj.destroy();
+ }
- delete this.store[id];
+ delete this.store[id];
+ }
}
-};
-ConfigStore.prototype.deleteAll = function () {
- Object.keys(this.store).forEach(id => this.deleteStore(id));
-};
+ deleteAll() {
+ Object.keys(this.store).forEach(id => this.deleteStore(id));
+ }
-ConfigStore.prototype.add = function (id, config) {
- this.store[id] = config;
-};
+ /**
+ @param {string} id
+ @param {any} config
+ */
+ add(id, config) {
+ this.store[id] = config;
+ }
-ConfigStore.prototype.get = function (id) {
- return this.store[id];
-};
+ /**
+ @param {string} id
+ */
+ get(id) {
+ return this.store[id];
+ }
+}
const STORE = new ConfigStore();
export default STORE;
+
+/** @typedef {{destroy?(): void}} Destroyable */
diff --git a/src/plugins/plot/configuration/LegendModel.js b/src/plugins/plot/configuration/LegendModel.js
index f99d709ec..0f66f81af 100644
--- a/src/plugins/plot/configuration/LegendModel.js
+++ b/src/plugins/plot/configuration/LegendModel.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -42,7 +42,10 @@ export default class LegendModel extends Model {
}
}
- defaults(options) {
+ /**
+ * @override
+ */
+ defaultModel(options) {
return {
position: 'top',
expandByDefault: false,
diff --git a/src/plugins/plot/configuration/Model.js b/src/plugins/plot/configuration/Model.js
index 952870161..6465fa271 100644
--- a/src/plugins/plot/configuration/Model.js
+++ b/src/plugins/plot/configuration/Model.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -20,13 +20,26 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
-import EventEmitter from 'EventEmitter';
+import EventEmitter from 'eventemitter3';
import eventHelpers from "../lib/eventHelpers";
import _ from 'lodash';
+/**
+ * @template {object} T
+ * @template {object} O
+ */
export default class Model extends EventEmitter {
+ /**
+ * @param {ModelOptions<T, O>} options
+ */
constructor(options) {
super();
+ Object.defineProperty(this, '_events', {
+ value: this._events,
+ enumerable: false,
+ configurable: false,
+ writable: true
+ });
//need to do this as we're already extending EventEmitter
eventHelpers.extend(this);
@@ -35,10 +48,14 @@ export default class Model extends EventEmitter {
options = {};
}
+ // FIXME: this.id is defined as a method further below, but here it is
+ // assigned a possibly-undefined value. Is this code unused?
this.id = options.id;
+
+ /** @type {ModelType<T>} */
this.model = options.model;
this.collection = options.collection;
- const defaults = this.defaults(options);
+ const defaults = this.defaultModel(options);
if (!this.model) {
this.model = options.model = defaults;
} else {
@@ -46,14 +63,24 @@ export default class Model extends EventEmitter {
}
this.initialize(options);
+
+ /** @type {keyof ModelType<T> } */
this.idAttr = 'id';
}
- defaults(options) {
+ /**
+ * @param {ModelOptions<T, O>} options
+ * @returns {ModelType<T>}
+ */
+ defaultModel(options) {
return {};
}
- initialize(model) {
+ /**
+ * @abstract
+ * @param {ModelOptions<T, O>} options
+ */
+ initialize(options) {
}
@@ -69,14 +96,29 @@ export default class Model extends EventEmitter {
return this.get(this.idAttr);
}
+ /**
+ * @template {keyof ModelType<T>} K
+ * @param {K} attribute
+ * @returns {ModelType<T>[K]}
+ */
get(attribute) {
return this.model[attribute];
}
+ /**
+ * @template {keyof ModelType<T>} K
+ * @param {K} attribute
+ * @returns boolean
+ */
has(attribute) {
return _.has(this.model, attribute);
}
+ /**
+ * @template {keyof ModelType<T>} K
+ * @param {K} attribute
+ * @param {ModelType<T>[K]} value
+ */
set(attribute, value) {
const oldValue = this.model[attribute];
this.model[attribute] = value;
@@ -84,6 +126,10 @@ export default class Model extends EventEmitter {
this.emit('change:' + attribute, value, oldValue, this);
}
+ /**
+ * @template {keyof ModelType<T>} K
+ * @param {K} attribute
+ */
unset(attribute) {
const oldValue = this.model[attribute];
delete this.model[attribute];
@@ -91,3 +137,26 @@ export default class Model extends EventEmitter {
this.emit('change:' + attribute, undefined, oldValue, this);
}
}
+
+/** @typedef {any} TODO */
+
+/** @typedef {TODO} OpenMCT */
+
+/**
+@template {object} T
+@typedef {{
+ id?: string
+} & T} ModelType
+*/
+
+/**
+@template {object} T
+@template {object} O
+@typedef {{
+ model?: ModelType<T>
+ models?: T[]
+ openmct: OpenMCT
+ id?: string
+ [k: string]: unknown
+} & O} ModelOptions
+*/
diff --git a/src/plugins/plot/configuration/PlotConfigurationModel.js b/src/plugins/plot/configuration/PlotConfigurationModel.js
index f9f14cef5..ce0c6e532 100644
--- a/src/plugins/plot/configuration/PlotConfigurationModel.js
+++ b/src/plugins/plot/configuration/PlotConfigurationModel.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -26,20 +26,30 @@ import SeriesCollection from "./SeriesCollection";
import XAxisModel from "./XAxisModel";
import YAxisModel from "./YAxisModel";
import LegendModel from "./LegendModel";
+
/**
* PlotConfiguration model stores the configuration of a plot and some
* limited state. The indiidual parts of the plot configuration model
* handle setting defaults and updating in response to various changes.
*
+ * @extends {Model<PlotConfigModelType, PlotConfigModelOptions>}
*/
export default class PlotConfigurationModel extends Model {
/**
* Initializes all sub models and then passes references to submodels
* to those that need it.
+ *
+ * @override
+ * @param {import('./Model').ModelOptions<PlotConfigModelType, PlotConfigModelOptions>} options
*/
initialize(options) {
this.openmct = options.openmct;
+ // This is a type assertion for TypeScript, this error is never thrown in practice.
+ if (!options.model) {
+ throw new Error('Not a collection model.');
+ }
+
this.xAxis = new XAxisModel({
model: options.model.xAxis,
plot: this,
@@ -58,7 +68,8 @@ export default class PlotConfigurationModel extends Model {
this.series = new SeriesCollection({
models: options.model.series,
plot: this,
- openmct: options.openmct
+ openmct: options.openmct,
+ palette: options.palette
});
if (this.get('domainObject').type === 'telemetry.plot.overlay') {
@@ -76,6 +87,8 @@ export default class PlotConfigurationModel extends Model {
}
/**
* Retrieve the persisted series config for a given identifier.
+ * @param {import('./PlotSeries').Identifier} identifier
+ * @returns {import('./PlotSeries').PlotSeriesModelType=}
*/
getPersistedSeriesConfig(identifier) {
const domainObject = this.get('domainObject');
@@ -123,15 +136,49 @@ export default class PlotConfigurationModel extends Model {
/**
* Return defaults, which are extracted from the passed in domain
* object.
+ * @override
+ * @param {import('./Model').ModelOptions<PlotConfigModelType, PlotConfigModelOptions>} options
*/
- defaults(options) {
+ defaultModel(options) {
return {
series: [],
domainObject: options.domainObject,
- xAxis: {
- },
- yAxis: _.cloneDeep(_.get(options.domainObject, 'configuration.yAxis', {})),
- legend: _.cloneDeep(_.get(options.domainObject, 'configuration.legend', {}))
+ xAxis: {},
+ yAxis: _.cloneDeep(options.domainObject.configuration?.yAxis ?? {}),
+ legend: _.cloneDeep(options.domainObject.configuration?.legend ?? {})
};
}
}
+
+/** @typedef {any} TODO */
+
+/** @typedef {import('./PlotSeries').default} PlotSeries */
+
+/**
+@typedef {{
+ configuration: {
+ series: import('./PlotSeries').PlotSeriesModelType[]
+ yAxis: import('./YAxisModel').YAxisModelType
+ },
+}} SomeDomainObject_NeedsName
+*/
+
+/**
+@typedef {{
+ xAxis: import('./XAxisModel').XAxisModelType
+ yAxis: import('./YAxisModel').YAxisModelType
+ legend: TODO
+ series: PlotSeries[]
+ domainObject: SomeDomainObject_NeedsName
+}} PlotConfigModelType
+*/
+
+/** @typedef {TODO} SomeOtherDomainObject */
+
+/**
+TODO: Is SomeOtherDomainObject the same domain object as with SomeDomainObject_NeedsName?
+@typedef {{
+ plot: import('./PlotConfigurationModel').default
+ domainObject: SomeOtherDomainObject
+}} PlotConfigModelOptions
+*/
diff --git a/src/plugins/plot/configuration/PlotSeries.js b/src/plugins/plot/configuration/PlotSeries.js
index bf8623ad7..1c537186e 100644
--- a/src/plugins/plot/configuration/PlotSeries.js
+++ b/src/plugins/plot/configuration/PlotSeries.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -23,6 +23,7 @@ import _ from 'lodash';
import Model from "./Model";
import { MARKER_SHAPES } from '../draw/MarkerShapes';
import configStore from "../configuration/ConfigStore";
+import { symlog } from '../mathUtils';
/**
* Plot series handle interpreting telemetry metadata for a single telemetry
@@ -59,11 +60,21 @@ import configStore from "../configuration/ConfigStore";
* `metadata`: the Open MCT Telemetry Metadata Manager for the associated
* telemetry point.
* `formats`: the Open MCT format map for this telemetry point.
+ *
+ * @extends {Model<PlotSeriesModelType, PlotSeriesModelOptions>}
*/
export default class PlotSeries extends Model {
+ logMode = false;
+
+ /**
+ @param {import('./Model').ModelOptions<PlotSeriesModelType, PlotSeriesModelOptions>} options
+ */
constructor(options) {
+
super(options);
+ this.logMode = options.collection.plot.model.yAxis.logMode;
+
this.listenTo(this, 'change:xKey', this.onXKeyChange, this);
this.listenTo(this, 'change:yKey', this.onYKeyChange, this);
this.persistedConfig = options.persistedConfig;
@@ -76,8 +87,10 @@ export default class PlotSeries extends Model {
/**
* Set defaults for telemetry series.
+ * @param {import('./Model').ModelOptions<PlotSeriesModelType, PlotSeriesModelOptions>} options
+ * @override
*/
- defaults(options) {
+ defaultModel(options) {
this.metadata = options
.openmct
.telemetry
@@ -109,13 +122,21 @@ export default class PlotSeries extends Model {
/**
* Remove real-time subscription when destroyed.
+ * @override
*/
- onDestroy(model) {
+ destroy() {
+ super.destroy();
+
if (this.unsubscribe) {
this.unsubscribe();
}
}
+ /**
+ * Set defaults for telemetry series.
+ * @override
+ * @param {import('./Model').ModelOptions<PlotSeriesModelType, PlotSeriesModelOptions>} options
+ */
initialize(options) {
this.openmct = options.openmct;
this.domainObject = options.domainObject;
@@ -136,9 +157,11 @@ export default class PlotSeries extends Model {
});
this.openmct.time.on('bounds', this.updateLimits);
- this.on('destroy', this.onDestroy, this);
}
+ /**
+ * @param {Bounds} bounds
+ */
updateLimits(bounds) {
this.emit('limitBounds', bounds);
}
@@ -188,7 +211,7 @@ export default class PlotSeries extends Model {
return this.openmct
.telemetry
.request(this.domainObject, options)
- .then(function (points) {
+ .then((points) => {
const data = this.getSeriesData();
const newPoints = _(data)
.concat(points)
@@ -196,7 +219,7 @@ export default class PlotSeries extends Model {
.uniq(true, point => [this.getXVal(point), this.getYVal(point)].join())
.value();
this.reset(newPoints);
- }.bind(this))
+ })
.catch((error) => {
console.warn('Error fetching data', error);
});
@@ -211,6 +234,7 @@ export default class PlotSeries extends Model {
this.getXVal = format.parse.bind(format);
}
}
+
/**
* Update y formatter on change, default to stepAfter interpolation if
* y range is an enumeration.
@@ -234,7 +258,11 @@ export default class PlotSeries extends Model {
}.bind(this);
this.set('unit', valueMetadata.unit);
const format = this.formats[newKey];
- this.getYVal = format.parse.bind(format);
+ this.getYVal = (value) => {
+ const y = format.parse(value);
+
+ return this.logMode ? symlog(y, 10) : y;
+ };
}
formatX(point) {
@@ -502,8 +530,56 @@ export default class PlotSeries extends Model {
/**
* Update the series data with the given value.
+ * This return type definition is totally wrong, only covers sinwave generator. It needs to be generic.
+ * @return-example {Array<{
+ cos: number
+ sin: number
+ mctLimitState: {
+ cssClass: string
+ high: number
+ low: {sin: number, cos: number}
+ name: string
+ }
+ utc: number
+ wavelength: number
+ yesterday: number
+ }>}
*/
getSeriesData() {
return configStore.get(this.dataStoreId) || [];
}
}
+
+/** @typedef {any} TODO */
+
+/** @typedef {{key: string, namespace: string}} Identifier */
+
+/**
+@typedef {{
+ identifier: Identifier
+ name: string
+ unit: string
+ xKey: string
+ yKey: string
+ markers: boolean
+ markerShape: keyof typeof MARKER_SHAPES
+ markerSize: number
+ alarmMarkers: boolean
+ limitLines: boolean
+ interpolate: boolean
+ stats: TODO
+}} PlotSeriesModelType
+*/
+
+/**
+@typedef {{
+ model: PlotSeriesModelType
+ collection: import('./SeriesCollection').default
+ persistedConfig: PlotSeriesModelType
+ filters: TODO
+}} PlotSeriesModelOptions
+*/
+
+/**
+@typedef {import('@/api/time/TimeContext').Bounds} Bounds
+*/
diff --git a/src/plugins/plot/configuration/SeriesCollection.js b/src/plugins/plot/configuration/SeriesCollection.js
index b1e01580e..b5bb81dbb 100644
--- a/src/plugins/plot/configuration/SeriesCollection.js
+++ b/src/plugins/plot/configuration/SeriesCollection.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -26,14 +26,20 @@ import Collection from "./Collection";
import Color from "@/ui/color/Color";
import ColorPalette from "@/ui/color/ColorPalette";
+/**
+ * @extends {Collection<SeriesCollectionModelType, SeriesCollectionOptions>}
+ */
export default class SeriesCollection extends Collection {
-
+ /**
+ @override
+ @param {import('./Model').ModelOptions<SeriesCollectionModelType, SeriesCollectionOptions>} options
+ */
initialize(options) {
super.initialize(options);
this.modelClass = PlotSeries;
this.plot = options.plot;
this.openmct = options.openmct;
- this.palette = new ColorPalette();
+ this.palette = options.palette || new ColorPalette();
this.listenTo(this, 'add', this.onSeriesAdd, this);
this.listenTo(this, 'remove', this.onSeriesRemove, this);
this.listenTo(this.plot, 'change:domainObject', this.trackPersistedConfig, this);
@@ -83,11 +89,15 @@ export default class SeriesCollection extends Collection {
// Clone to prevent accidental mutation by ref.
seriesConfig = JSON.parse(JSON.stringify(seriesConfig));
+ if (!seriesConfig) {
+ throw "not possible";
+ }
+
this.add(new PlotSeries({
model: seriesConfig,
domainObject: domainObject,
- collection: this,
openmct: this.openmct,
+ collection: this,
persistedConfig: this.plot
.getPersistedSeriesConfig(domainObject.identifier),
filters: filters
@@ -163,3 +173,13 @@ export default class SeriesCollection extends Collection {
})[0];
}
}
+
+/**
+@typedef {PlotSeries} SeriesCollectionModelType
+*/
+
+/**
+@typedef {{
+ plot: import('./PlotConfigurationModel').default
+}} SeriesCollectionOptions
+*/
diff --git a/src/plugins/plot/configuration/XAxisModel.js b/src/plugins/plot/configuration/XAxisModel.js
index 58f2413c2..b228305ad 100644
--- a/src/plugins/plot/configuration/XAxisModel.js
+++ b/src/plugins/plot/configuration/XAxisModel.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -19,25 +19,41 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
-import Model from "./Model";
+import Model from './Model';
+
/**
- * TODO: doc strings.
- */
+ * @extends {Model<XAxisModelType, XAxisModelOptions>}
+ */
export default class XAxisModel extends Model {
+ // Despite providing template types to the Model class, we still need to
+ // re-define the type of the following initialize() method's options arg. Tracking
+ // issue for this: https://github.com/microsoft/TypeScript/issues/32082
+ // When they fix it, we can remove the `@param` we have here.
+ /**
+ * @override
+ * @param {import('./Model').ModelOptions<XAxisModelType, XAxisModelOptions>} options
+ */
initialize(options) {
this.plot = options.plot;
+
+ // This is a type assertion for TypeScript, this error is not thrown in practice.
+ if (!options.model) {
+ throw new Error('Not a collection model.');
+ }
+
this.set('label', options.model.name || '');
- this.on('change:range', function (newValue, oldValue, model) {
- if (!model.get('frozen')) {
- model.set('displayRange', newValue);
+
+ this.on('change:range', (newValue) => {
+ if (!this.get('frozen')) {
+ this.set('displayRange', newValue);
}
});
- this.on('change:frozen', ((frozen, oldValue, model) => {
+ this.on('change:frozen', (frozen) => {
if (!frozen) {
- model.set('range', this.get('range'));
+ this.set('range', this.get('range'));
}
- }));
+ });
if (this.get('range')) {
this.set('range', this.get('range'));
@@ -45,6 +61,10 @@ export default class XAxisModel extends Model {
this.listenTo(this, 'change:key', this.changeKey, this);
}
+
+ /**
+ * @param {string} newKey
+ */
changeKey(newKey) {
const series = this.plot.series.first();
if (series) {
@@ -68,12 +88,17 @@ export default class XAxisModel extends Model {
plotSeries.reset();
});
}
- defaults(options) {
+ /**
+ * @param {import('./Model').ModelOptions<XAxisModelType, XAxisModelOptions>} options
+ * @override
+ */
+ defaultModel(options) {
const bounds = options.openmct.time.bounds();
const timeSystem = options.openmct.time.timeSystem();
const format = options.openmct.telemetry.getFormatter(timeSystem.timeFormat);
- return {
+ /** @type {XAxisModelType} */
+ const defaultModel = {
name: timeSystem.name,
key: timeSystem.key,
format: format.format.bind(format),
@@ -83,5 +108,42 @@ export default class XAxisModel extends Model {
},
frozen: false
};
+
+ return defaultModel;
}
}
+
+/** @typedef {any} TODO */
+
+/** @typedef {TODO} OpenMCT */
+
+/**
+@typedef {{
+ min: number
+ max: number
+}} NumberRange
+*/
+
+/**
+@typedef {import("./Model").ModelType<{
+ range?: NumberRange
+ displayRange: NumberRange
+ frozen: boolean
+ label: string
+ format: (n: number) => string
+ values: Array<TODO>
+}>} AxisModelType
+*/
+
+/**
+@typedef {AxisModelType & {
+ name: string
+ key: string
+}} XAxisModelType
+*/
+
+/**
+@typedef {{
+ plot: import('./PlotConfigurationModel').default
+}} XAxisModelOptions
+*/
diff --git a/src/plugins/plot/configuration/YAxisModel.js b/src/plugins/plot/configuration/YAxisModel.js
index 2bd0f8629..8c03631d6 100644
--- a/src/plugins/plot/configuration/YAxisModel.js
+++ b/src/plugins/plot/configuration/YAxisModel.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -19,58 +19,62 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
-import _ from 'lodash';
+import { antisymlog, symlog } from '../mathUtils';
import Model from './Model';
/**
- * YAxis model
- *
- * TODO: docstrings.
- *
- * has the following Model properties:
- *
- * `autoscale`: boolean, whether or not to autoscale.
- * `autoscalePadding`: float, percent of padding to display in plots.
- * `displayRange`: the current display range for the x Axis.
- * `format`: the formatter for the axis.
- * `frozen`: boolean, if true, displayRange will not be updated automatically.
- * Used to temporarily disable automatic updates during user interaction.
- * `label`: label to display on axis.
- * `stats`: Min and Max Values of data, automatically updated by observing
- * plot series.
- * `values`: for enumerated types, an array of possible display values.
- * `range`: the user-configured range to use for display, when autoscale is
- * disabled.
- *
- */
+ * YAxis model
+ *
+ * TODO: docstrings.
+ *
+ * has the following Model properties:
+ *
+ * `autoscale`: boolean, whether or not to autoscale.
+ * `autoscalePadding`: float, percent of padding to display in plots.
+ * `displayRange`: the current display range for the axis.
+ * `format`: the formatter for the axis.
+ * `frozen`: boolean, if true, displayRange will not be updated automatically.
+ * Used to temporarily disable automatic updates during user interaction.
+ * `label`: label to display on axis.
+ * `stats`: Min and Max Values of data, automatically updated by observing
+ * plot series.
+ * `values`: for enumerated types, an array of possible display values.
+ * `range`: the user-configured range to use for display, when autoscale is
+ * disabled.
+ *
+ * @extends {Model<YAxisModelType, YAxisModelOptions>}
+ */
export default class YAxisModel extends Model {
+ /**
+ * @override
+ * @param {import('./Model').ModelOptions<YAxisModelType, YAxisModelOptions>} options
+ */
initialize(options) {
this.plot = options.plot;
this.listenTo(this, 'change:stats', this.calculateAutoscaleExtents, this);
this.listenTo(this, 'change:autoscale', this.toggleAutoscale, this);
this.listenTo(this, 'change:autoscalePadding', this.updatePadding, this);
+ this.listenTo(this, 'change:logMode', this.onLogModeChange, this);
this.listenTo(this, 'change:frozen', this.toggleFreeze, this);
this.listenTo(this, 'change:range', this.updateDisplayRange, this);
this.updateDisplayRange(this.get('range'));
}
+ /**
+ * @param {import('./SeriesCollection').default} seriesCollection
+ */
listenToSeriesCollection(seriesCollection) {
this.seriesCollection = seriesCollection;
- this.listenTo(this.seriesCollection, 'add', (series => {
+ this.listenTo(this.seriesCollection, 'add', series => {
this.trackSeries(series);
this.updateFromSeries(this.seriesCollection);
- }), this);
- this.listenTo(this.seriesCollection, 'remove', (series => {
+ }, this);
+ this.listenTo(this.seriesCollection, 'remove', series => {
this.untrackSeries(series);
this.updateFromSeries(this.seriesCollection);
- }), this);
+ }, this);
this.seriesCollection.forEach(this.trackSeries, this);
this.updateFromSeries(this.seriesCollection);
}
- updateDisplayRange(range) {
- if (!this.get('autoscale')) {
- this.set('displayRange', range);
- }
- }
toggleFreeze(frozen) {
if (!frozen) {
this.toggleAutoscale(this.get('autoscale'));
@@ -132,12 +136,15 @@ export default class YAxisModel extends Model {
}
resetStats() {
this.unset('stats');
- this.seriesCollection.forEach(function (series) {
+ this.seriesCollection.forEach(series => {
if (series.has('stats')) {
this.updateStats(series.get('stats'));
}
- }, this);
+ });
}
+ /**
+ * @param {import('./PlotSeries').default} series
+ */
trackSeries(series) {
this.listenTo(series, 'change:stats', seriesStats => {
if (!seriesStats) {
@@ -155,21 +162,105 @@ export default class YAxisModel extends Model {
this.resetStats();
this.updateFromSeries(this.seriesCollection);
}
+
+ /**
+ * This is called in order to map the user-provided `range` to the
+ * `displayRange` that we actually use for plot display.
+ *
+ * @param {import('./XAxisModel').NumberRange} range
+ */
+ updateDisplayRange(range) {
+ if (this.get('autoscale')) {
+ return;
+ }
+
+ const _range = { ...range };
+
+ if (this.get('logMode')) {
+ _range.min = symlog(range.min, 10);
+ _range.max = symlog(range.max, 10);
+ }
+
+ this.set('displayRange', _range);
+ }
+
+ /**
+ * @param {boolean} autoscale
+ */
toggleAutoscale(autoscale) {
if (autoscale && this.has('stats')) {
this.set('displayRange', this.applyPadding(this.get('stats')));
+
+ return;
+ }
+
+ const range = this.get('range');
+
+ if (range) {
+ // If we already have a user-defined range, make sure it maps to the
+ // range we'll actually use for the ticks.
+
+ const _range = { ...range };
+
+ if (this.get('logMode')) {
+ _range.min = symlog(range.min, 10);
+ _range.max = symlog(range.max, 10);
+ }
+
+ this.set('displayRange', _range);
} else {
- this.set('displayRange', this.get('range'));
+ // Otherwise use the last known displayRange as the initial
+ // values for the user-defined range, so that we don't end up
+ // with any error from an undefined user range.
+
+ const _range = this.get('displayRange');
+
+ if (!_range) {
+ return;
+ }
+
+ if (this.get('logMode')) {
+ _range.min = antisymlog(_range.min, 10);
+ _range.max = antisymlog(_range.max, 10);
+ }
+
+ this.set('range', _range);
}
}
+
+ /** @param {boolean} logMode */
+ onLogModeChange(logMode) {
+ const range = this.get('displayRange');
+
+ if (logMode) {
+ range.min = symlog(range.min, 10);
+ range.max = symlog(range.max, 10);
+ } else {
+ range.min = antisymlog(range.min, 10);
+ range.max = antisymlog(range.max, 10);
+ }
+
+ this.set('displayRange', range);
+
+ this.resetSeries();
+ }
+ resetSeries() {
+ this.plot.series.forEach((plotSeries) => {
+ plotSeries.logMode = this.get('logMode');
+ plotSeries.reset(plotSeries.getSeriesData());
+ });
+ // Update the series collection labels and formatting
+ this.updateFromSeries(this.seriesCollection);
+ }
/**
- * Update yAxis format, values, and label from known series.
- */
- updateFromSeries(series) {
+ * Update yAxis format, values, and label from known series.
+ * @param {import('./SeriesCollection').default} seriesCollection
+ */
+ updateFromSeries(seriesCollection) {
const plotModel = this.plot.get('domainObject');
- const label = _.get(plotModel, 'configuration.yAxis.label');
- const sampleSeries = series.first();
- if (!sampleSeries) {
+ const label = plotModel.configuration?.yAxis?.label;
+ const sampleSeries = seriesCollection.first();
+ if (!sampleSeries || !sampleSeries.metadata) {
if (!label) {
this.unset('label');
}
@@ -180,22 +271,28 @@ export default class YAxisModel extends Model {
const yKey = sampleSeries.get('yKey');
const yMetadata = sampleSeries.metadata.value(yKey);
const yFormat = sampleSeries.formats[yKey];
- this.set('format', yFormat.format.bind(yFormat));
+
+ if (this.get('logMode')) {
+ this.set('format', (n) => yFormat.format(antisymlog(n, 10)));
+ } else {
+ this.set('format', (n) => yFormat.format(n));
+ }
+
this.set('values', yMetadata.values);
if (!label) {
- const labelName = series.map(function (s) {
- return s.metadata ? s.metadata.value(s.get('yKey')).name : '';
- }).reduce(function (a, b) {
- if (a === undefined) {
- return b;
- }
+ const labelName = seriesCollection
+ .map(s => (s.metadata ? s.metadata.value(s.get('yKey')).name : ''))
+ .reduce((a, b) => {
+ if (a === undefined) {
+ return b;
+ }
- if (a === b) {
- return a;
- }
+ if (a === b) {
+ return a;
+ }
- return '';
- }, undefined);
+ return '';
+ }, undefined);
if (labelName) {
this.set('label', labelName);
@@ -203,19 +300,19 @@ export default class YAxisModel extends Model {
return;
}
- const labelUnits = series.map(function (s) {
- return s.metadata ? s.metadata.value(s.get('yKey')).units : '';
- }).reduce(function (a, b) {
- if (a === undefined) {
- return b;
- }
+ const labelUnits = seriesCollection
+ .map(s => (s.metadata ? s.metadata.value(s.get('yKey')).units : ''))
+ .reduce((a, b) => {
+ if (a === undefined) {
+ return b;
+ }
- if (a === b) {
- return a;
- }
+ if (a === b) {
+ return a;
+ }
- return '';
- }, undefined);
+ return '';
+ }, undefined);
if (labelUnits) {
this.set('label', labelUnits);
@@ -224,11 +321,39 @@ export default class YAxisModel extends Model {
}
}
}
- defaults(options) {
+ /**
+ * @override
+ * @param {import('./Model').ModelOptions<YAxisModelType, YAxisModelOptions>} options
+ * @returns {Partial<YAxisModelType>}
+ */
+ defaultModel(options) {
return {
frozen: false,
autoscale: true,
+ logMode: options.model?.logMode ?? false,
autoscalePadding: 0.1
+
+ // 'range' is not specified here, it is undefined at first. When the
+ // user turns off autoscale, the current 'displayRange' is used for
+ // the initial value of 'range'.
};
}
}
+
+/** @typedef {any} TODO */
+
+/**
+@typedef {import('./XAxisModel').AxisModelType & {
+ autoscale: boolean
+ logMode: boolean
+ autoscalePadding: number
+ stats?: import('./XAxisModel').NumberRange
+ values: Array<TODO>
+}} YAxisModelType
+*/
+
+/**
+@typedef {{
+ plot: import('./PlotConfigurationModel').default
+}} YAxisModelOptions
+*/
diff --git a/src/plugins/plot/draw/Draw2D.js b/src/plugins/plot/draw/Draw2D.js
index 6a4e07073..0b05c74b8 100644
--- a/src/plugins/plot/draw/Draw2D.js
+++ b/src/plugins/plot/draw/Draw2D.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/plot/draw/DrawLoader.js b/src/plugins/plot/draw/DrawLoader.js
index fa1e698f3..839543561 100644
--- a/src/plugins/plot/draw/DrawLoader.js
+++ b/src/plugins/plot/draw/DrawLoader.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/plot/draw/DrawWebGL.js b/src/plugins/plot/draw/DrawWebGL.js
index 7918852a0..6ab1e4d51 100644
--- a/src/plugins/plot/draw/DrawWebGL.js
+++ b/src/plugins/plot/draw/DrawWebGL.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -110,6 +110,7 @@ DrawWebGL.prototype.onContextLost = function (event) {
this.emit('error');
this.isContextLost = true;
this.destroy();
+ // TODO re-initialize and re-draw on context restored
};
DrawWebGL.prototype.initContext = function () {
diff --git a/src/plugins/plot/draw/MarkerShapes.js b/src/plugins/plot/draw/MarkerShapes.js
index 9ff5448e8..7a0c59fc4 100644
--- a/src/plugins/plot/draw/MarkerShapes.js
+++ b/src/plugins/plot/draw/MarkerShapes.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/plot/inspector/PlotOptions.vue b/src/plugins/plot/inspector/PlotOptions.vue
index 0aa28000b..a72fcb8c9 100644
--- a/src/plugins/plot/inspector/PlotOptions.vue
+++ b/src/plugins/plot/inspector/PlotOptions.vue
@@ -1,5 +1,5 @@
<!--
- Open MCT, Copyright (c) 2014-2020, United States Government
+ Open MCT, Copyright (c) 2014-2022, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
diff --git a/src/plugins/plot/inspector/PlotOptionsBrowse.vue b/src/plugins/plot/inspector/PlotOptionsBrowse.vue
index 2ab893ff6..c32536b01 100644
--- a/src/plugins/plot/inspector/PlotOptionsBrowse.vue
+++ b/src/plugins/plot/inspector/PlotOptionsBrowse.vue
@@ -1,5 +1,5 @@
<!--
- Open MCT, Copyright (c) 2014-2020, United States Government
+ Open MCT, Copyright (c) 2014-2022, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
@@ -20,76 +20,106 @@
at runtime from the About dialog for additional information.
-->
<template>
-<div v-if="loaded"
- class="js-plot-options-browse"
+<div
+ v-if="loaded"
+ class="js-plot-options-browse"
>
- <ul class="c-tree">
+ <ul
+ v-if="!isStackedPlotObject"
+ class="c-tree"
+ >
<h2 title="Plot series display properties in this object">Plot Series</h2>
- <plot-options-item v-for="series in plotSeries"
- :key="series.key"
- :series="series"
+ <plot-options-item
+ v-for="series in plotSeries"
+ :key="series.key"
+ :series="series"
/>
</ul>
- <div v-if="plotSeries.length"
- class="grid-properties"
+ <div
+ v-if="plotSeries.length"
+ class="grid-properties"
>
- <ul class="l-inspector-part">
+ <ul
+ v-if="!isStackedPlotObject"
+ class="l-inspector-part js-yaxis-properties"
+ >
<h2 title="Y axis settings for this object">Y Axis</h2>
<li class="grid-row">
- <div class="grid-cell label"
- title="Manually override how the Y axis is labeled."
+ <div
+ class="grid-cell label"
+ title="Manually override how the Y axis is labeled."
>Label</div>
<div class="grid-cell value">{{ label ? label : "Not defined" }}</div>
</li>
<li class="grid-row">
- <div class="grid-cell label"
- title="Automatically scale the Y axis to keep all values in view."
- >Autoscale</div>
+ <div
+ class="grid-cell label"
+ title="Enable log mode."
+ >Log mode</div>
<div class="grid-cell value">
- {{ autoscale ? "Enabled: " : "Disabled" }}
- {{ autoscale ? autoscalePadding : "" }}
+ {{ logMode ? "Enabled" : "Disabled" }}
</div>
</li>
- <li v-if="!autoscale && rangeMin"
+ <li class="grid-row">
+ <div
+ class="grid-cell label"
+ title="Automatically scale the Y axis to keep all values in view."
+ >Auto scale</div>
+ <div class="grid-cell value">
+ {{ autoscale ? "Enabled: " + autoscalePadding : "Disabled" }}
+ </div>
+ </li>
+ <li
+ v-if="!autoscale && rangeMin"
class="grid-row"
>
- <div class="grid-cell label"
- title="Minimum Y axis value."
+ <div
+ class="grid-cell label"
+ title="Minimum Y axis value."
>Minimum value</div>
<div class="grid-cell value">{{ rangeMin }}</div>
</li>
- <li v-if="!autoscale && rangeMax"
+ <li
+ v-if="!autoscale && rangeMax"
class="grid-row"
>
- <div class="grid-cell label"
- title="Maximum Y axis value."
+ <div
+ class="grid-cell label"
+ title="Maximum Y axis value."
>Maximum value</div>
<div class="grid-cell value">{{ rangeMax }}</div>
</li>
</ul>
- <ul class="l-inspector-part">
+ <ul
+ v-if="isStackedPlotObject || !isNestedWithinAStackedPlot"
+ class="l-inspector-part js-legend-properties"
+ >
<h2 title="Legend settings for this object">Legend</h2>
<li class="grid-row">
- <div class="grid-cell label"
- title="The position of the legend relative to the plot display area."
+ <div
+ class="grid-cell label"
+ title="The position of the legend relative to the plot display area."
>Position</div>
<div class="grid-cell value capitalize">{{ position }}</div>
</li>
<li class="grid-row">
- <div class="grid-cell label"
- title="Hide the legend when the plot is small"
+ <div
+ class="grid-cell label"
+ title="Hide the legend when the plot is small"
>Hide when plot small</div>
<div class="grid-cell value">{{ hideLegendWhenSmall ? "Yes" : "No" }}</div>
</li>
<li class="grid-row">
- <div class="grid-cell label"
- title="Show the legend expanded by default"
+ <div
+ class="grid-cell label"
+ title="Show the legend expanded by default"
>Expand by Default</div>
<div class="grid-cell value">{{ expandByDefault ? "Yes" : "No" }}</div>
</li>
<li class="grid-row">
- <div class="grid-cell label"
- title="What to display in the legend when it's collapsed."
+ <div
+ class="grid-cell label"
+ title="What to display in the legend when it's collapsed."
>Show when collapsed:</div>
<div class="grid-cell value">{{
valueToShowWhenCollapsed.replace('nearest', '')
@@ -97,8 +127,9 @@
</div>
</li>
<li class="grid-row">
- <div class="grid-cell label"
- title="What to display in the legend when it's expanded."
+ <div
+ class="grid-cell label"
+ title="What to display in the legend when it's expanded."
>Show when expanded:</div>
<div class="grid-cell value comma-list">
<span v-if="showTimestampWhenExpanded">Timestamp</span>
@@ -122,12 +153,13 @@ export default {
components: {
PlotOptionsItem
},
- inject: ['openmct', 'domainObject'],
+ inject: ['openmct', 'domainObject', 'path'],
data() {
return {
config: {},
label: '',
autoscale: '',
+ logMode: false,
autoscalePadding: '',
rangeMin: '',
rangeMax: '',
@@ -144,36 +176,48 @@ export default {
plotSeries: []
};
},
+ computed: {
+ isNestedWithinAStackedPlot() {
+ return this.path.find((pathObject, pathObjIndex) => pathObjIndex > 0 && pathObject.type === 'telemetry.plot.stacked');
+ },
+ isStackedPlotObject() {
+ return this.path.find((pathObject, pathObjIndex) => pathObjIndex === 0 && pathObject.type === 'telemetry.plot.stacked');
+ }
+ },
mounted() {
eventHelpers.extend(this);
this.config = this.getConfig();
this.registerListeners();
this.initConfiguration();
this.loaded = true;
+
},
beforeDestroy() {
this.stopListening();
},
methods: {
initConfiguration() {
- this.label = this.config.yAxis.get('label');
- this.autoscale = this.config.yAxis.get('autoscale');
- this.autoscalePadding = this.config.yAxis.get('autoscalePadding');
- const range = this.config.yAxis.get('range');
- if (range) {
- this.rangeMin = range.min;
- this.rangeMax = range.max;
- }
+ if (this.config) {
+ this.label = this.config.yAxis.get('label');
+ this.autoscale = this.config.yAxis.get('autoscale');
+ this.logMode = this.config.yAxis.get('logMode');
+ this.autoscalePadding = this.config.yAxis.get('autoscalePadding');
+ const range = this.config.yAxis.get('range');
+ if (range) {
+ this.rangeMin = range.min;
+ this.rangeMax = range.max;
+ }
- this.position = this.config.legend.get('position');
- this.hideLegendWhenSmall = this.config.legend.get('hideLegendWhenSmall');
- this.expandByDefault = this.config.legend.get('expandByDefault');
- this.valueToShowWhenCollapsed = this.config.legend.get('valueToShowWhenCollapsed');
- this.showTimestampWhenExpanded = this.config.legend.get('showTimestampWhenExpanded');
- this.showValueWhenExpanded = this.config.legend.get('showValueWhenExpanded');
- this.showMinimumWhenExpanded = this.config.legend.get('showMinimumWhenExpanded');
- this.showMaximumWhenExpanded = this.config.legend.get('showMaximumWhenExpanded');
- this.showUnitsWhenExpanded = this.config.legend.get('showUnitsWhenExpanded');
+ this.position = this.config.legend.get('position');
+ this.hideLegendWhenSmall = this.config.legend.get('hideLegendWhenSmall');
+ this.expandByDefault = this.config.legend.get('expandByDefault');
+ this.valueToShowWhenCollapsed = this.config.legend.get('valueToShowWhenCollapsed');
+ this.showTimestampWhenExpanded = this.config.legend.get('showTimestampWhenExpanded');
+ this.showValueWhenExpanded = this.config.legend.get('showValueWhenExpanded');
+ this.showMinimumWhenExpanded = this.config.legend.get('showMinimumWhenExpanded');
+ this.showMaximumWhenExpanded = this.config.legend.get('showMaximumWhenExpanded');
+ this.showUnitsWhenExpanded = this.config.legend.get('showUnitsWhenExpanded');
+ }
},
getConfig() {
this.configId = this.openmct.objects.makeKeyString(this.domainObject.identifier);
@@ -181,10 +225,12 @@ export default {
return configStore.get(this.configId);
},
registerListeners() {
- this.config.series.forEach(this.addSeries, this);
+ if (this.config) {
+ this.config.series.forEach(this.addSeries, this);
- this.listenTo(this.config.series, 'add', this.addSeries, this);
- this.listenTo(this.config.series, 'remove', this.resetAllSeries, this);
+ this.listenTo(this.config.series, 'add', this.addSeries, this);
+ this.listenTo(this.config.series, 'remove', this.resetAllSeries, this);
+ }
},
addSeries(series, index) {
diff --git a/src/plugins/plot/inspector/PlotOptionsEdit.vue b/src/plugins/plot/inspector/PlotOptionsEdit.vue
index 3adcc0fb0..9151784d5 100644
--- a/src/plugins/plot/inspector/PlotOptionsEdit.vue
+++ b/src/plugins/plot/inspector/PlotOptionsEdit.vue
@@ -1,5 +1,5 @@
<!--
- Open MCT, Copyright (c) 2014-2020, United States Government
+ Open MCT, Copyright (c) 2014-2022, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
@@ -20,26 +20,40 @@
at runtime from the About dialog for additional information.
-->
<template>
-<div v-if="loaded"
- class="js-plot-options-edit"
+<div
+ v-if="loaded"
+ class="js-plot-options-edit"
>
- <ul class="c-tree">
+ <ul
+ v-if="!isStackedPlotObject"
+ class="c-tree"
+ >
<h2 title="Display properties for this object">Plot Series</h2>
- <li v-for="series in plotSeries"
+ <li
+ v-for="series in plotSeries"
:key="series.key"
>
- <series-form :series="series" />
+ <series-form
+ :series="series"
+ @seriesUpdated="updateSeriesConfigForObject"
+ />
</li>
</ul>
- <y-axis-form v-if="plotSeries.length"
- class="grid-properties"
- :y-axis="config.yAxis"
+ <y-axis-form
+ v-if="plotSeries.length && !isStackedPlotObject"
+ class="grid-properties"
+ :y-axis="config.yAxis"
+ @seriesUpdated="updateSeriesConfigForObject"
/>
- <ul class="l-inspector-part">
+ <ul
+ v-if="isStackedPlotObject || !isStackedPlotNestedObject"
+ class="l-inspector-part"
+ >
<h2 title="Legend options">Legend</h2>
- <legend-form v-if="plotSeries.length"
- class="grid-properties"
- :legend="config.legend"
+ <legend-form
+ v-if="plotSeries.length"
+ class="grid-properties"
+ :legend="config.legend"
/>
</ul>
</div>
@@ -50,6 +64,7 @@ import YAxisForm from "./forms/YAxisForm.vue";
import LegendForm from "./forms/LegendForm.vue";
import eventHelpers from "../lib/eventHelpers";
import configStore from "../configuration/ConfigStore";
+import _ from "lodash";
export default {
components: {
@@ -57,7 +72,7 @@ export default {
SeriesForm,
YAxisForm
},
- inject: ['openmct', 'domainObject'],
+ inject: ['openmct', 'domainObject', 'path'],
data() {
return {
config: {},
@@ -65,6 +80,14 @@ export default {
loaded: false
};
},
+ computed: {
+ isStackedPlotNestedObject() {
+ return this.path.find((pathObject, pathObjIndex) => pathObjIndex > 0 && pathObject.type === 'telemetry.plot.stacked');
+ },
+ isStackedPlotObject() {
+ return this.path.find((pathObject, pathObjIndex) => pathObjIndex === 0 && pathObject.type === 'telemetry.plot.stacked');
+ }
+ },
mounted() {
eventHelpers.extend(this);
this.config = this.getConfig();
@@ -94,6 +117,34 @@ export default {
resetAllSeries() {
this.plotSeries = [];
this.config.series.forEach(this.addSeries, this);
+ },
+
+ updateSeriesConfigForObject(config) {
+ const stackedPlotObject = this.path.find((pathObject) => pathObject.type === 'telemetry.plot.stacked');
+ let index = stackedPlotObject.configuration.series.findIndex((seriesConfig) => {
+ return this.openmct.objects.areIdsEqual(seriesConfig.identifier, config.identifier);
+ });
+ if (index < 0) {
+ index = stackedPlotObject.configuration.series.length;
+ const configPath = `configuration.series[${index}]`;
+ let newConfig = {
+ identifier: config.identifier
+ };
+ _.set(newConfig, `${config.path}`, config.value);
+ this.openmct.objects.mutate(
+ stackedPlotObject,
+ configPath,
+ newConfig
+ );
+ } else {
+ const configPath = `configuration.series[${index}].${config.path}`;
+ this.openmct.objects.mutate(
+ stackedPlotObject,
+ configPath,
+ config.value
+ );
+ }
+
}
}
};
diff --git a/src/plugins/plot/inspector/PlotOptionsItem.vue b/src/plugins/plot/inspector/PlotOptionsItem.vue
index 4b87e64a7..41d4b586f 100644
--- a/src/plugins/plot/inspector/PlotOptionsItem.vue
+++ b/src/plugins/plot/inspector/PlotOptionsItem.vue
@@ -1,41 +1,49 @@
<template>
<ul>
- <li class="c-tree__item menus-to-left"
+ <li
+ class="c-tree__item menus-to-left"
:class="isAliasClass"
>
- <span class="c-disclosure-triangle is-enabled flex-elem"
- :class="expandedCssClass"
- @click="toggleExpanded"
+ <span
+ class="c-disclosure-triangle is-enabled flex-elem"
+ :class="expandedCssClass"
+ @click="toggleExpanded"
>
</span>
- <div class="c-object-label"
- :class="statusClass"
+ <div
+ class="c-object-label"
+ :class="statusClass"
>
- <div class="c-object-label__type-icon"
- :class="getSeriesClass"
+ <div
+ class="c-object-label__type-icon"
+ :class="getSeriesClass"
>
- <span class="is-status__indicator"
- title="This item is missing or suspect"
+ <span
+ class="is-status__indicator"
+ title="This item is missing or suspect"
></span>
</div>
<div class="c-object-label__name">{{ series.domainObject.name }}</div>
</div>
</li>
- <li v-show="expanded"
+ <li
+ v-show="expanded"
class="c-tree__item menus-to-left"
>
<ul class="grid-properties js-plot-options-browse-properties">
<li class="grid-row">
- <div class="grid-cell label"
- title="The field to be plotted as a value for this series."
+ <div
+ class="grid-cell label"
+ title="The field to be plotted as a value for this series."
>Value</div>
<div class="grid-cell value">
{{ yKey }}
</div>
</li>
<li class="grid-row">
- <div class="grid-cell label"
- title="The rendering method to join lines for this series."
+ <div
+ class="grid-cell label"
+ title="The rendering method to join lines for this series."
>Line Method</div>
<div class="grid-cell value">{{ {
'none': 'None',
@@ -45,33 +53,37 @@
</div>
</li>
<li class="grid-row">
- <div class="grid-cell label"
- title="Whether markers are displayed, and their size."
+ <div
+ class="grid-cell label"
+ title="Whether markers are displayed, and their size."
>Markers</div>
<div class="grid-cell value">
{{ markerOptionsDisplayText }}
</div>
</li>
<li class="grid-row">
- <div class="grid-cell label"
- title="Display markers visually denoting points in alarm."
+ <div
+ class="grid-cell label"
+ title="Display markers visually denoting points in alarm."
>Alarm Markers</div>
<div class="grid-cell value">
{{ alarmMarkers ? "Enabled" : "Disabled" }}
</div>
</li>
<li class="grid-row">
- <div class="grid-cell label"
- title="Display lines visually denoting alarm limits."
+ <div
+ class="grid-cell label"
+ title="Display lines visually denoting alarm limits."
>Limit lines</div>
<div class="grid-cell value">
{{ limitLines ? "Enabled" : "Disabled" }}
</div>
</li>
- <ColorSwatch :current-color="seriesHexColor"
- edit-title="Manually set the plot line and marker color for this series."
- view-title="The plot line and marker color for this series."
- short-label="Color"
+ <ColorSwatch
+ :current-color="seriesHexColor"
+ edit-title="Manually set the plot line and marker color for this series."
+ view-title="The plot line and marker color for this series."
+ short-label="Color"
/>
</ul>
</li>
diff --git a/src/plugins/plot/inspector/PlotsInspectorViewProvider.js b/src/plugins/plot/inspector/PlotsInspectorViewProvider.js
index 16b88412c..aebacfa7e 100644
--- a/src/plugins/plot/inspector/PlotsInspectorViewProvider.js
+++ b/src/plugins/plot/inspector/PlotsInspectorViewProvider.js
@@ -13,8 +13,10 @@ export default function PlotsInspectorViewProvider(openmct) {
let object = selection[0][0].context.item;
- return object
- && object.type === 'telemetry.plot.overlay';
+ const isOverlayPlotObject = object && object.type === 'telemetry.plot.overlay';
+ const isStackedPlotObject = object && object.type === 'telemetry.plot.stacked';
+
+ return isStackedPlotObject || isOverlayPlotObject;
},
view: function (selection) {
let component;
diff --git a/src/plugins/plot/inspector/StackedPlotsInspectorViewProvider.js b/src/plugins/plot/inspector/StackedPlotsInspectorViewProvider.js
new file mode 100644
index 000000000..8cd6bc78d
--- /dev/null
+++ b/src/plugins/plot/inspector/StackedPlotsInspectorViewProvider.js
@@ -0,0 +1,59 @@
+
+import PlotOptions from "./PlotOptions.vue";
+import Vue from 'vue';
+
+export default function StackedPlotsInspectorViewProvider(openmct) {
+ return {
+ key: 'stacked-plots-inspector',
+ name: 'Stacked Plots Inspector View',
+ canView: function (selection) {
+ if (selection.length === 0 || selection[0].length === 0) {
+ return false;
+ }
+
+ const object = selection[0][0].context.item;
+ const parent = selection[0].length > 1 && selection[0][1].context.item;
+
+ const isOverlayPlotObject = object && object.type === 'telemetry.plot.overlay';
+ const isParentStackedPlotObject = parent && parent.type === 'telemetry.plot.stacked';
+
+ return !isOverlayPlotObject && isParentStackedPlotObject;
+ },
+ view: function (selection) {
+ let component;
+ let objectPath;
+
+ if (selection.length) {
+ objectPath = selection[0].map((selectionItem) => {
+ return selectionItem.context.item;
+ });
+ }
+
+ return {
+ show: function (element) {
+ component = new Vue({
+ el: element,
+ components: {
+ PlotOptions: PlotOptions
+ },
+ provide: {
+ openmct,
+ domainObject: selection[0][0].context.item,
+ path: objectPath
+ },
+ template: '<plot-options></plot-options>'
+ });
+ },
+ destroy: function () {
+ if (component) {
+ component.$destroy();
+ component = undefined;
+ }
+ }
+ };
+ },
+ priority: function () {
+ return 1;
+ }
+ };
+}
diff --git a/src/plugins/plot/inspector/forms/LegendForm.vue b/src/plugins/plot/inspector/forms/LegendForm.vue
index e00d7987b..370c1d52e 100644
--- a/src/plugins/plot/inspector/forms/LegendForm.vue
+++ b/src/plugins/plot/inspector/forms/LegendForm.vue
@@ -1,12 +1,14 @@
<template>
<div>
<li class="grid-row">
- <div class="grid-cell label"
- title="The position of the legend relative to the plot display area."
+ <div
+ class="grid-cell label"
+ title="The position of the legend relative to the plot display area."
>Position</div>
<div class="grid-cell value">
- <select v-model="position"
- @change="updateForm('position')"
+ <select
+ v-model="position"
+ @change="updateForm('position')"
>
<option value="top">Top</option>
<option value="right">Right</option>
@@ -16,30 +18,36 @@
</div>
</li>
<li class="grid-row">
- <div class="grid-cell label"
- title="Hide the legend when the plot is small"
+ <div
+ class="grid-cell label"
+ title="Hide the legend when the plot is small"
>Hide when plot small</div>
- <div class="grid-cell value"><input v-model="hideLegendWhenSmall"
- type="checkbox"
- @change="updateForm('hideLegendWhenSmall')"
+ <div class="grid-cell value"><input
+ v-model="hideLegendWhenSmall"
+ type="checkbox"
+ @change="updateForm('hideLegendWhenSmall')"
></div>
</li>
<li class="grid-row">
- <div class="grid-cell label"
- title="Show the legend expanded by default"
+ <div
+ class="grid-cell label"
+ title="Show the legend expanded by default"
>Expand by default</div>
- <div class="grid-cell value"><input v-model="expandByDefault"
- type="checkbox"
- @change="updateForm('expandByDefault')"
+ <div class="grid-cell value"><input
+ v-model="expandByDefault"
+ type="checkbox"
+ @change="updateForm('expandByDefault')"
></div>
</li>
<li class="grid-row">
- <div class="grid-cell label"
- title="What to display in the legend when it's collapsed."
+ <div
+ class="grid-cell label"
+ title="What to display in the legend when it's collapsed."
>When collapsed show</div>
<div class="grid-cell value">
- <select v-model="valueToShowWhenCollapsed"
- @change="updateForm('valueToShowWhenCollapsed')"
+ <select
+ v-model="valueToShowWhenCollapsed"
+ @change="updateForm('valueToShowWhenCollapsed')"
>
<option value="none">Nothing</option>
<option value="nearestTimestamp">Nearest timestamp</option>
@@ -51,30 +59,36 @@
</div>
</li>
<li class="grid-row">
- <div class="grid-cell label"
- title="What to display in the legend when it's expanded."
+ <div
+ class="grid-cell label"
+ title="What to display in the legend when it's expanded."
>When expanded show</div>
<div class="grid-cell value">
<ul>
- <li><input v-model="showTimestampWhenExpanded"
- type="checkbox"
- @change="updateForm('showTimestampWhenExpanded')"
+ <li><input
+ v-model="showTimestampWhenExpanded"
+ type="checkbox"
+ @change="updateForm('showTimestampWhenExpanded')"
> Nearest timestamp</li>
- <li><input v-model="showValueWhenExpanded"
- type="checkbox"
- @change="updateForm('showValueWhenExpanded')"
+ <li><input
+ v-model="showValueWhenExpanded"
+ type="checkbox"
+ @change="updateForm('showValueWhenExpanded')"
> Nearest value</li>
- <li><input v-model="showMinimumWhenExpanded"
- type="checkbox"
- @change="updateForm('showMinimumWhenExpanded')"
+ <li><input
+ v-model="showMinimumWhenExpanded"
+ type="checkbox"
+ @change="updateForm('showMinimumWhenExpanded')"
> Minimum value</li>
- <li><input v-model="showMaximumWhenExpanded"
- type="checkbox"
- @change="updateForm('showMaximumWhenExpanded')"
+ <li><input
+ v-model="showMaximumWhenExpanded"
+ type="checkbox"
+ @change="updateForm('showMaximumWhenExpanded')"
> Maximum value</li>
- <li><input v-model="showUnitsWhenExpanded"
- type="checkbox"
- @change="updateForm('showUnitsWhenExpanded')"
+ <li><input
+ v-model="showUnitsWhenExpanded"
+ type="checkbox"
+ @change="updateForm('showUnitsWhenExpanded')"
> Units</li>
</ul>
diff --git a/src/plugins/plot/inspector/forms/SeriesForm.vue b/src/plugins/plot/inspector/forms/SeriesForm.vue
index d9a97b230..2a7ff4d2b 100644
--- a/src/plugins/plot/inspector/forms/SeriesForm.vue
+++ b/src/plugins/plot/inspector/forms/SeriesForm.vue
@@ -1,40 +1,48 @@
<template>
<ul>
- <li class="c-tree__item menus-to-left"
+ <li
+ class="c-tree__item menus-to-left"
:class="isAliasCss"
>
- <span class="c-disclosure-triangle is-enabled flex-elem"
- :class="expandedCssClass"
- @click="toggleExpanded"
+ <span
+ class="c-disclosure-triangle is-enabled flex-elem"
+ :class="expandedCssClass"
+ @click="toggleExpanded"
>
</span>
<div :class="objectLabelCss">
- <div class="c-object-label__type-icon"
- :class="[seriesCss]"
+ <div
+ class="c-object-label__type-icon"
+ :class="[seriesCss]"
>
- <span class="is-status__indicator"
- title="This item is missing or suspect"
+ <span
+ class="is-status__indicator"
+ title="This item is missing or suspect"
></span>
</div>
<div class="c-object-label__name">{{ series.domainObject.name }}</div>
</div>
</li>
- <ul v-show="expanded"
+ <ul
+ v-show="expanded"
class="grid-properties js-plot-options-edit-properties"
>
<li class="grid-row">
<!-- Value to be displayed -->
- <div class="grid-cell label"
- title="The field to be plotted as a value for this series."
+ <div
+ class="grid-cell label"
+ title="The field to be plotted as a value for this series."
>Value</div>
<div class="grid-cell value">
- <select v-model="yKey"
- @change="updateForm('yKey')"
+ <select
+ v-model="yKey"
+ @change="updateForm('yKey')"
>
- <option v-for="option in yKeyOptions"
- :key="option.value"
- :value="option.value"
- :selected="option.value == yKey"
+ <option
+ v-for="option in yKeyOptions"
+ :key="option.value"
+ :value="option.value"
+ :selected="option.value == yKey"
>
{{ option.name }}
</option>
@@ -42,12 +50,14 @@
</div>
</li>
<li class="grid-row">
- <div class="grid-cell label"
- title="The rendering method to join lines for this series."
+ <div
+ class="grid-cell label"
+ title="The rendering method to join lines for this series."
>Line Method</div>
<div class="grid-cell value">
- <select v-model="interpolate"
- @change="updateForm('interpolate')"
+ <select
+ v-model="interpolate"
+ @change="updateForm('interpolate')"
>
<option value="none">None</option>
<option value="linear">Linear interpolate</option>
@@ -56,13 +66,15 @@
</div>
</li>
<li class="grid-row">
- <div class="grid-cell label"
- title="Whether markers are displayed."
+ <div
+ class="grid-cell label"
+ title="Whether markers are displayed."
>Markers</div>
<div class="grid-cell value">
- <input v-model="markers"
- type="checkbox"
- @change="updateForm('markers')"
+ <input
+ v-model="markers"
+ type="checkbox"
+ @change="updateForm('markers')"
>
<select
v-show="markers"
@@ -81,47 +93,56 @@
</div>
</li>
<li class="grid-row">
- <div class="grid-cell label"
- title="Display markers visually denoting points in alarm."
+ <div
+ class="grid-cell label"
+ title="Display markers visually denoting points in alarm."
>Alarm Markers</div>
<div class="grid-cell value">
- <input v-model="alarmMarkers"
- type="checkbox"
- @change="updateForm('alarmMarkers')"
+ <input
+ v-model="alarmMarkers"
+ type="checkbox"
+ @change="updateForm('alarmMarkers')"
>
</div>
</li>
<li class="grid-row">
- <div class="grid-cell label"
- title="Display limit lines"
+ <div
+ class="grid-cell label"
+ title="Display limit lines"
>Limit lines</div>
<div class="grid-cell value">
- <input v-model="limitLines"
- type="checkbox"
- @change="updateForm('limitLines')"
+ <input
+ v-model="limitLines"
+ type="checkbox"
+ @change="updateForm('limitLines')"
>
</div>
</li>
- <li v-show="markers || alarmMarkers"
+ <li
+ v-show="markers || alarmMarkers"
class="grid-row"
>
- <div class="grid-cell label"
- title="The size of regular and alarm markers for this series."
+ <div
+ class="grid-cell label"
+ title="The size of regular and alarm markers for this series."
>Marker Size:</div>
- <div class="grid-cell value"><input v-model="markerSize"
- class="c-input--flex"
- type="text"
- @change="updateForm('markerSize')"
+ <div class="grid-cell value"><input
+ v-model="markerSize"
+ class="c-input--flex"
+ type="text"
+ @change="updateForm('markerSize')"
></div>
</li>
- <li v-show="interpolate !== 'none' || markers"
+ <li
+ v-show="interpolate !== 'none' || markers"
class="grid-row"
>
- <ColorSwatch :current-color="currentColor"
- edit-title="Manually set the plot line and marker color for this series."
- view-title="The plot line and marker color for this series."
- short-label="Color"
- @colorSet="setColor"
+ <ColorSwatch
+ :current-color="currentColor"
+ edit-title="Manually set the plot line and marker color for this series."
+ view-title="The plot line and marker color for this series."
+ short-label="Color"
+ @colorSet="setColor"
/>
</li>
</ul>
@@ -277,28 +298,45 @@ export default {
this.series.set('color', color);
- const getPath = this.dynamicPathForKey('color');
- const seriesColorPath = getPath(this.domainObject, this.series);
+ if (!this.domainObject.configuration || !this.domainObject.configuration.series) {
+ this.$emit('seriesUpdated', {
+ identifier: this.domainObject.identifier,
+ path: `series.color`,
+ value: color.asHexString()
+ });
+ } else {
+ const getPath = this.dynamicPathForKey('color');
+ const seriesColorPath = getPath(this.domainObject, this.series);
- this.openmct.objects.mutate(
- this.domainObject,
- seriesColorPath,
- color.asHexString()
- );
+ this.openmct.objects.mutate(
+ this.domainObject,
+ seriesColorPath,
+ color.asHexString()
+ );
+ }
if (otherSeriesWithColor) {
otherSeriesWithColor.set('color', oldColor);
- const otherSeriesColorPath = getPath(
- this.domainObject,
- otherSeriesWithColor
- );
+ if (!this.domainObject.configuration || !this.domainObject.configuration.series) {
+ this.$emit('seriesUpdated', {
+ identifier: this.domainObject.identifier,
+ path: `series.color`,
+ value: oldColor.asHexString()
+ });
+ } else {
+ const getPath = this.dynamicPathForKey('color');
+ const otherSeriesColorPath = getPath(
+ this.domainObject,
+ otherSeriesWithColor
+ );
- this.openmct.objects.mutate(
- this.domainObject,
- otherSeriesColorPath,
- oldColor.asHexString()
- );
+ this.openmct.objects.mutate(
+ this.domainObject,
+ otherSeriesColorPath,
+ oldColor.asHexString()
+ );
+ }
}
},
toggleExpanded() {
@@ -322,11 +360,19 @@ export default {
if (!_.isEqual(coerce(newVal, formField.coerce), coerce(oldVal, formField.coerce))) {
this.series.set(formKey, coerce(newVal, formField.coerce));
if (path) {
- this.openmct.objects.mutate(
- this.domainObject,
- path(this.domainObject, this.series),
- coerce(newVal, formField.coerce)
- );
+ if (!this.domainObject.configuration || !this.domainObject.configuration.series) {
+ this.$emit('seriesUpdated', {
+ identifier: this.domainObject.identifier,
+ path: `series.${formKey}`,
+ value: coerce(newVal, formField.coerce)
+ });
+ } else {
+ this.openmct.objects.mutate(
+ this.domainObject,
+ path(this.domainObject, this.series),
+ coerce(newVal, formField.coerce)
+ );
+ }
}
}
},
diff --git a/src/plugins/plot/inspector/forms/YAxisForm.vue b/src/plugins/plot/inspector/forms/YAxisForm.vue
index c96129c10..d852e5b8b 100644
--- a/src/plugins/plot/inspector/forms/YAxisForm.vue
+++ b/src/plugins/plot/inspector/forms/YAxisForm.vue
@@ -3,71 +3,97 @@
<ul class="l-inspector-part">
<h2>Y Axis</h2>
<li class="grid-row">
- <div class="grid-cell label"
- title="Manually override how the Y axis is labeled."
+ <div
+ class="grid-cell label"
+ title="Manually override how the Y axis is labeled."
>Label</div>
- <div class="grid-cell value"><input v-model="label"
- class="c-input--flex"
- type="text"
- @change="updateForm('label')"
+ <div class="grid-cell value"><input
+ v-model="label"
+ class="c-input--flex"
+ type="text"
+ @change="updateForm('label')"
></div>
</li>
- </ul>
- <ul class="l-inspector-part">
- <h2>Y Axis Scaling</h2>
<li class="grid-row">
- <div class="grid-cell label"
- title="Automatically scale the Y axis to keep all values in view."
+ <div
+ class="grid-cell label"
+ title="Enable log mode."
+ >
+ Log mode
+ </div>
+ <div class="grid-cell value">
+ <!-- eslint-disable-next-line vue/html-self-closing -->
+ <input
+ v-model="logMode"
+ type="checkbox"
+ @change="updateForm('logMode')"
+ />
+ </div>
+ </li>
+ <li class="grid-row">
+ <div
+ class="grid-cell label"
+ title="Automatically scale the Y axis to keep all values in view."
>Auto scale</div>
- <div class="grid-cell value"><input v-model="autoscale"
- type="checkbox"
- @change="updateForm('autoscale')"
+ <div class="grid-cell value"><input
+ v-model="autoscale"
+ type="checkbox"
+ @change="updateForm('autoscale')"
></div>
</li>
- <li v-show="autoscale"
+ <li
+ v-show="autoscale"
class="grid-row"
>
- <div class="grid-cell label"
- title="Percentage of padding above and below plotted min and max values. 0.1, 1.0, etc."
+ <div
+ class="grid-cell label"
+ title="Percentage of padding above and below plotted min and max values. 0.1, 1.0, etc."
>
Padding</div>
<div class="grid-cell value">
- <input v-model="autoscalePadding"
- class="c-input--flex"
- type="text"
- @change="updateForm('autoscalePadding')"
+ <input
+ v-model="autoscalePadding"
+ class="c-input--flex"
+ type="text"
+ @change="updateForm('autoscalePadding')"
>
</div>
</li>
</ul>
- <ul v-show="!autoscale"
+ <ul
+ v-show="!autoscale"
class="l-inspector-part"
>
- <div v-show="!autoscale && validation.range"
- class="grid-span-all form-error"
+ <div
+ v-show="!autoscale && validationErrors.range"
+ class="grid-span-all form-error"
>
- {{ validation.range }}
+ {{ validationErrors.range }}
</div>
<li class="grid-row force-border">
- <div class="grid-cell label"
- title="Minimum Y axis value."
+ <div
+ class="grid-cell label"
+ title="Minimum Y axis value."
>Minimum Value</div>
<div class="grid-cell value">
- <input v-model="rangeMin"
- class="c-input--flex"
- type="number"
- @change="updateForm('range')"
+ <input
+ v-model="rangeMin"
+ class="c-input--flex"
+ type="number"
+ @change="updateForm('range')"
>
</div>
</li>
<li class="grid-row">
- <div class="grid-cell label"
- title="Maximum Y axis value."
+ <div
+ class="grid-cell label"
+ title="Maximum Y axis value."
>Maximum Value</div>
- <div class="grid-cell value"><input v-model="rangeMax"
- class="c-input--flex"
- type="number"
- @change="updateForm('range')"
+ <div class="grid-cell value"><input
+ v-model="rangeMax"
+ class="c-input--flex"
+ type="number"
+ @change="updateForm('range')"
></div>
</li>
</ul>
@@ -75,7 +101,7 @@
</template>
<script>
-import { objectPath, validate, coerce } from "./formUtil";
+import { objectPath } from "./formUtil";
import _ from "lodash";
export default {
@@ -92,10 +118,11 @@ export default {
return {
label: '',
autoscale: '',
+ logMode: false,
autoscalePadding: '',
rangeMin: '',
rangeMax: '',
- validation: {}
+ validationErrors: {}
};
},
mounted() {
@@ -104,38 +131,35 @@ export default {
},
methods: {
initialize: function () {
- this.fields = [
- {
- modelProp: 'label',
+ this.fields = {
+ label: {
objectPath: 'configuration.yAxis.label'
},
- {
- modelProp: 'autoscale',
+ autoscale: {
coerce: Boolean,
objectPath: 'configuration.yAxis.autoscale'
},
- {
- modelProp: 'autoscalePadding',
+ autoscalePadding: {
coerce: Number,
objectPath: 'configuration.yAxis.autoscalePadding'
},
- {
- modelProp: 'range',
+ logMode: {
+ coerce: Boolean,
+ objectPath: 'configuration.yAxis.logMode'
+ },
+ range: {
objectPath: 'configuration.yAxis.range',
coerce: function coerceRange(range) {
- if (!range) {
- return {
- min: 0,
- max: 0
- };
- }
+ const newRange = {
+ min: -1,
+ max: 1
+ };
- const newRange = {};
- if (typeof range.min !== 'undefined' && range.min !== null) {
+ if (range && typeof range.min !== 'undefined' && range.min !== null) {
newRange.min = Number(range.min);
}
- if (typeof range.max !== 'undefined' && range.max !== null) {
+ if (range && typeof range.max !== 'undefined' && range.max !== null) {
newRange.max = Number(range.max);
}
@@ -165,28 +189,18 @@ export default {
if (Number(range.min) > Number(range.max)) {
return 'Minimum must be less than Maximum.';
}
-
- if (model.get('autoscale')) {
- return false;
- }
-
- return true;
}
}
- ];
+ };
},
initFormValues() {
this.label = this.yAxis.get('label');
this.autoscale = this.yAxis.get('autoscale');
+ this.logMode = this.yAxis.get('logMode');
this.autoscalePadding = this.yAxis.get('autoscalePadding');
- const range = this.yAxis.get('range');
- if (!range) {
- this.rangeMin = undefined;
- this.rangeMax = undefined;
- } else {
- this.rangeMin = range.min;
- this.rangeMax = range.max;
- }
+ const range = this.yAxis.get('range') ?? this.yAxis.get('displayRange');
+ this.rangeMin = range?.min;
+ this.rangeMax = range?.max;
},
updateForm(formKey) {
let newVal;
@@ -199,27 +213,37 @@ export default {
newVal = this[formKey];
}
- const oldVal = this.yAxis.get(formKey);
- const formField = this.fields.find((field) => field.modelProp === formKey);
-
- const path = objectPath(formField.objectPath);
- const validationResult = validate(newVal, this.yAxis, formField.validate);
- if (validationResult === true) {
- delete this.validation[formKey];
- } else {
- this.validation[formKey] = validationResult;
+ let oldVal = this.yAxis.get(formKey);
+ const formField = this.fields[formKey];
+ const validationError = formField.validate?.(newVal, this.yAxis);
+ this.validationErrors[formKey] = validationError;
+ if (validationError) {
return;
}
- if (!_.isEqual(coerce(newVal, formField.coerce), coerce(oldVal, formField.coerce))) {
- this.yAxis.set(formKey, coerce(newVal, formField.coerce));
+ newVal = formField.coerce?.(newVal) ?? newVal;
+ oldVal = formField.coerce?.(oldVal) ?? oldVal;
+
+ const path = objectPath(formField.objectPath);
+ if (!_.isEqual(newVal, oldVal)) {
+ // We mutate the model for the plots first PlotConfigurationModel - this triggers changes that affects the plot behavior
+ this.yAxis.set(formKey, newVal);
+ // Then we mutate the domain object configuration to persist the settings
if (path) {
- this.openmct.objects.mutate(
- this.domainObject,
- path(this.domainObject, this.yAxis),
- coerce(newVal, formField.coerce)
- );
+ if (!this.domainObject.configuration || !this.domainObject.configuration.series) {
+ this.$emit('seriesUpdated', {
+ identifier: this.domainObject.identifier,
+ path: `yAxis.${formKey}`,
+ value: newVal
+ });
+ } else {
+ this.openmct.objects.mutate(
+ this.domainObject,
+ path(this.domainObject, this.yAxis),
+ newVal
+ );
+ }
}
}
}
diff --git a/src/plugins/plot/inspector/forms/formUtil.js b/src/plugins/plot/inspector/forms/formUtil.js
index 9231fa5c6..661d9c4b9 100644
--- a/src/plugins/plot/inspector/forms/formUtil.js
+++ b/src/plugins/plot/inspector/forms/formUtil.js
@@ -15,15 +15,5 @@ export function validate(value, model, validateFunc) {
}
export function objectPath(path) {
- if (path) {
- if (typeof path !== "function") {
- const staticObjectPath = path;
-
- return function (object, model) {
- return staticObjectPath;
- };
- }
-
- return path;
- }
+ return path && typeof path !== 'function' ? () => path : path;
}
diff --git a/src/plugins/plot/legend/PlotLegend.vue b/src/plugins/plot/legend/PlotLegend.vue
index 2189f23c6..1bdf4b3bb 100644
--- a/src/plugins/plot/legend/PlotLegend.vue
+++ b/src/plugins/plot/legend/PlotLegend.vue
@@ -1,5 +1,5 @@
<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
+ Open MCT, Copyright (c) 2014-2022, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
@@ -20,43 +20,51 @@
at runtime from the About dialog for additional information.
-->
<template>
-<div class="c-plot-legend gl-plot-legend"
- :class="{
- 'hover-on-plot': !!highlights.length,
- 'is-legend-hidden': isLegendHidden
- }"
+<div
+ class="c-plot-legend gl-plot-legend"
+ :class="{
+ 'hover-on-plot': !!highlights.length,
+ 'is-legend-hidden': isLegendHidden
+ }"
>
- <div class="c-plot-legend__view-control gl-plot-legend__view-control c-disclosure-triangle is-enabled"
- :class="{ 'c-disclosure-triangle--expanded': isLegendExpanded }"
- @click="expandLegend"
+ <div
+ class="c-plot-legend__view-control gl-plot-legend__view-control c-disclosure-triangle is-enabled"
+ :class="{ 'c-disclosure-triangle--expanded': isLegendExpanded }"
+ @click="expandLegend"
>
</div>
- <div class="c-plot-legend__wrapper"
- :class="{ 'is-cursor-locked': cursorLocked }"
+ <div
+ class="c-plot-legend__wrapper"
+ :class="{ 'is-cursor-locked': cursorLocked }"
>
<!-- COLLAPSED PLOT LEGEND -->
- <div class="plot-wrapper-collapsed-legend"
- :class="{'is-cursor-locked': cursorLocked }"
+ <div
+ class="plot-wrapper-collapsed-legend"
+ :class="{'is-cursor-locked': cursorLocked }"
>
- <div class="c-state-indicator__alert-cursor-lock icon-cursor-lock"
- title="Cursor is point locked. Click anywhere in the plot to unlock."
+ <div
+ class="c-state-indicator__alert-cursor-lock icon-cursor-lock"
+ title="Cursor is point locked. Click anywhere in the plot to unlock."
></div>
- <plot-legend-item-collapsed v-for="seriesObject in series"
- :key="seriesObject.keyString"
- :highlights="highlights"
- :value-to-show-when-collapsed="legend.get('valueToShowWhenCollapsed')"
- :series-object="seriesObject"
- @legendHoverChanged="legendHoverChanged"
+ <plot-legend-item-collapsed
+ v-for="(seriesObject, seriesIndex) in series"
+ :key="`seriesObject.keyString-${seriesIndex}`"
+ :highlights="highlights"
+ :value-to-show-when-collapsed="legend.get('valueToShowWhenCollapsed')"
+ :series-object="seriesObject"
+ @legendHoverChanged="legendHoverChanged"
/>
</div>
<!-- EXPANDED PLOT LEGEND -->
- <div class="plot-wrapper-expanded-legend"
- :class="{'is-cursor-locked': cursorLocked }"
+ <div
+ class="plot-wrapper-expanded-legend"
+ :class="{'is-cursor-locked': cursorLocked }"
>
- <div class="c-state-indicator__alert-cursor-lock--verbose icon-cursor-lock"
- title="Click anywhere in the plot to unlock."
+ <div
+ class="c-state-indicator__alert-cursor-lock--verbose icon-cursor-lock"
+ title="Click anywhere in the plot to unlock."
> Cursor locked to point</div>
<table>
<thead>
@@ -71,12 +79,14 @@
<th v-if="showUnitsWhenExpanded">
Unit
</th>
- <th v-if="showMinimumWhenExpanded"
+ <th
+ v-if="showMinimumWhenExpanded"
class="mobile-hide"
>
Min
</th>
- <th v-if="showMaximumWhenExpanded"
+ <th
+ v-if="showMaximumWhenExpanded"
class="mobile-hide"
>
Max
@@ -84,12 +94,13 @@
</tr>
</thead>
<tbody>
- <plot-legend-item-expanded v-for="seriesObject in series"
- :key="seriesObject.keyString"
- :series-object="seriesObject"
- :highlights="highlights"
- :legend="legend"
- @legendHoverChanged="legendHoverChanged"
+ <plot-legend-item-expanded
+ v-for="(seriesObject, seriesIndex) in series"
+ :key="`seriesObject.keyString-${seriesIndex}`"
+ :series-object="seriesObject"
+ :highlights="highlights"
+ :legend="legend"
+ @legendHoverChanged="legendHoverChanged"
/>
</tbody>
</table>
diff --git a/src/plugins/plot/legend/PlotLegendItemCollapsed.vue b/src/plugins/plot/legend/PlotLegendItemCollapsed.vue
index 108883f44..197f9da9d 100644
--- a/src/plugins/plot/legend/PlotLegendItemCollapsed.vue
+++ b/src/plugins/plot/legend/PlotLegendItemCollapsed.vue
@@ -1,5 +1,5 @@
<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
+ Open MCT, Copyright (c) 2014-2022, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
@@ -20,26 +20,30 @@
at runtime from the About dialog for additional information.
-->
<template>
-<div class="plot-legend-item"
- :class="{
- 'is-status--missing': isMissing
- }"
- @mouseover="toggleHover(true)"
- @mouseleave="toggleHover(false)"
+<div
+ class="plot-legend-item"
+ :class="{
+ 'is-status--missing': isMissing
+ }"
+ @mouseover="toggleHover(true)"
+ @mouseleave="toggleHover(false)"
>
<div class="plot-series-swatch-and-name">
- <span class="plot-series-color-swatch"
- :style="{ 'background-color': colorAsHexString }"
+ <span
+ class="plot-series-color-swatch"
+ :style="{ 'background-color': colorAsHexString }"
>
</span>
- <span class="is-status__indicator"
- title="This item is missing or suspect"
+ <span
+ class="is-status__indicator"
+ title="This item is missing or suspect"
></span>
<span class="plot-series-name">{{ nameWithUnit }}</span>
</div>
- <div v-show="!!highlights.length && (valueToShowWhenCollapsed !== 'none')"
- class="plot-series-value hover-value-enabled"
- :class="[{ 'cursor-hover': notNearest }, valueToDisplayWhenCollapsedClass, mctLimitStateClass]"
+ <div
+ v-show="!!highlights.length && (valueToShowWhenCollapsed !== 'none' && valueToShowWhenCollapsed !== 'units')"
+ class="plot-series-value hover-value-enabled"
+ :class="[{ 'cursor-hover': notNearest }, valueToDisplayWhenCollapsedClass, mctLimitStateClass]"
>
<span v-if="valueToShowWhenCollapsed === 'nearestValue'">{{ formattedYValue }}</span>
<span v-else-if="valueToShowWhenCollapsed === 'nearestTimestamp'">{{ formattedXValue }}</span>
diff --git a/src/plugins/plot/legend/PlotLegendItemExpanded.vue b/src/plugins/plot/legend/PlotLegendItemExpanded.vue
index 1b3ecb64a..1b3157172 100644
--- a/src/plugins/plot/legend/PlotLegendItemExpanded.vue
+++ b/src/plugins/plot/legend/PlotLegendItemExpanded.vue
@@ -1,5 +1,5 @@
<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
+ Open MCT, Copyright (c) 2014-2022, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
@@ -29,12 +29,14 @@
@mouseleave="toggleHover(false)"
>
<td class="plot-series-swatch-and-name">
- <span class="plot-series-color-swatch"
- :style="{ 'background-color': colorAsHexString }"
+ <span
+ class="plot-series-color-swatch"
+ :style="{ 'background-color': colorAsHexString }"
>
</span>
- <span class="is-status__indicator"
- title="This item is missing or suspect"
+ <span
+ class="is-status__indicator"
+ title="This item is missing or suspect"
></span>
<span class="plot-series-name">{{ name }}</span>
</td>
@@ -45,8 +47,9 @@
</span>
</td>
<td v-if="showValueWhenExpanded">
- <span class="plot-series-value cursor-hover hover-value-enabled"
- :class="[mctLimitStateClass]"
+ <span
+ class="plot-series-value cursor-hover hover-value-enabled"
+ :class="[mctLimitStateClass]"
>
{{ formattedYValue }}
</span>
@@ -56,14 +59,16 @@
{{ unit }}
</span>
</td>
- <td v-if="showMinimumWhenExpanded"
+ <td
+ v-if="showMinimumWhenExpanded"
class="mobile-hide"
>
<span class="plot-series-value">
{{ formattedMinY }}
</span>
</td>
- <td v-if="showMaximumWhenExpanded"
+ <td
+ v-if="showMaximumWhenExpanded"
class="mobile-hide"
>
<span class="plot-series-value">
diff --git a/src/plugins/plot/lib/eventHelpers.js b/src/plugins/plot/lib/eventHelpers.js
index 34600851a..2b0ad0b57 100644
--- a/src/plugins/plot/lib/eventHelpers.js
+++ b/src/plugins/plot/lib/eventHelpers.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -90,3 +90,10 @@ const helperFunctions = {
};
export default helperFunctions;
+
+/**
+@typedef {{
+ listenTo: (object: any, event: any, callback: any, context: any) => void
+ stopListening: (object: any, event: any, callback: any, context: any) => void
+}} EventHelpers
+*/
diff --git a/src/plugins/plot/mathUtils.js b/src/plugins/plot/mathUtils.js
new file mode 100644
index 000000000..38dc35618
--- /dev/null
+++ b/src/plugins/plot/mathUtils.js
@@ -0,0 +1,44 @@
+/** The natural number `e`. */
+export const e = Math.exp(1);
+
+/**
+Returns the logarithm of a number, using the given base or the natural number
+`e` as base if not specified.
+@param {number} n
+@param {number=} base log base, defaults to e
+*/
+export function log(n, base = e) {
+ if (base === e) {
+ return Math.log(n);
+ }
+
+ return Math.log(n) / Math.log(base);
+}
+
+/**
+Returns the inverse of the logarithm of a number, using the given base or the
+natural number `e` as base if not specified.
+@param {number} n
+@param {number=} base log base, defaults to e
+*/
+export function antilog(n, base = e) {
+ return Math.pow(base, n);
+}
+
+/**
+A symmetric logarithm function. See https://github.com/nasa/openmct/issues/2297#issuecomment-1032914258
+@param {number} n
+@param {number=} base log base, defaults to e
+*/
+export function symlog(n, base = e) {
+ return Math.sign(n) * log(Math.abs(n) + 1, base);
+}
+
+/**
+An inverse symmetric logarithm function. See https://github.com/nasa/openmct/issues/2297#issuecomment-1032914258
+@param {number} n
+@param {number=} base log base, defaults to e
+*/
+export function antisymlog(n, base = e) {
+ return Math.sign(n) * (antilog(Math.abs(n), base) - 1);
+}
diff --git a/src/plugins/plot/overlayPlot/OverlayPlotViewProvider.js b/src/plugins/plot/overlayPlot/OverlayPlotViewProvider.js
index 577463815..e34bb59d2 100644
--- a/src/plugins/plot/overlayPlot/OverlayPlotViewProvider.js
+++ b/src/plugins/plot/overlayPlot/OverlayPlotViewProvider.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -65,9 +65,16 @@ export default function OverlayPlotViewProvider(openmct) {
}
};
},
- template: '<plot :options="options"></plot>'
+ template: '<plot ref="plotComponent" :options="options"></plot>'
});
},
+ getViewContext() {
+ if (!component) {
+ return {};
+ }
+
+ return component.$refs.plotComponent.getViewContext();
+ },
destroy: function () {
component.$destroy();
component = undefined;
diff --git a/src/plugins/plot/plugin.js b/src/plugins/plot/plugin.js
index e8f618335..5eacd34fd 100644
--- a/src/plugins/plot/plugin.js
+++ b/src/plugins/plot/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -25,6 +25,9 @@ import StackedPlotViewProvider from './stackedPlot/StackedPlotViewProvider';
import PlotsInspectorViewProvider from './inspector/PlotsInspectorViewProvider';
import OverlayPlotCompositionPolicy from './overlayPlot/OverlayPlotCompositionPolicy';
import StackedPlotCompositionPolicy from './stackedPlot/StackedPlotCompositionPolicy';
+import PlotViewActions from "./actions/ViewActions";
+import StackedPlotsInspectorViewProvider from "./inspector/StackedPlotsInspectorViewProvider";
+import stackedPlotConfigurationInterceptor from "./stackedPlot/stackedPlotConfigurationInterceptor";
export default function () {
return function install(openmct) {
@@ -38,9 +41,8 @@ export default function () {
initialize: function (domainObject) {
domainObject.composition = [];
domainObject.configuration = {
- series: [],
- yAxis: {},
- xAxis: {}
+ //series is an array of objects of type: {identifier, series: {color...}, yAxis:{}}
+ series: []
};
},
priority: 891
@@ -54,19 +56,29 @@ export default function () {
creatable: true,
initialize: function (domainObject) {
domainObject.composition = [];
- domainObject.configuration = {};
+ domainObject.configuration = {
+ series: [],
+ yAxis: {},
+ xAxis: {}
+ };
},
priority: 890
});
+ stackedPlotConfigurationInterceptor(openmct);
+
openmct.objectViews.addProvider(new StackedPlotViewProvider(openmct));
openmct.objectViews.addProvider(new OverlayPlotViewProvider(openmct));
openmct.objectViews.addProvider(new PlotViewProvider(openmct));
openmct.inspectorViews.addProvider(new PlotsInspectorViewProvider(openmct));
+ openmct.inspectorViews.addProvider(new StackedPlotsInspectorViewProvider(openmct));
openmct.composition.addPolicy(new OverlayPlotCompositionPolicy(openmct).allow);
openmct.composition.addPolicy(new StackedPlotCompositionPolicy(openmct).allow);
+
+ PlotViewActions.forEach(action => {
+ openmct.actions.register(action);
+ });
};
}
-
diff --git a/src/plugins/plot/pluginSpec.js b/src/plugins/plot/pluginSpec.js
index 19d9e5367..36859a50d 100644
--- a/src/plugins/plot/pluginSpec.js
+++ b/src/plugins/plot/pluginSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -23,7 +23,6 @@
import {createMouseEvent, createOpenMct, resetApplicationState, spyOnBuiltins} from "utils/testing";
import PlotVuePlugin from "./plugin";
import Vue from "vue";
-import StackedPlot from "./stackedPlot/StackedPlot.vue";
import configStore from "./configuration/ConfigStore";
import EventEmitter from "EventEmitter";
import PlotOptions from "./inspector/PlotOptions.vue";
@@ -348,14 +347,20 @@ describe("the plugin", function () {
}
};
+ openmct.router.path = [testTelemetryObject];
+
applicableViews = openmct.objectViews.get(testTelemetryObject, mockObjectPath);
plotViewProvider = applicableViews.find((viewProvider) => viewProvider.key === "plot-single");
- plotView = plotViewProvider.view(testTelemetryObject, [testTelemetryObject]);
+ plotView = plotViewProvider.view(testTelemetryObject, []);
plotView.show(child, true);
return Vue.nextTick();
});
+ afterEach(() => {
+ openmct.router.path = null;
+ });
+
it("Makes only one request for telemetry on load", () => {
expect(openmct.telemetry.request).toHaveBeenCalledTimes(1);
});
@@ -389,7 +394,7 @@ describe("the plugin", function () {
expect(xAxisElement.length).toBe(1);
let ticks = xAxisElement[0].querySelectorAll(".gl-plot-tick");
- expect(ticks.length).toBe(5);
+ expect(ticks.length).toBe(9);
done();
});
@@ -523,318 +528,6 @@ describe("the plugin", function () {
});
});
- describe("The stacked plot view", () => {
- let testTelemetryObject;
- let testTelemetryObject2;
- let config;
- let stackedPlotObject;
- let component;
- let mockComposition;
- let plotViewComponentObject;
-
- beforeEach(() => {
-
- stackedPlotObject = {
- identifier: {
- namespace: "",
- key: "test-plot"
- },
- type: "telemetry.plot.stacked",
- name: "Test Stacked Plot"
- };
-
- testTelemetryObject = {
- identifier: {
- namespace: "",
- key: "test-object"
- },
- type: "test-object",
- name: "Test Object",
- telemetry: {
- values: [{
- key: "utc",
- format: "utc",
- name: "Time",
- hints: {
- domain: 1
- }
- }, {
- key: "some-key",
- name: "Some attribute",
- hints: {
- range: 1
- }
- }, {
- key: "some-other-key",
- name: "Another attribute",
- hints: {
- range: 2
- }
- }]
- }
- };
-
- testTelemetryObject2 = {
- identifier: {
- namespace: "",
- key: "test-object2"
- },
- type: "test-object",
- name: "Test Object2",
- telemetry: {
- values: [{
- key: "utc",
- format: "utc",
- name: "Time",
- hints: {
- domain: 1
- }
- }, {
- key: "some-key2",
- name: "Some attribute2",
- hints: {
- range: 1
- }
- }, {
- key: "some-other-key2",
- name: "Another attribute2",
- hints: {
- range: 2
- }
- }]
- }
- };
-
- mockComposition = new EventEmitter();
- mockComposition.load = () => {
- mockComposition.emit('add', testTelemetryObject);
-
- return [testTelemetryObject];
- };
-
- spyOn(openmct.composition, 'get').and.returnValue(mockComposition);
-
- let viewContainer = document.createElement("div");
- child.append(viewContainer);
- component = new Vue({
- el: viewContainer,
- components: {
- StackedPlot
- },
- provide: {
- openmct: openmct,
- domainObject: stackedPlotObject,
- composition: openmct.composition.get(stackedPlotObject),
- path: [stackedPlotObject]
- },
- template: "<stacked-plot></stacked-plot>"
- });
-
- return telemetryPromise
- .then(Vue.nextTick())
- .then(() => {
- plotViewComponentObject = component.$root.$children[0];
- const configId = openmct.objects.makeKeyString(testTelemetryObject.identifier);
- config = configStore.get(configId);
- });
- });
-
- it("Renders a collapsed legend for every telemetry", () => {
- let legend = element.querySelectorAll(".plot-wrapper-collapsed-legend .plot-series-name");
- expect(legend.length).toBe(1);
- expect(legend[0].innerHTML).toEqual("Test Object");
- });
-
- it("Renders an expanded legend for every telemetry", () => {
- let legendControl = element.querySelector(".c-plot-legend__view-control.gl-plot-legend__view-control.c-disclosure-triangle");
- const clickEvent = createMouseEvent("click");
-
- legendControl.dispatchEvent(clickEvent);
-
- let legend = element.querySelectorAll(".plot-wrapper-expanded-legend .plot-legend-item td");
- expect(legend.length).toBe(6);
- });
-
- it("Renders X-axis ticks for the telemetry object", (done) => {
- let xAxisElement = element.querySelectorAll(".gl-plot-axis-area.gl-plot-x .gl-plot-tick-wrapper");
- expect(xAxisElement.length).toBe(1);
-
- config.xAxis.set('displayRange', {
- min: 0,
- max: 4
- });
-
- Vue.nextTick(() => {
- let ticks = xAxisElement[0].querySelectorAll(".gl-plot-tick");
- expect(ticks.length).toBe(5);
-
- done();
- });
- });
-
- it("Renders Y-axis ticks for the telemetry object", (done) => {
- config.yAxis.set('displayRange', {
- min: 10,
- max: 20
- });
- Vue.nextTick(() => {
- let yAxisElement = element.querySelectorAll(".gl-plot-axis-area.gl-plot-y .gl-plot-tick-wrapper");
- expect(yAxisElement.length).toBe(1);
- let ticks = yAxisElement[0].querySelectorAll(".gl-plot-tick");
- expect(ticks.length).toBe(6);
- done();
- });
- });
-
- it("Renders Y-axis options for the telemetry object", () => {
- let yAxisElement = element.querySelectorAll(".gl-plot-axis-area.gl-plot-y .gl-plot-y-label__select");
- expect(yAxisElement.length).toBe(1);
- let options = yAxisElement[0].querySelectorAll("option");
- expect(options.length).toBe(2);
- expect(options[0].value).toBe("Some attribute");
- expect(options[1].value).toBe("Another attribute");
- });
-
- it("turns on cursor Guides all telemetry objects", (done) => {
- expect(plotViewComponentObject.cursorGuide).toBeFalse();
- plotViewComponentObject.toggleCursorGuide();
- Vue.nextTick(() => {
- expect(plotViewComponentObject.$children[0].component.$children[0].cursorGuide).toBeTrue();
- done();
- });
- });
-
- it("shows grid lines for all telemetry objects", () => {
- expect(plotViewComponentObject.gridLines).toBeTrue();
- let gridLinesContainer = element.querySelectorAll(".gl-plot-display-area .js-ticks");
- let visible = 0;
- gridLinesContainer.forEach(el => {
- if (el.style.display !== "none") {
- visible++;
- }
- });
- expect(visible).toBe(2);
- });
-
- it("hides grid lines for all telemetry objects", (done) => {
- expect(plotViewComponentObject.gridLines).toBeTrue();
- plotViewComponentObject.toggleGridLines();
- Vue.nextTick(() => {
- expect(plotViewComponentObject.gridLines).toBeFalse();
- let gridLinesContainer = element.querySelectorAll(".gl-plot-display-area .js-ticks");
- let visible = 0;
- gridLinesContainer.forEach(el => {
- if (el.style.display !== "none") {
- visible++;
- }
- });
- expect(visible).toBe(0);
- done();
- });
- });
-
- it('plots a new series when a new telemetry object is added', (done) => {
- mockComposition.emit('add', testTelemetryObject2);
- Vue.nextTick(() => {
- let legend = element.querySelectorAll(".plot-wrapper-collapsed-legend .plot-series-name");
- expect(legend.length).toBe(2);
- expect(legend[1].innerHTML).toEqual("Test Object2");
- done();
- });
- });
-
- it('removes plots from series when a telemetry object is removed', (done) => {
- mockComposition.emit('remove', testTelemetryObject.identifier);
- Vue.nextTick(() => {
- let legend = element.querySelectorAll(".plot-wrapper-collapsed-legend .plot-series-name");
- expect(legend.length).toBe(0);
- done();
- });
- });
-
- it("Changes the label of the y axis when the option changes", (done) => {
- let selectEl = element.querySelector('.gl-plot-y-label__select');
- selectEl.value = 'Another attribute';
- selectEl.dispatchEvent(new Event("change"));
-
- Vue.nextTick(() => {
- expect(config.yAxis.get('label')).toEqual('Another attribute');
- done();
- });
- });
-
- it("Renders a new series when added to one of the plots", (done) => {
- mockComposition.emit('add', testTelemetryObject2);
- Vue.nextTick(() => {
- let legend = element.querySelectorAll(".plot-wrapper-collapsed-legend .plot-series-name");
- expect(legend.length).toBe(2);
- expect(legend[1].innerHTML).toEqual("Test Object2");
- done();
- });
- });
-
- it("Adds a new point to the plot", (done) => {
- let originalLength = config.series.models[0].getSeriesData().length;
- config.series.models[0].add({
- utc: 2,
- 'some-key': 1,
- 'some-other-key': 2
- });
- Vue.nextTick(() => {
- const seriesData = config.series.models[0].getSeriesData();
- expect(seriesData.length).toEqual(originalLength + 1);
- done();
- });
- });
-
- it("updates the xscale", (done) => {
- config.xAxis.set('displayRange', {
- min: 0,
- max: 10
- });
- Vue.nextTick(() => {
- expect(plotViewComponentObject.$children[0].component.$children[0].xScale.domain()).toEqual({
- min: 0,
- max: 10
- });
- done();
- });
- });
-
- it("updates the yscale", (done) => {
- config.yAxis.set('displayRange', {
- min: 10,
- max: 20
- });
- Vue.nextTick(() => {
- expect(plotViewComponentObject.$children[0].component.$children[0].yScale.domain()).toEqual({
- min: 10,
- max: 20
- });
- done();
- });
- });
-
- describe('limits', () => {
-
- it('lines are not displayed by default', () => {
- let limitEl = element.querySelectorAll(".js-limit-area hr");
- expect(limitEl.length).toBe(0);
- });
-
- it('lines are displayed when configuration is set to true', (done) => {
- config.series.models[0].set('limitLines', true);
-
- Vue.nextTick(() => {
- let limitEl = element.querySelectorAll(".js-limit-area .js-limit-line");
- expect(limitEl.length).toBe(4);
- done();
- });
-
- });
- });
- });
-
describe('the inspector view', () => {
let component;
let viewComponentObject;
@@ -913,6 +606,7 @@ describe("the plugin", function () {
]
];
+ openmct.router.path = [testTelemetryObject];
mockComposition = new EventEmitter();
mockComposition.load = () => {
mockComposition.emit('add', testTelemetryObject);
@@ -951,6 +645,10 @@ describe("the plugin", function () {
});
});
+ afterEach(() => {
+ openmct.router.path = null;
+ });
+
describe('in view only mode', () => {
let browseOptionsEl;
let editOptionsEl;
@@ -1044,7 +742,9 @@ describe("the plugin", function () {
expandControl.dispatchEvent(clickEvent);
const yAxisProperties = editOptionsEl.querySelectorAll("div.grid-properties:first-of-type .l-inspector-part");
- expect(yAxisProperties.length).toEqual(3);
+
+ // TODO better test
+ expect(yAxisProperties.length).toEqual(2);
});
it('renders color palette options', () => {
@@ -1052,5 +752,24 @@ describe("the plugin", function () {
expect(colorSwatch).toBeDefined();
});
});
+
+ describe('limits', () => {
+
+ it('lines are not displayed by default', () => {
+ let limitEl = element.querySelectorAll(".js-limit-area .js-limit-line");
+ expect(limitEl.length).toBe(0);
+ });
+
+ xit('lines are displayed when configuration is set to true', (done) => {
+ config.series.models[0].set('limitLines', true);
+
+ Vue.nextTick(() => {
+ let limitEl = element.querySelectorAll(".js-limit-area .js-limit-line");
+ expect(limitEl.length).toBe(4);
+ done();
+ });
+
+ });
+ });
});
});
diff --git a/src/plugins/plot/stackedPlot/StackedPlot.vue b/src/plugins/plot/stackedPlot/StackedPlot.vue
index 3ff08582d..482744920 100644
--- a/src/plugins/plot/stackedPlot/StackedPlot.vue
+++ b/src/plugins/plot/stackedPlot/StackedPlot.vue
@@ -1,5 +1,5 @@
<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
+ Open MCT, Copyright (c) 2014-2022, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
@@ -21,48 +21,37 @@
-->
<template>
-<div class="c-plot c-plot--stacked holder holder-plot has-control-bar">
- <div v-show="!hideExportButtons && !options.compact"
- class="c-control-bar"
- >
- <span class="c-button-set c-button-set--strip-h">
- <button class="c-button icon-download"
- title="Export This View's Data as PNG"
- @click="exportPNG()"
- >
- <span class="c-button__label">PNG</span>
- </button>
- <button class="c-button"
- title="Export This View's Data as JPG"
- @click="exportJPG()"
- >
- <span class="c-button__label">JPG</span>
- </button>
- </span>
- <button class="c-button icon-crosshair"
- :class="{ 'is-active': cursorGuide }"
- title="Toggle cursor guides"
- @click="toggleCursorGuide"
- >
- </button>
- <button class="c-button"
- :class="{ 'icon-grid-on': gridLines, 'icon-grid-off': !gridLines }"
- title="Toggle grid lines"
- @click="toggleGridLines"
- >
- </button>
- </div>
+<div
+ v-if="loaded"
+ class="c-plot c-plot--stacked holder holder-plot has-control-bar"
+ :class="[plotLegendExpandedStateClass, plotLegendPositionClass]"
+>
+ <plot-legend
+ :cursor-locked="!!lockHighlightPoint"
+ :series="seriesModels"
+ :highlights="highlights"
+ :legend="legend"
+ @legendHoverChanged="legendHoverChanged"
+ />
<div class="l-view-section">
- <stacked-plot-item v-for="object in compositionObjects"
- :key="object.id"
- class="c-plot--stacked-container"
- :object="object"
- :options="options"
- :grid-lines="gridLines"
- :cursor-guide="cursorGuide"
- :plot-tick-width="maxTickWidth"
- @plotTickWidth="onTickWidthChange"
- @loadingUpdated="loadingUpdated"
+ <stacked-plot-item
+ v-for="object in compositionObjects"
+ :key="object.id"
+ class="c-plot--stacked-container"
+ :child-object="object"
+ :options="options"
+ :grid-lines="gridLines"
+ :color-palette="colorPalette"
+ :cursor-guide="cursorGuide"
+ :show-limit-line-labels="showLimitLineLabels"
+ :plot-tick-width="maxTickWidth"
+ @plotTickWidth="onTickWidthChange"
+ @loadingUpdated="loadingUpdated"
+ @cursorGuide="onCursorGuideChange"
+ @gridLines="onGridLinesChange"
+ @lockHighlightPoint="lockHighlightPointUpdated"
+ @highlights="highlightsUpdated"
+ @configLoaded="registerSeriesListeners"
/>
</div>
</div>
@@ -70,12 +59,19 @@
<script>
+import PlotConfigurationModel from '../configuration/PlotConfigurationModel';
+import configStore from '../configuration/ConfigStore';
+import ColorPalette from "@/ui/color/ColorPalette";
+
+import PlotLegend from "../legend/PlotLegend.vue";
import StackedPlotItem from './StackedPlotItem.vue';
import ImageExporter from '../../../exporters/ImageExporter';
+import eventHelpers from "@/plugins/plot/lib/eventHelpers";
export default {
components: {
- StackedPlotItem
+ StackedPlotItem,
+ PlotLegend
},
inject: ['openmct', 'domainObject', 'composition', 'path'],
props: {
@@ -87,16 +83,35 @@ export default {
}
},
data() {
+ this.seriesConfig = {};
+
return {
hideExportButtons: false,
cursorGuide: false,
gridLines: true,
loading: false,
compositionObjects: [],
- tickWidthMap: {}
+ tickWidthMap: {},
+ legend: {},
+ loaded: false,
+ lockHighlightPoint: false,
+ highlights: [],
+ seriesModels: [],
+ showLimitLineLabels: undefined,
+ colorPalette: new ColorPalette()
};
},
computed: {
+ plotLegendPositionClass() {
+ return `plot-legend-${this.config.legend.get('position')}`;
+ },
+ plotLegendExpandedStateClass() {
+ if (this.config.legend.get('expanded')) {
+ return 'plot-legend-expanded';
+ } else {
+ return 'plot-legend-collapsed';
+ }
+ },
maxTickWidth() {
return Math.max(...Object.values(this.tickWidthMap));
}
@@ -105,6 +120,13 @@ export default {
this.destroy();
},
mounted() {
+ eventHelpers.extend(this);
+
+ const configId = this.openmct.objects.makeKeyString(this.domainObject.identifier);
+ this.config = this.getConfig(configId);
+ this.legend = this.config.legend;
+
+ this.loaded = true;
this.imageExporter = new ImageExporter(this.openmct);
this.composition.on('add', this.addChild);
@@ -113,10 +135,29 @@ export default {
this.composition.load();
},
methods: {
+ getConfig(configId) {
+ let config = configStore.get(configId);
+ if (!config) {
+ config = new PlotConfigurationModel({
+ id: configId,
+ domainObject: this.domainObject,
+ openmct: this.openmct,
+ callback: (data) => {
+ this.data = data;
+ }
+ });
+ configStore.add(configId, config);
+ }
+
+ return config;
+ },
loadingUpdated(loaded) {
this.loading = loaded;
},
destroy() {
+ this.stopListening();
+ configStore.deleteStore(this.config.id);
+
this.composition.off('add', this.addChild);
this.composition.off('remove', this.removeChild);
this.composition.off('reorder', this.compositionReorder);
@@ -126,6 +167,7 @@ export default {
const id = this.openmct.objects.makeKeyString(child.identifier);
this.$set(this.tickWidthMap, id, 0);
+
this.compositionObjects.push(child);
},
@@ -134,6 +176,13 @@ export default {
this.$delete(this.tickWidthMap, id);
+ const configIndex = this.domainObject.configuration.series.findIndex((seriesConfig) => {
+ return this.openmct.objects.areIdsEqual(seriesConfig.identifier, childIdentifier);
+ });
+ if (configIndex > -1) {
+ this.domainObject.configuration.series.splice(configIndex, 1);
+ }
+
const childObj = this.compositionObjects.filter((c) => {
const identifier = this.openmct.objects.makeKeyString(c.identifier);
@@ -178,20 +227,52 @@ export default {
this.hideExportButtons = false;
}.bind(this));
},
-
- toggleCursorGuide() {
- this.cursorGuide = !this.cursorGuide;
- },
-
- toggleGridLines() {
- this.gridLines = !this.gridLines;
- },
onTickWidthChange(width, plotId) {
if (!Object.prototype.hasOwnProperty.call(this.tickWidthMap, plotId)) {
return;
}
this.$set(this.tickWidthMap, plotId, width);
+ },
+ legendHoverChanged(data) {
+ this.showLimitLineLabels = data;
+ },
+ lockHighlightPointUpdated(data) {
+ this.lockHighlightPoint = data;
+ },
+ highlightsUpdated(data) {
+ this.highlights = data;
+ },
+ registerSeriesListeners(configId) {
+ this.seriesConfig[configId] = this.getConfig(configId);
+ this.listenTo(this.seriesConfig[configId].series, 'add', this.addSeries, this);
+ this.listenTo(this.seriesConfig[configId].series, 'remove', this.removeSeries, this);
+
+ this.seriesConfig[configId].series.models.forEach(this.addSeries, this);
+ },
+ addSeries(series) {
+ const index = this.seriesModels.length;
+ this.$set(this.seriesModels, index, series);
+ },
+ removeSeries(plotSeries) {
+ const index = this.seriesModels.findIndex(seriesModel => this.openmct.objects.areIdsEqual(seriesModel.identifier, plotSeries.identifier));
+ if (index > -1) {
+ this.$delete(this.seriesModels, index);
+ }
+
+ this.stopListening(plotSeries);
+ },
+ onCursorGuideChange(cursorGuide) {
+ this.cursorGuide = cursorGuide === true;
+ },
+ onGridLinesChange(gridLines) {
+ this.gridLines = gridLines === true;
+ },
+ getViewContext() {
+ return {
+ exportPNG: this.exportPNG,
+ exportJPG: this.exportJPG
+ };
}
}
};
diff --git a/src/plugins/plot/stackedPlot/StackedPlotItem.vue b/src/plugins/plot/stackedPlot/StackedPlotItem.vue
index fbfc5a281..b0049a361 100644
--- a/src/plugins/plot/stackedPlot/StackedPlotItem.vue
+++ b/src/plugins/plot/stackedPlot/StackedPlotItem.vue
@@ -1,5 +1,5 @@
<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
+ Open MCT, Copyright (c) 2014-2022, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
@@ -26,11 +26,16 @@
import MctPlot from '../MctPlot.vue';
import Vue from "vue";
+import conditionalStylesMixin from "./mixins/objectStyles-mixin";
+import configStore from "@/plugins/plot/configuration/ConfigStore";
+import PlotConfigurationModel from "@/plugins/plot/configuration/PlotConfigurationModel";
+import ProgressBar from "../../../ui/components/ProgressBar.vue";
export default {
+ mixins: [conditionalStylesMixin],
inject: ['openmct', 'domainObject', 'path'],
props: {
- object: {
+ childObject: {
type: Object,
default() {
return {};
@@ -54,6 +59,18 @@ export default {
return true;
}
},
+ showLimitLineLabels: {
+ type: Object,
+ default() {
+ return {};
+ }
+ },
+ colorPalette: {
+ type: Object,
+ default() {
+ return undefined;
+ }
+ },
plotTickWidth: {
type: Number,
default() {
@@ -70,12 +87,22 @@ export default {
},
plotTickWidth(width) {
this.updateComponentProp('plotTickWidth', width);
+ },
+ showLimitLineLabels: {
+ handler(data) {
+ this.updateComponentProp('limitLineLabels', data);
+ },
+ deep: true
}
},
mounted() {
this.updateView();
},
beforeDestroy() {
+ if (this.removeSelectable) {
+ this.removeSelectable();
+ }
+
if (this.component) {
this.component.$destroy();
}
@@ -94,21 +121,28 @@ export default {
}
const onTickWidthChange = this.onTickWidthChange;
- const loadingUpdated = this.loadingUpdated;
+ const onLockHighlightPointUpdated = this.onLockHighlightPointUpdated;
+ const onHighlightsUpdated = this.onHighlightsUpdated;
+ const onConfigLoaded = this.onConfigLoaded;
+ const onCursorGuideChange = this.onCursorGuideChange;
+ const onGridLinesChange = this.onGridLinesChange;
const setStatus = this.setStatus;
const openmct = this.openmct;
- const object = this.object;
const path = this.path;
+ //If this object is not persistable, then package it with it's parent
+ const object = this.getPlotObject();
const getProps = this.getProps;
+ const isMissing = openmct.objects.isMissing(object);
let viewContainer = document.createElement('div');
this.$el.append(viewContainer);
this.component = new Vue({
el: viewContainer,
components: {
- MctPlot
+ MctPlot,
+ ProgressBar
},
provide: {
openmct,
@@ -119,33 +153,123 @@ export default {
return {
...getProps(),
onTickWidthChange,
- loadingUpdated,
- setStatus
+ onLockHighlightPointUpdated,
+ onHighlightsUpdated,
+ onConfigLoaded,
+ onCursorGuideChange,
+ onGridLinesChange,
+ setStatus,
+ isMissing,
+ loading: true
};
},
- template: '<div ref="plotWrapper" class="l-view-section u-style-receiver js-style-receiver" :class="{\'s-status-timeconductor-unsynced\': status && status === \'timeconductor-unsynced\'}"><div v-show="!!loading" class="c-loading--overlay loading"></div><mct-plot :grid-lines="gridLines" :cursor-guide="cursorGuide" :plot-tick-width="plotTickWidth" :options="options" @plotTickWidth="onTickWidthChange" @statusUpdated="setStatus" @loadingUpdated="loadingUpdated"/></div>'
+ methods: {
+ loadingUpdated(loaded) {
+ this.loading = loaded;
+ }
+ },
+ template: '<div v-if="!isMissing" ref="plotWrapper" class="l-view-section u-style-receiver js-style-receiver" :class="{\'s-status-timeconductor-unsynced\': status && status === \'timeconductor-unsynced\'}"><progress-bar v-show="loading !== false" class="c-telemetry-table__progress-bar" :model="{progressPerc: undefined}" /><mct-plot :init-grid-lines="gridLines" :init-cursor-guide="cursorGuide" :plot-tick-width="plotTickWidth" :limit-line-labels="limitLineLabels" :color-palette="colorPalette" :options="options" @plotTickWidth="onTickWidthChange" @lockHighlightPoint="onLockHighlightPointUpdated" @highlights="onHighlightsUpdated" @configLoaded="onConfigLoaded" @cursorGuide="onCursorGuideChange" @gridLines="onGridLinesChange" @statusUpdated="setStatus" @loadingUpdated="loadingUpdated"/></div>'
});
+
+ this.setSelection();
+ },
+ onLockHighlightPointUpdated() {
+ this.$emit('lockHighlightPoint', ...arguments);
+ },
+ onHighlightsUpdated() {
+ this.$emit('highlights', ...arguments);
+ },
+ onConfigLoaded() {
+ this.$emit('configLoaded', ...arguments);
},
onTickWidthChange() {
this.$emit('plotTickWidth', ...arguments);
},
+ onCursorGuideChange() {
+ this.$emit('cursorGuide', ...arguments);
+ },
+ onGridLinesChange() {
+ this.$emit('gridLines', ...arguments);
+ },
setStatus(status) {
this.status = status;
this.updateComponentProp('status', status);
},
- loadingUpdated(loaded) {
- this.loading = loaded;
- this.updateComponentProp('loading', loaded);
+ setSelection() {
+ let childContext = {};
+ childContext.item = this.childObject;
+ this.context = childContext;
+ if (this.removeSelectable) {
+ this.removeSelectable();
+ }
+
+ this.removeSelectable = this.openmct.selection.selectable(
+ this.$el, this.context);
},
getProps() {
return {
+ limitLineLabels: this.showLimitLineLabels,
gridLines: this.gridLines,
cursorGuide: this.cursorGuide,
plotTickWidth: this.plotTickWidth,
- loading: this.loading,
options: this.options,
- status: this.status
+ status: this.status,
+ colorPalette: this.colorPalette
};
+ },
+ getPlotObject() {
+ if (this.childObject.configuration && this.childObject.configuration.series) {
+ //If the object has a configuration, allow initialization of the config from it's persisted config
+ return this.childObject;
+ } else {
+ //If object is missing, warn and return object
+ if (this.openmct.objects.isMissing(this.childObject)) {
+ console.warn('Missing domain object');
+
+ return this.childObject;
+ }
+
+ // If the object does not have configuration, initialize the series config with the persisted config from the stacked plot
+ const configId = this.openmct.objects.makeKeyString(this.childObject.identifier);
+ let config = configStore.get(configId);
+ if (!config) {
+ let persistedSeriesConfig = this.domainObject.configuration.series.find((seriesConfig) => {
+ return this.openmct.objects.areIdsEqual(seriesConfig.identifier, this.childObject.identifier);
+ });
+
+ if (!persistedSeriesConfig) {
+ persistedSeriesConfig = {
+ series: {},
+ yAxis: {}
+ };
+ }
+
+ config = new PlotConfigurationModel({
+ id: configId,
+ domainObject: {
+ ...this.childObject,
+ configuration: {
+ series: [
+ {
+ identifier: this.childObject.identifier,
+ ...persistedSeriesConfig.series
+ }
+ ],
+ yAxis: persistedSeriesConfig.yAxis
+
+ }
+ },
+ openmct: this.openmct,
+ palette: this.colorPalette,
+ callback: (data) => {
+ this.data = data;
+ }
+ });
+ configStore.add(configId, config);
+ }
+
+ return this.childObject;
+ }
}
}
};
diff --git a/src/plugins/plot/stackedPlot/StackedPlotViewProvider.js b/src/plugins/plot/stackedPlot/StackedPlotViewProvider.js
index 2ff60d9e6..f97ac6e8e 100644
--- a/src/plugins/plot/stackedPlot/StackedPlotViewProvider.js
+++ b/src/plugins/plot/stackedPlot/StackedPlotViewProvider.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -67,9 +67,16 @@ export default function StackedPlotViewProvider(openmct) {
}
};
},
- template: '<stacked-plot :options="options"></stacked-plot>'
+ template: '<stacked-plot ref="plotComponent" :options="options"></stacked-plot>'
});
},
+ getViewContext() {
+ if (!component) {
+ return {};
+ }
+
+ return component.$refs.plotComponent.getViewContext();
+ },
destroy: function () {
component.$destroy();
component = undefined;
diff --git a/src/plugins/plot/stackedPlot/mixins/objectStyles-mixin.js b/src/plugins/plot/stackedPlot/mixins/objectStyles-mixin.js
new file mode 100644
index 000000000..37398c43c
--- /dev/null
+++ b/src/plugins/plot/stackedPlot/mixins/objectStyles-mixin.js
@@ -0,0 +1,137 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+import StyleRuleManager from "@/plugins/condition/StyleRuleManager";
+import {STYLE_CONSTANTS} from "@/plugins/condition/utils/constants";
+
+export default {
+ inject: ['openmct', 'domainObject', 'path'],
+ data() {
+ return {
+ objectStyle: undefined
+ };
+ },
+ mounted() {
+ this.objectStyles = this.getObjectStyleForItem(this.childObject.configuration);
+ this.initObjectStyles();
+ },
+ beforeDestroy() {
+ if (this.stopListeningStyles) {
+ this.stopListeningStyles();
+ }
+
+ if (this.styleRuleManager) {
+ this.styleRuleManager.destroy();
+ }
+ },
+ methods: {
+ getObjectStyleForItem(config) {
+ if (config && config.objectStyles) {
+ return config.objectStyles ? Object.assign({}, config.objectStyles) : undefined;
+ } else {
+ return undefined;
+ }
+ },
+ initObjectStyles() {
+ if (!this.styleRuleManager) {
+ this.styleRuleManager = new StyleRuleManager(this.objectStyles, this.openmct, this.updateStyle.bind(this), true);
+ } else {
+ this.styleRuleManager.updateObjectStyleConfig(this.objectStyles);
+ }
+
+ if (this.stopListeningStyles) {
+ this.stopListeningStyles();
+ }
+
+ this.stopListeningStyles = this.openmct.objects.observe(this.childObject, 'configuration.objectStyles', (newObjectStyle) => {
+ //Updating styles in the inspector view will trigger this so that the changes are reflected immediately
+ this.styleRuleManager.updateObjectStyleConfig(newObjectStyle);
+ });
+
+ if (this.childObject && this.childObject.configuration && this.childObject.configuration.fontStyle) {
+ const { fontSize, font } = this.childObject.configuration.fontStyle;
+ this.setFontSize(fontSize);
+ this.setFont(font);
+ }
+
+ this.stopListeningFontStyles = this.openmct.objects.observe(this.childObject, 'configuration.fontStyle', (newFontStyle) => {
+ this.setFontSize(newFontStyle.fontSize);
+ this.setFont(newFontStyle.font);
+ });
+ },
+ getStyleReceiver() {
+ let styleReceiver;
+
+ if (this.$el !== undefined) {
+ styleReceiver = this.$el.querySelector('.js-style-receiver')
+ || this.$el.querySelector(':first-child');
+
+ if (styleReceiver === null) {
+ styleReceiver = undefined;
+ }
+ }
+
+ return styleReceiver;
+ },
+ setFontSize(newSize) {
+ let elemToStyle = this.getStyleReceiver();
+
+ if (elemToStyle !== undefined) {
+ elemToStyle.dataset.fontSize = newSize;
+ }
+ },
+ setFont(newFont) {
+ let elemToStyle = this.getStyleReceiver();
+
+ if (elemToStyle !== undefined) {
+ elemToStyle.dataset.font = newFont;
+ }
+ },
+ updateStyle(styleObj) {
+ let elemToStyle = this.getStyleReceiver();
+
+ if (!styleObj || elemToStyle === undefined) {
+ return;
+ }
+
+ let keys = Object.keys(styleObj);
+
+ keys.forEach(key => {
+ if (elemToStyle) {
+ if ((typeof styleObj[key] === 'string') && (styleObj[key].indexOf('__no_value') > -1)) {
+ if (elemToStyle.style[key]) {
+ elemToStyle.style[key] = '';
+ }
+ } else {
+ if (!styleObj.isStyleInvisible && elemToStyle.classList.contains(STYLE_CONSTANTS.isStyleInvisible)) {
+ elemToStyle.classList.remove(STYLE_CONSTANTS.isStyleInvisible);
+ } else if (styleObj.isStyleInvisible && !elemToStyle.classList.contains(styleObj.isStyleInvisible)) {
+ elemToStyle.classList.add(styleObj.isStyleInvisible);
+ }
+
+ elemToStyle.style[key] = styleObj[key];
+ }
+ }
+ });
+ }
+ }
+};
diff --git a/src/plugins/plot/stackedPlot/pluginSpec.js b/src/plugins/plot/stackedPlot/pluginSpec.js
new file mode 100644
index 000000000..18d4a3034
--- /dev/null
+++ b/src/plugins/plot/stackedPlot/pluginSpec.js
@@ -0,0 +1,771 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+import {createMouseEvent, createOpenMct, resetApplicationState, spyOnBuiltins} from "utils/testing";
+import PlotVuePlugin from "../plugin";
+import Vue from "vue";
+import StackedPlot from "./StackedPlot.vue";
+import configStore from "../configuration/ConfigStore";
+import EventEmitter from "EventEmitter";
+import PlotConfigurationModel from "../configuration/PlotConfigurationModel";
+import PlotOptions from "../inspector/PlotOptions.vue";
+
+describe("the plugin", function () {
+ let element;
+ let child;
+ let openmct;
+ let telemetryPromise;
+ let telemetryPromiseResolve;
+ let mockObjectPath;
+ let stackedPlotObject = {
+ identifier: {
+ namespace: "",
+ key: "test-plot"
+ },
+ type: "telemetry.plot.stacked",
+ name: "Test Stacked Plot",
+ configuration: {
+ series: []
+ }
+ };
+
+ beforeEach((done) => {
+ mockObjectPath = [
+ {
+ name: 'mock folder',
+ type: 'fake-folder',
+ identifier: {
+ key: 'mock-folder',
+ namespace: ''
+ }
+ },
+ {
+ name: 'mock parent folder',
+ type: 'time-strip',
+ identifier: {
+ key: 'mock-parent-folder',
+ namespace: ''
+ }
+ }
+ ];
+ const testTelemetry = [
+ {
+ 'utc': 1,
+ 'some-key': 'some-value 1',
+ 'some-other-key': 'some-other-value 1'
+ },
+ {
+ 'utc': 2,
+ 'some-key': 'some-value 2',
+ 'some-other-key': 'some-other-value 2'
+ },
+ {
+ 'utc': 3,
+ 'some-key': 'some-value 3',
+ 'some-other-key': 'some-other-value 3'
+ }
+ ];
+
+ const timeSystem = {
+ timeSystemKey: 'utc',
+ bounds: {
+ start: 0,
+ end: 4
+ }
+ };
+
+ openmct = createOpenMct(timeSystem);
+
+ telemetryPromise = new Promise((resolve) => {
+ telemetryPromiseResolve = resolve;
+ });
+
+ spyOn(openmct.telemetry, 'request').and.callFake(() => {
+ telemetryPromiseResolve(testTelemetry);
+
+ return telemetryPromise;
+ });
+
+ openmct.install(new PlotVuePlugin());
+
+ element = document.createElement("div");
+ element.style.width = "640px";
+ element.style.height = "480px";
+ child = document.createElement("div");
+ child.style.width = "640px";
+ child.style.height = "480px";
+ element.appendChild(child);
+ document.body.appendChild(element);
+
+ spyOn(window, 'ResizeObserver').and.returnValue({
+ observe() {},
+ unobserve() {},
+ disconnect() {}
+ });
+
+ openmct.types.addType("test-object", {
+ creatable: true
+ });
+
+ spyOnBuiltins(["requestAnimationFrame"]);
+ window.requestAnimationFrame.and.callFake((callBack) => {
+ callBack();
+ });
+
+ openmct.router.path = [stackedPlotObject];
+ openmct.on("start", done);
+ openmct.startHeadless();
+ });
+
+ afterEach((done) => {
+ openmct.time.timeSystem('utc', {
+ start: 0,
+ end: 1
+ });
+ configStore.deleteAll();
+ resetApplicationState(openmct).then(done).catch(done);
+ });
+
+ afterAll(() => {
+ openmct.router.path = null;
+ });
+
+ describe("the plot views", () => {
+ it("provides a stacked plot view for objects with telemetry", () => {
+ const testTelemetryObject = {
+ id: "test-object",
+ type: "telemetry.plot.stacked",
+ telemetry: {
+ values: [{
+ key: "some-key"
+ }]
+ }
+ };
+
+ const applicableViews = openmct.objectViews.get(testTelemetryObject, mockObjectPath);
+ let plotView = applicableViews.find((viewProvider) => viewProvider.key === "plot-stacked");
+ expect(plotView).toBeDefined();
+ });
+
+ });
+
+ describe("The stacked plot view", () => {
+ let testTelemetryObject;
+ let testTelemetryObject2;
+ let config;
+ let component;
+ let mockComposition;
+ let plotViewComponentObject;
+
+ afterAll(() => {
+ openmct.router.path = null;
+ });
+
+ beforeEach(() => {
+ testTelemetryObject = {
+ identifier: {
+ namespace: "",
+ key: "test-object"
+ },
+ type: "test-object",
+ name: "Test Object",
+ telemetry: {
+ values: [{
+ key: "utc",
+ format: "utc",
+ name: "Time",
+ hints: {
+ domain: 1
+ }
+ }, {
+ key: "some-key",
+ name: "Some attribute",
+ hints: {
+ range: 1
+ }
+ }, {
+ key: "some-other-key",
+ name: "Another attribute",
+ hints: {
+ range: 2
+ }
+ }]
+ },
+ configuration: {
+ objectStyles: {
+ staticStyle: {
+ style: {
+ backgroundColor: 'rgb(0, 200, 0)',
+ color: '',
+ border: ''
+ }
+ },
+ conditionSetIdentifier: {
+ namespace: '',
+ key: 'testConditionSetId'
+ },
+ selectedConditionId: 'conditionId1',
+ defaultConditionId: 'conditionId1',
+ styles: [
+ {
+ conditionId: 'conditionId1',
+ style: {
+ backgroundColor: 'rgb(0, 155, 0)',
+ color: '',
+ output: '',
+ border: ''
+ }
+ }
+ ]
+ }
+ }
+ };
+
+ testTelemetryObject2 = {
+ identifier: {
+ namespace: "",
+ key: "test-object2"
+ },
+ type: "test-object",
+ name: "Test Object2",
+ telemetry: {
+ values: [{
+ key: "utc",
+ format: "utc",
+ name: "Time",
+ hints: {
+ domain: 1
+ }
+ }, {
+ key: "some-key2",
+ name: "Some attribute2",
+ hints: {
+ range: 1
+ }
+ }, {
+ key: "some-other-key2",
+ name: "Another attribute2",
+ hints: {
+ range: 2
+ }
+ }]
+ }
+ };
+
+ mockComposition = new EventEmitter();
+ mockComposition.load = () => {
+ mockComposition.emit('add', testTelemetryObject);
+
+ return [testTelemetryObject];
+ };
+
+ spyOn(openmct.composition, 'get').and.returnValue(mockComposition);
+
+ let viewContainer = document.createElement("div");
+ child.append(viewContainer);
+ component = new Vue({
+ el: viewContainer,
+ components: {
+ StackedPlot
+ },
+ provide: {
+ openmct: openmct,
+ domainObject: stackedPlotObject,
+ composition: openmct.composition.get(stackedPlotObject),
+ path: [stackedPlotObject]
+ },
+ template: "<stacked-plot></stacked-plot>"
+ });
+
+ return telemetryPromise
+ .then(Vue.nextTick())
+ .then(() => {
+ plotViewComponentObject = component.$root.$children[0];
+ const configId = openmct.objects.makeKeyString(testTelemetryObject.identifier);
+ config = configStore.get(configId);
+ });
+ });
+
+ it("Renders a collapsed legend for every telemetry", () => {
+ let legend = element.querySelectorAll(".plot-wrapper-collapsed-legend .plot-series-name");
+ expect(legend.length).toBe(1);
+ expect(legend[0].innerHTML).toEqual("Test Object");
+ });
+
+ it("Renders an expanded legend for every telemetry", () => {
+ let legendControl = element.querySelector(".c-plot-legend__view-control.gl-plot-legend__view-control.c-disclosure-triangle");
+ const clickEvent = createMouseEvent("click");
+
+ legendControl.dispatchEvent(clickEvent);
+
+ let legend = element.querySelectorAll(".plot-wrapper-expanded-legend .plot-legend-item td");
+ expect(legend.length).toBe(6);
+ });
+
+ it("Renders X-axis ticks for the telemetry object", (done) => {
+ let xAxisElement = element.querySelectorAll(".gl-plot-axis-area.gl-plot-x .gl-plot-tick-wrapper");
+ expect(xAxisElement.length).toBe(1);
+
+ config.xAxis.set('displayRange', {
+ min: 0,
+ max: 4
+ });
+
+ Vue.nextTick(() => {
+ let ticks = xAxisElement[0].querySelectorAll(".gl-plot-tick");
+ expect(ticks.length).toBe(9);
+
+ done();
+ });
+ });
+
+ it("Renders Y-axis ticks for the telemetry object", (done) => {
+ config.yAxis.set('displayRange', {
+ min: 10,
+ max: 20
+ });
+ Vue.nextTick(() => {
+ let yAxisElement = element.querySelectorAll(".gl-plot-axis-area.gl-plot-y .gl-plot-tick-wrapper");
+ expect(yAxisElement.length).toBe(1);
+ let ticks = yAxisElement[0].querySelectorAll(".gl-plot-tick");
+ expect(ticks.length).toBe(6);
+ done();
+ });
+ });
+
+ it("Renders Y-axis options for the telemetry object", () => {
+ let yAxisElement = element.querySelectorAll(".gl-plot-axis-area.gl-plot-y .gl-plot-y-label__select");
+ expect(yAxisElement.length).toBe(1);
+ let options = yAxisElement[0].querySelectorAll("option");
+ expect(options.length).toBe(2);
+ expect(options[0].value).toBe("Some attribute");
+ expect(options[1].value).toBe("Another attribute");
+ });
+
+ it("turns on cursor Guides all telemetry objects", (done) => {
+ expect(plotViewComponentObject.cursorGuide).toBeFalse();
+ plotViewComponentObject.cursorGuide = true;
+ Vue.nextTick(() => {
+ let childCursorGuides = element.querySelectorAll(".c-cursor-guide--v");
+ expect(childCursorGuides.length).toBe(1);
+ done();
+ });
+ });
+
+ it("shows grid lines for all telemetry objects", () => {
+ expect(plotViewComponentObject.gridLines).toBeTrue();
+ let gridLinesContainer = element.querySelectorAll(".gl-plot-display-area .js-ticks");
+ let visible = 0;
+ gridLinesContainer.forEach(el => {
+ if (el.style.display !== "none") {
+ visible++;
+ }
+ });
+ expect(visible).toBe(2);
+ });
+
+ it("hides grid lines for all telemetry objects", (done) => {
+ expect(plotViewComponentObject.gridLines).toBeTrue();
+ plotViewComponentObject.gridLines = false;
+ Vue.nextTick(() => {
+ expect(plotViewComponentObject.gridLines).toBeFalse();
+ let gridLinesContainer = element.querySelectorAll(".gl-plot-display-area .js-ticks");
+ let visible = 0;
+ gridLinesContainer.forEach(el => {
+ if (el.style.display !== "none") {
+ visible++;
+ }
+ });
+ expect(visible).toBe(0);
+ done();
+ });
+ });
+
+ it('plots a new series when a new telemetry object is added', (done) => {
+ mockComposition.emit('add', testTelemetryObject2);
+ Vue.nextTick(() => {
+ let legend = element.querySelectorAll(".plot-wrapper-collapsed-legend .plot-series-name");
+ expect(legend.length).toBe(2);
+ expect(legend[1].innerHTML).toEqual("Test Object2");
+ done();
+ });
+ });
+
+ it('removes plots from series when a telemetry object is removed', (done) => {
+ mockComposition.emit('remove', testTelemetryObject.identifier);
+ Vue.nextTick(() => {
+ expect(plotViewComponentObject.compositionObjects.length).toBe(0);
+ done();
+ });
+ });
+
+ it("Changes the label of the y axis when the option changes", (done) => {
+ let selectEl = element.querySelector('.gl-plot-y-label__select');
+ selectEl.value = 'Another attribute';
+ selectEl.dispatchEvent(new Event("change"));
+
+ Vue.nextTick(() => {
+ expect(config.yAxis.get('label')).toEqual('Another attribute');
+ done();
+ });
+ });
+
+ it("Renders a new series when added to one of the plots", (done) => {
+ mockComposition.emit('add', testTelemetryObject2);
+ Vue.nextTick(() => {
+ let legend = element.querySelectorAll(".plot-wrapper-collapsed-legend .plot-series-name");
+ expect(legend.length).toBe(2);
+ expect(legend[1].innerHTML).toEqual("Test Object2");
+ done();
+ });
+ });
+
+ it("Adds a new point to the plot", (done) => {
+ let originalLength = config.series.models[0].getSeriesData().length;
+ config.series.models[0].add({
+ utc: 2,
+ 'some-key': 1,
+ 'some-other-key': 2
+ });
+ Vue.nextTick(() => {
+ const seriesData = config.series.models[0].getSeriesData();
+ expect(seriesData.length).toEqual(originalLength + 1);
+ done();
+ });
+ });
+
+ it("updates the xscale", (done) => {
+ config.xAxis.set('displayRange', {
+ min: 0,
+ max: 10
+ });
+ Vue.nextTick(() => {
+ expect(plotViewComponentObject.$children[1].component.$children[1].xScale.domain()).toEqual({
+ min: 0,
+ max: 10
+ });
+ done();
+ });
+ });
+
+ it("updates the yscale", (done) => {
+ config.yAxis.set('displayRange', {
+ min: 10,
+ max: 20
+ });
+ Vue.nextTick(() => {
+ expect(plotViewComponentObject.$children[1].component.$children[1].yScale.domain()).toEqual({
+ min: 10,
+ max: 20
+ });
+ done();
+ });
+ });
+
+ it("shows styles for telemetry objects if available", (done) => {
+ Vue.nextTick(() => {
+ let conditionalStylesContainer = element.querySelectorAll(".c-plot--stacked-container .js-style-receiver");
+ let hasStyles = 0;
+ conditionalStylesContainer.forEach(el => {
+ if (el.style.backgroundColor !== '') {
+ hasStyles++;
+ }
+ });
+ expect(hasStyles).toBe(1);
+ done();
+ });
+ });
+ });
+
+ describe('the stacked plot inspector view', () => {
+ let component;
+ let viewComponentObject;
+ let mockComposition;
+ let testTelemetryObject;
+ let selection;
+ let config;
+ beforeEach((done) => {
+ testTelemetryObject = {
+ identifier: {
+ namespace: "",
+ key: "test-object"
+ },
+ type: "test-object",
+ name: "Test Object",
+ telemetry: {
+ values: [{
+ key: "utc",
+ format: "utc",
+ name: "Time",
+ hints: {
+ domain: 1
+ }
+ }, {
+ key: "some-key",
+ name: "Some attribute",
+ hints: {
+ range: 1
+ }
+ }, {
+ key: "some-other-key",
+ name: "Another attribute",
+ hints: {
+ range: 2
+ }
+ }]
+ }
+ };
+
+ selection = [
+ [
+ {
+ context: {
+ item: {
+ type: 'telemetry.plot.stacked',
+ identifier: {
+ key: 'some-stacked-plot',
+ namespace: ''
+ },
+ configuration: {
+ series: []
+ }
+ }
+ }
+ }
+ ]
+ ];
+
+ openmct.router.path = [testTelemetryObject];
+ mockComposition = new EventEmitter();
+ mockComposition.load = () => {
+ mockComposition.emit('add', testTelemetryObject);
+
+ return [testTelemetryObject];
+ };
+
+ spyOn(openmct.composition, 'get').and.returnValue(mockComposition);
+
+ const configId = openmct.objects.makeKeyString(selection[0][0].context.item.identifier);
+ config = new PlotConfigurationModel({
+ id: configId,
+ domainObject: selection[0][0].context.item,
+ openmct: openmct
+ });
+ configStore.add(configId, config);
+
+ let viewContainer = document.createElement('div');
+ child.append(viewContainer);
+ component = new Vue({
+ el: viewContainer,
+ components: {
+ PlotOptions
+ },
+ provide: {
+ openmct: openmct,
+ domainObject: selection[0][0].context.item,
+ path: [selection[0][0].context.item]
+ },
+ template: '<plot-options/>'
+ });
+
+ Vue.nextTick(() => {
+ viewComponentObject = component.$root.$children[0];
+ done();
+ });
+ });
+
+ afterEach(() => {
+ openmct.router.path = null;
+ });
+
+ describe('in view only mode', () => {
+ let browseOptionsEl;
+ beforeEach(() => {
+ browseOptionsEl = viewComponentObject.$el.querySelector('.js-plot-options-browse');
+ });
+
+ it('shows legend properties', () => {
+ const legendPropertiesEl = browseOptionsEl.querySelector('.js-legend-properties');
+ expect(legendPropertiesEl).not.toBeNull();
+ });
+
+ it('does not show series properties', () => {
+ const seriesPropertiesEl = browseOptionsEl.querySelector('.c-tree');
+ expect(seriesPropertiesEl).toBeNull();
+ });
+
+ it('does not show yaxis properties', () => {
+ const yAxisPropertiesEl = browseOptionsEl.querySelector('.js-yaxis-properties');
+ expect(yAxisPropertiesEl).toBeNull();
+ });
+ });
+
+ });
+
+ describe('inspector view of stacked plot child', () => {
+ let component;
+ let viewComponentObject;
+ let mockComposition;
+ let testTelemetryObject;
+ let selection;
+ let config;
+ beforeEach((done) => {
+ testTelemetryObject = {
+ identifier: {
+ namespace: "",
+ key: "test-object"
+ },
+ type: "test-object",
+ name: "Test Object",
+ telemetry: {
+ values: [{
+ key: "utc",
+ format: "utc",
+ name: "Time",
+ hints: {
+ domain: 1
+ }
+ }, {
+ key: "some-key",
+ name: "Some attribute",
+ hints: {
+ range: 1
+ }
+ }, {
+ key: "some-other-key",
+ name: "Another attribute",
+ hints: {
+ range: 2
+ }
+ }]
+ }
+ };
+
+ selection = [
+ [
+ {
+ context: {
+ item: {
+ id: "test-object",
+ identifier: {
+ key: "test-object",
+ namespace: ''
+ },
+ type: "telemetry.plot.overlay",
+ configuration: {
+ series: [
+ {
+ identifier: {
+ key: "test-object",
+ namespace: ''
+ }
+ }
+ ]
+ },
+ composition: []
+ }
+ }
+ },
+ {
+ context: {
+ item: {
+ type: 'telemetry.plot.stacked',
+ identifier: {
+ key: 'some-stacked-plot',
+ namespace: ''
+ },
+ configuration: {
+ series: []
+ }
+ }
+ }
+ }
+ ]
+ ];
+
+ openmct.router.path = [testTelemetryObject];
+ mockComposition = new EventEmitter();
+ mockComposition.load = () => {
+ mockComposition.emit('add', testTelemetryObject);
+
+ return [testTelemetryObject];
+ };
+
+ spyOn(openmct.composition, 'get').and.returnValue(mockComposition);
+
+ const configId = openmct.objects.makeKeyString(selection[0][0].context.item.identifier);
+ config = new PlotConfigurationModel({
+ id: configId,
+ domainObject: selection[0][0].context.item,
+ openmct: openmct
+ });
+ configStore.add(configId, config);
+
+ let viewContainer = document.createElement('div');
+ child.append(viewContainer);
+ component = new Vue({
+ el: viewContainer,
+ components: {
+ PlotOptions
+ },
+ provide: {
+ openmct: openmct,
+ domainObject: selection[0][0].context.item,
+ path: [selection[0][0].context.item, selection[0][1].context.item]
+ },
+ template: '<plot-options/>'
+ });
+
+ Vue.nextTick(() => {
+ viewComponentObject = component.$root.$children[0];
+ done();
+ });
+ });
+
+ afterEach(() => {
+ openmct.router.path = null;
+ });
+
+ describe('in view only mode', () => {
+ let browseOptionsEl;
+ beforeEach(() => {
+ browseOptionsEl = viewComponentObject.$el.querySelector('.js-plot-options-browse');
+ });
+
+ it('hides legend properties', () => {
+ const legendPropertiesEl = browseOptionsEl.querySelector('.js-legend-properties');
+ expect(legendPropertiesEl).toBeNull();
+ });
+
+ it('shows series properties', () => {
+ const seriesPropertiesEl = browseOptionsEl.querySelector('.c-tree');
+ expect(seriesPropertiesEl).not.toBeNull();
+ });
+
+ it('shows yaxis properties', () => {
+ const yAxisPropertiesEl = browseOptionsEl.querySelector('.js-yaxis-properties');
+ expect(yAxisPropertiesEl).not.toBeNull();
+ });
+ });
+
+ });
+});
diff --git a/example/mobile/bundle.js b/src/plugins/plot/stackedPlot/stackedPlotConfigurationInterceptor.js
index 1230bba5b..d16ca1b46 100644
--- a/example/mobile/bundle.js
+++ b/src/plugins/plot/stackedPlot/stackedPlotConfigurationInterceptor.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -20,22 +20,19 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
-define([], function () {
- "use strict";
+export default function stackedPlotConfigurationInterceptor(openmct) {
- return {
- name: "example/mobile",
- definition: {
- "name": "Mobile",
- "description": "Allows elements with pertinence to mobile usage and development",
- "extensions": {
- "stylesheets": [
- {
- "stylesheetUrl": "css/mobile-example.css",
- "priority": "mandatory"
- }
- ]
+ openmct.objects.addGetInterceptor({
+ appliesTo: (identifier, domainObject) => {
+ return domainObject && domainObject.type === 'telemetry.plot.stacked';
+ },
+ invoke: (identifier, object) => {
+
+ if (object && object.configuration && object.configuration.series === undefined) {
+ object.configuration.series = [];
}
+
+ return object;
}
- };
-});
+ });
+}
diff --git a/src/plugins/plot/tickUtils.js b/src/plugins/plot/tickUtils.js
index 7dd84673b..a83a77989 100644
--- a/src/plugins/plot/tickUtils.js
+++ b/src/plugins/plot/tickUtils.js
@@ -1,3 +1,5 @@
+import { antisymlog, symlog } from "./mathUtils";
+
const e10 = Math.sqrt(50);
const e5 = Math.sqrt(10);
const e2 = Math.sqrt(2);
@@ -40,6 +42,42 @@ function getPrecision(step) {
return precision;
}
+export function getLogTicks(start, stop, mainTickCount = 8, secondaryTickCount = 6) {
+ // log()'ed values
+ const mainLogTicks = ticks(start, stop, mainTickCount);
+
+ // original values
+ const mainTicks = mainLogTicks.map(n => antisymlog(n, 10));
+
+ const result = [];
+
+ let i = 0;
+ for (const logTick of mainLogTicks) {
+ result.push(logTick);
+
+ if (i === mainLogTicks.length - 1) {
+ break;
+ }
+
+ const tick = mainTicks[i];
+ const nextTick = mainTicks[i + 1];
+ const rangeBetweenMainTicks = nextTick - tick;
+
+ const secondaryLogTicks = ticks(
+ tick + rangeBetweenMainTicks / (secondaryTickCount + 1),
+ nextTick - rangeBetweenMainTicks / (secondaryTickCount + 1),
+ secondaryTickCount - 2
+ )
+ .map(n => symlog(n, 10));
+
+ result.push(...secondaryLogTicks);
+
+ i++;
+ }
+
+ return result;
+}
+
/**
* Linear tick generation from d3-array.
*/
diff --git a/src/plugins/plugins.js b/src/plugins/plugins.js
index a146aefcc..10264993e 100644
--- a/src/plugins/plugins.js
+++ b/src/plugins/plugins.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -28,15 +28,18 @@ define([
'./ISOTimeFormat/plugin',
'./myItems/plugin',
'../../example/generator/plugin',
+ '../../example/eventGenerator/plugin',
'./autoflow/AutoflowTabularPlugin',
'./timeConductor/plugin',
'../../example/imagery/plugin',
+ '../../example/faultManagment/exampleFaultSource',
'./imagery/plugin',
'./summaryWidget/plugin',
'./URLIndicatorPlugin/URLIndicatorPlugin',
'./telemetryMean/plugin',
'./plot/plugin',
- './charts/plugin',
+ './charts/bar/plugin',
+ './charts/scatter/plugin',
'./telemetryTable/plugin',
'./staticRootPlugin/plugin',
'./notebook/plugin',
@@ -60,7 +63,6 @@ define([
'./URLTimeSettingsSynchronizer/plugin',
'./notificationIndicator/plugin',
'./newFolderAction/plugin',
- './nonEditableFolder/plugin',
'./persistence/couch/plugin',
'./defaultRootName/plugin',
'./plan/plugin',
@@ -74,7 +76,14 @@ define([
'./clock/plugin',
'./DeviceClassifier/plugin',
'./timer/plugin',
- './localStorage/plugin'
+ './userIndicator/plugin',
+ '../../example/exampleUser/plugin',
+ './localStorage/plugin',
+ './operatorStatus/plugin',
+ './gauge/GaugePlugin',
+ './timelist/plugin',
+ './faultManagement/FaultManagementPlugin',
+ '../../example/exampleTags/plugin'
], function (
_,
UTCTimeSystem,
@@ -83,15 +92,18 @@ define([
ISOTimeFormat,
MyItems,
GeneratorPlugin,
+ EventGeneratorPlugin,
AutoflowPlugin,
TimeConductorPlugin,
ExampleImagery,
+ ExampleFaultSource,
ImageryPlugin,
SummaryWidget,
URLIndicatorPlugin,
TelemetryMean,
PlotPlugin,
- ChartPlugin,
+ BarChartPlugin,
+ ScatterPlotPlugin,
TelemetryTablePlugin,
StaticRootPlugin,
Notebook,
@@ -115,7 +127,6 @@ define([
URLTimeSettingsSynchronizer,
NotificationIndicator,
NewFolderAction,
- NonEditableFolder,
CouchDBPlugin,
DefaultRootName,
PlanLayout,
@@ -129,19 +140,24 @@ define([
Clock,
DeviceClassifier,
Timer,
- LocalStorage
+ UserIndicator,
+ ExampleUser,
+ LocalStorage,
+ OperatorStatus,
+ GaugePlugin,
+ TimeList,
+ FaultManagementPlugin,
+ ExampleTags
) {
- const bundleMap = {
- Elasticsearch: 'platform/persistence/elastic'
- };
+ const plugins = {};
- const plugins = _.mapValues(bundleMap, function (bundleName, pluginName) {
- return function pluginConstructor() {
- return function (openmct) {
- openmct.legacyRegistry.enable(bundleName);
- };
- };
- });
+ 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;
plugins.UTCTimeSystem = UTCTimeSystem.default;
plugins.LocalTimeSystem = LocalTimeSystem;
@@ -149,7 +165,7 @@ define([
plugins.MyItems = MyItems.default;
- plugins.StaticRootPlugin = StaticRootPlugin;
+ plugins.StaticRootPlugin = StaticRootPlugin.default;
/**
* A tabular view showing the latest values of multiple telemetry points at
@@ -166,43 +182,19 @@ define([
plugins.CouchDB = CouchDBPlugin.default;
- plugins.Elasticsearch = function (url) {
- return function (openmct) {
- if (url) {
- const bundleName = "config/elastic";
- openmct.legacyRegistry.register(bundleName, {
- "extensions": {
- "constants": [
- {
- "key": "ELASTIC_ROOT",
- "value": url,
- "priority": "mandatory"
- }
- ]
- }
- });
- openmct.legacyRegistry.enable(bundleName);
- }
-
- openmct.legacyRegistry.enable(bundleMap.Elasticsearch);
- };
- };
-
- plugins.Generator = function () {
- return GeneratorPlugin;
- };
-
- plugins.ExampleImagery = ExampleImagery.default;
plugins.ImageryPlugin = ImageryPlugin;
plugins.Plot = PlotPlugin.default;
- plugins.Chart = ChartPlugin.default;
+ plugins.BarChart = BarChartPlugin.default;
+ plugins.ScatterPlot = ScatterPlotPlugin.default;
plugins.TelemetryTable = TelemetryTablePlugin;
plugins.SummaryWidget = SummaryWidget;
plugins.TelemetryMean = TelemetryMean;
plugins.URLIndicator = URLIndicatorPlugin;
- plugins.Notebook = Notebook.default;
+ plugins.Notebook = Notebook.NotebookPlugin;
+ plugins.RestrictedNotebook = Notebook.RestrictedNotebookPlugin;
plugins.DisplayLayout = DisplayLayoutPlugin.default;
+ plugins.FaultManagement = FaultManagementPlugin.default;
plugins.FormActions = FormActions;
plugins.FolderView = FolderView;
plugins.Tabs = Tabs;
@@ -222,7 +214,6 @@ define([
plugins.URLTimeSettingsSynchronizer = URLTimeSettingsSynchronizer.default;
plugins.NotificationIndicator = NotificationIndicator.default;
plugins.NewFolderAction = NewFolderAction.default;
- plugins.NonEditableFolder = NonEditableFolder.default;
plugins.ISOTimeFormat = ISOTimeFormat.default;
plugins.DefaultRootName = DefaultRootName.default;
plugins.PlanLayout = PlanLayout.default;
@@ -236,7 +227,11 @@ define([
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;
return plugins;
});
diff --git a/src/plugins/remoteClock/RemoteClock.js b/src/plugins/remoteClock/RemoteClock.js
index 96aa33624..3d6e6fcf4 100644
--- a/src/plugins/remoteClock/RemoteClock.js
+++ b/src/plugins/remoteClock/RemoteClock.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT Web, Copyright (c) 2014-2021, United States Government
+ * Open MCT Web, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -20,6 +20,7 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
import DefaultClock from '../../utils/clock/DefaultClock';
+import remoteClockRequestInterceptor from './requestInterceptor';
/**
* A {@link openmct.TimeAPI.Clock} that updates the temporal bounds of the
@@ -49,6 +50,14 @@ export default class RemoteClock extends DefaultClock {
this.lastTick = 0;
+ this.openmct.telemetry.addRequestInterceptor(
+ remoteClockRequestInterceptor(
+ this.openmct,
+ this.identifier,
+ this.#waitForReady.bind(this)
+ )
+ );
+
this._processDatum = this._processDatum.bind(this);
}
@@ -129,4 +138,25 @@ export default class RemoteClock extends DefaultClock {
return timeFormatter.parse(datum);
};
}
+
+ /**
+ * Waits for the clock to have a non-default tick value.
+ *
+ * @private
+ */
+ #waitForReady() {
+ const waitForInitialTick = (resolve) => {
+ if (this.lastTick > 0) {
+ const offsets = this.openmct.time.clockOffsets();
+ resolve({
+ start: this.lastTick + offsets.start,
+ end: this.lastTick + offsets.end
+ });
+ } else {
+ setTimeout(() => waitForInitialTick(resolve), 100);
+ }
+ };
+
+ return new Promise(waitForInitialTick);
+ }
}
diff --git a/src/plugins/remoteClock/RemoteClockSpec.js b/src/plugins/remoteClock/RemoteClockSpec.js
index 8b6cf9793..63aa03791 100644
--- a/src/plugins/remoteClock/RemoteClockSpec.js
+++ b/src/plugins/remoteClock/RemoteClockSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT Web, Copyright (c) 2014-2015, United States Government
+ * Open MCT Web, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/remoteClock/plugin.js b/src/plugins/remoteClock/plugin.js
index a6b24d084..9a47788f1 100644
--- a/src/plugins/remoteClock/plugin.js
+++ b/src/plugins/remoteClock/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT Web, Copyright (c) 2014-2021, United States Government
+ * Open MCT Web, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/example/export/bundle.js b/src/plugins/remoteClock/requestInterceptor.js
index 55a700759..d6cffe7b3 100644
--- a/example/export/bundle.js
+++ b/src/plugins/remoteClock/requestInterceptor.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -20,27 +20,27 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
-define([
- './ExportTelemetryAsCSVAction'
-], function (ExportTelemetryAsCSVAction) {
- "use strict";
+function remoteClockRequestInterceptor(openmct, remoteClockIdentifier, waitForBounds) {
+ let remoteClockLoaded = false;
return {
- name: "example/export",
- definition: {
- "name": "Example of using CSV Export",
- "extensions": {
- "actions": [
- {
- "key": "example.export",
- "name": "Export Telemetry as CSV",
- "implementation": ExportTelemetryAsCSVAction,
- "category": "contextual",
- "cssClass": "icon-download",
- "depends": ["exportService"]
- }
- ]
- }
+ appliesTo: () => {
+ // Get the activeClock from the Global Time Context
+ const { activeClock } = openmct.time.getContextForView();
+
+ return activeClock !== undefined
+ && activeClock.key === 'remote-clock'
+ && !remoteClockLoaded;
+ },
+ invoke: async (request) => {
+ const { start, end } = await waitForBounds();
+ remoteClockLoaded = true;
+ request.start = start;
+ request.end = end;
+
+ return request;
}
};
-});
+}
+
+export default remoteClockRequestInterceptor;
diff --git a/src/plugins/remove/RemoveAction.js b/src/plugins/remove/RemoveAction.js
index 8cc82c0f8..8a0b9ec4c 100644
--- a/src/plugins/remove/RemoveAction.js
+++ b/src/plugins/remove/RemoveAction.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -92,34 +92,42 @@ export default class RemoveAction {
this.openmct.editor.save();
}
- const parentKeyString = this.openmct.objects.makeKeyString(parent.identifier);
- const isAlias = parentKeyString !== child.location;
-
- if (!isAlias) {
+ if (!this.isAlias(child, parent)) {
this.openmct.objects.mutate(child, 'location', null);
}
}
+ isAlias(child, parent) {
+ if (parent === undefined) {
+ // then it's a root item, not an alias
+ return false;
+ }
+
+ const parentKeyString = this.openmct.objects.makeKeyString(parent.identifier);
+ const childLocation = child.location;
+
+ return childLocation !== parentKeyString;
+ }
+
appliesTo(objectPath) {
- let parent = objectPath[1];
- let parentType = parent && this.openmct.types.get(parent.type);
- let child = objectPath[0];
- let locked = child.locked ? child.locked : parent && parent.locked;
- let isEditing = this.openmct.editor.isEditing();
+ const parent = objectPath[1];
+ const parentType = parent && this.openmct.types.get(parent.type);
+ const child = objectPath[0];
+ const locked = child.locked ? child.locked : parent && parent.locked;
+ const isEditing = this.openmct.editor.isEditing();
+ const isPersistable = this.openmct.objects.isPersistable(child.identifier);
+ const isAlias = this.isAlias(child, parent);
+
+ if (locked || (!isPersistable && !isAlias)) {
+ return false;
+ }
if (isEditing) {
- let currentItemInView = this.openmct.router.path[0];
- let domainObject = objectPath[0];
-
- if (this.openmct.objects.areIdsEqual(currentItemInView.identifier, domainObject.identifier)) {
+ if (this.openmct.router.isNavigatedObject(objectPath)) {
return false;
}
}
- if (locked) {
- return false;
- }
-
return parentType
&& parentType.definition.creatable
&& Array.isArray(parent.composition);
diff --git a/src/plugins/remove/plugin.js b/src/plugins/remove/plugin.js
index a2ced2938..cbba37e03 100644
--- a/src/plugins/remove/plugin.js
+++ b/src/plugins/remove/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/remove/pluginSpec.js b/src/plugins/remove/pluginSpec.js
index 887cc0a50..8b1af6304 100644
--- a/src/plugins/remove/pluginSpec.js
+++ b/src/plugins/remove/pluginSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2018, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/staticRootPlugin/StaticModelProvider.js b/src/plugins/staticRootPlugin/StaticModelProvider.js
index f916e6cc1..f05991446 100644
--- a/src/plugins/staticRootPlugin/StaticModelProvider.js
+++ b/src/plugins/staticRootPlugin/StaticModelProvider.js
@@ -1,45 +1,143 @@
-define([
- 'objectUtils'
-], function (
- objectUtils
-) {
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+/**
+ * Transforms an import json blob into a object map that can be used to
+ * provide objects. Rewrites root identifier in import data with provided
+ * rootIdentifier, and rewrites all child object identifiers so that they
+ * exist in the same namespace as the rootIdentifier.
+ */
+import objectUtils from 'objectUtils';
+
+class StaticModelProvider {
+ constructor(importData, rootIdentifier) {
+ this.objectMap = {};
+ this.rewriteModel(importData, rootIdentifier);
+ }
+
/**
- * Transforms an import json blob into a object map that can be used to
- * provide objects. Rewrites root identifier in import data with provided
- * rootIdentifier, and rewrites all child object identifiers so that they
- * exist in the same namespace as the rootIdentifier.
+ * Standard "Get".
*/
- function rewriteObjectIdentifiers(importData, rootIdentifier) {
- const rootId = importData.rootId;
- let objectString = JSON.stringify(importData.openmct);
-
- Object.keys(importData.openmct).forEach(function (originalId, i) {
- let newId;
- if (originalId === rootId) {
- newId = objectUtils.makeKeyString(rootIdentifier);
+ get(identifier) {
+ const keyString = objectUtils.makeKeyString(identifier);
+ if (this.objectMap[keyString]) {
+ return this.objectMap[keyString];
+ }
+
+ throw new Error(keyString + ' not found in import models.');
+ }
+
+ parseObjectLeaf(objectLeaf, idMap, namespace) {
+ Object.keys(objectLeaf).forEach((nodeKey) => {
+ if (idMap.get(nodeKey)) {
+ const newIdentifier = objectUtils.makeKeyString({
+ namespace,
+ key: idMap.get(nodeKey)
+ });
+ objectLeaf[newIdentifier] = { ...objectLeaf[nodeKey] };
+ delete objectLeaf[nodeKey];
+ objectLeaf[newIdentifier] = this.parseTreeLeaf(newIdentifier, objectLeaf[newIdentifier], idMap, namespace);
} else {
- newId = objectUtils.makeKeyString({
- namespace: rootIdentifier.namespace,
- key: i
+ objectLeaf[nodeKey] = this.parseTreeLeaf(nodeKey, objectLeaf[nodeKey], idMap, namespace);
+ }
+ });
+
+ return objectLeaf;
+ }
+
+ parseArrayLeaf(arrayLeaf, idMap, namespace) {
+ return arrayLeaf.map((leafValue, index) => this.parseTreeLeaf(
+ null, leafValue, idMap, namespace));
+ }
+
+ parseBranchedLeaf(branchedLeafValue, idMap, namespace) {
+ if (Array.isArray(branchedLeafValue)) {
+ return this.parseArrayLeaf(branchedLeafValue, idMap, namespace);
+ } else {
+ return this.parseObjectLeaf(branchedLeafValue, idMap, namespace);
+ }
+ }
+
+ parseTreeLeaf(leafKey, leafValue, idMap, namespace) {
+ if (leafValue === null || leafValue === undefined) {
+ return leafValue;
+ }
+
+ const hasChild = typeof leafValue === 'object';
+ if (hasChild) {
+ return this.parseBranchedLeaf(leafValue, idMap, namespace);
+ }
+
+ if (leafKey === 'key') {
+ return idMap.get(leafValue);
+ } else if (leafKey === 'namespace') {
+ return namespace;
+ } else if (leafKey === 'location') {
+ if (idMap.get(leafValue)) {
+ const newLocationIdentifier = objectUtils.makeKeyString({
+ namespace,
+ key: idMap.get(leafValue)
});
+
+ return newLocationIdentifier;
}
- while (objectString.indexOf(originalId) !== -1) {
- objectString = objectString.replace(
- '"' + originalId + '"',
- '"' + newId + '"'
- );
+ return null;
+ } else if (idMap.get(leafValue)) {
+ const newIdentifier = objectUtils.makeKeyString({
+ namespace,
+ key: idMap.get(leafValue)
+ });
+
+ return newIdentifier;
+ } else {
+ return leafValue;
+ }
+ }
+
+ rewriteObjectIdentifiers(importData, rootIdentifier) {
+ const namespace = rootIdentifier.namespace;
+ const idMap = new Map();
+ const objectTree = importData.openmct;
+
+ Object.keys(objectTree).forEach((originalId, index) => {
+ let newId = index.toString();
+ if (originalId === importData.rootId) {
+ newId = rootIdentifier.key;
}
+
+ idMap.set(originalId, newId);
});
- return JSON.parse(objectString);
+ const newTree = this.parseTreeLeaf(null, objectTree, idMap, namespace);
+
+ return newTree;
}
/**
* Converts all objects in an object make from old format objects to new
* format objects.
*/
- function convertToNewObjects(oldObjectMap) {
+ convertToNewObjects(oldObjectMap) {
return Object.keys(oldObjectMap)
.reduce(function (newObjectMap, key) {
newObjectMap[key] = objectUtils.toNewFormat(oldObjectMap[key], key);
@@ -49,7 +147,7 @@ define([
}
/* Set the root location correctly for a top-level object */
- function setRootLocation(objectMap, rootIdentifier) {
+ setRootLocation(objectMap, rootIdentifier) {
objectMap[objectUtils.makeKeyString(rootIdentifier)].location = 'ROOT';
return objectMap;
@@ -59,24 +157,11 @@ define([
* Takes importData (as provided by the ImportExport plugin) and exposes
* an object provider to fetch those objects.
*/
- function StaticModelProvider(importData, rootIdentifier) {
- const oldFormatObjectMap = rewriteObjectIdentifiers(importData, rootIdentifier);
- const newFormatObjectMap = convertToNewObjects(oldFormatObjectMap);
- this.objectMap = setRootLocation(newFormatObjectMap, rootIdentifier);
+ rewriteModel(importData, rootIdentifier) {
+ const oldFormatObjectMap = this.rewriteObjectIdentifiers(importData, rootIdentifier);
+ const newFormatObjectMap = this.convertToNewObjects(oldFormatObjectMap);
+ this.objectMap = this.setRootLocation(newFormatObjectMap, rootIdentifier);
}
+}
- /**
- * Standard "Get".
- */
- StaticModelProvider.prototype.get = function (identifier) {
- const keyString = objectUtils.makeKeyString(identifier);
- if (this.objectMap[keyString]) {
- return this.objectMap[keyString];
- }
-
- throw new Error(keyString + ' not found in import models.');
- };
-
- return StaticModelProvider;
-
-});
+export default StaticModelProvider;
diff --git a/src/plugins/staticRootPlugin/StaticModelProviderSpec.js b/src/plugins/staticRootPlugin/StaticModelProviderSpec.js
index 07118ddeb..0044c30e0 100644
--- a/src/plugins/staticRootPlugin/StaticModelProviderSpec.js
+++ b/src/plugins/staticRootPlugin/StaticModelProviderSpec.js
@@ -1,133 +1,149 @@
-define([
- './StaticModelProvider',
- './static-provider-test.json'
-], function (
- StaticModelProvider,
- testStaticData
-) {
-
- describe('StaticModelProvider', function () {
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+import testStaticData from './static-provider-test.json';
+import StaticModelProvider from './StaticModelProvider';
+
+describe('StaticModelProvider', function () {
+
+ let staticProvider;
+
+ beforeEach(function () {
+ const staticData = JSON.parse(JSON.stringify(testStaticData));
+ staticProvider = new StaticModelProvider(staticData, {
+ namespace: 'my-import',
+ key: 'root'
+ });
+ });
- let staticProvider;
+ describe('rootObject', function () {
+ let rootModel;
beforeEach(function () {
- const staticData = JSON.parse(JSON.stringify(testStaticData));
- staticProvider = new StaticModelProvider(staticData, {
+ rootModel = staticProvider.get({
namespace: 'my-import',
key: 'root'
});
});
- describe('rootObject', function () {
- let rootModel;
+ it('is located at top level', function () {
+ expect(rootModel.location).toBe('ROOT');
+ });
- beforeEach(function () {
- rootModel = staticProvider.get({
- namespace: 'my-import',
- key: 'root'
- });
+ it('has new-format identifier', function () {
+ expect(rootModel.identifier).toEqual({
+ namespace: 'my-import',
+ key: 'root'
});
+ });
- it('is located at top level', function () {
- expect(rootModel.location).toBe('ROOT');
+ it('has new-format composition', function () {
+ expect(rootModel.composition).toContain({
+ namespace: 'my-import',
+ key: '1'
});
-
- it('has new-format identifier', function () {
- expect(rootModel.identifier).toEqual({
- namespace: 'my-import',
- key: 'root'
- });
+ expect(rootModel.composition).toContain({
+ namespace: 'my-import',
+ key: '2'
});
+ });
+ });
- it('has new-format composition', function () {
- expect(rootModel.composition).toContain({
- namespace: 'my-import',
- key: '1'
- });
- expect(rootModel.composition).toContain({
- namespace: 'my-import',
- key: '2'
- });
+ describe('childObjects', function () {
+ let swg;
+ let layout;
+ let fixed;
+
+ beforeEach(function () {
+ swg = staticProvider.get({
+ namespace: 'my-import',
+ key: '1'
+ });
+ layout = staticProvider.get({
+ namespace: 'my-import',
+ key: '2'
+ });
+ fixed = staticProvider.get({
+ namespace: 'my-import',
+ key: '3'
});
});
- describe('childObjects', function () {
- let swg;
- let layout;
- let fixed;
-
- beforeEach(function () {
- swg = staticProvider.get({
- namespace: 'my-import',
- key: '1'
- });
- layout = staticProvider.get({
- namespace: 'my-import',
- key: '2'
- });
- fixed = staticProvider.get({
- namespace: 'my-import',
- key: '3'
- });
- });
+ it('match expected ordering', function () {
+ // this is a sanity check to make sure the identifiers map in
+ // the correct order.
+ expect(swg.type).toBe('generator');
+ expect(layout.type).toBe('layout');
+ expect(fixed.type).toBe('telemetry.fixed');
+ });
- it('match expected ordering', function () {
- // this is a sanity check to make sure the identifiers map in
- // the correct order.
- expect(swg.type).toBe('generator');
- expect(layout.type).toBe('layout');
- expect(fixed.type).toBe('telemetry.fixed');
+ it('have new-style identifiers', function () {
+ expect(swg.identifier).toEqual({
+ namespace: 'my-import',
+ key: '1'
});
-
- it('have new-style identifiers', function () {
- expect(swg.identifier).toEqual({
- namespace: 'my-import',
- key: '1'
- });
- expect(layout.identifier).toEqual({
- namespace: 'my-import',
- key: '2'
- });
- expect(fixed.identifier).toEqual({
- namespace: 'my-import',
- key: '3'
- });
+ expect(layout.identifier).toEqual({
+ namespace: 'my-import',
+ key: '2'
});
-
- it('have new-style composition', function () {
- expect(layout.composition).toContain({
- namespace: 'my-import',
- key: '1'
- });
- expect(layout.composition).toContain({
- namespace: 'my-import',
- key: '3'
- });
- expect(fixed.composition).toContain({
- namespace: 'my-import',
- key: '1'
- });
+ expect(fixed.identifier).toEqual({
+ namespace: 'my-import',
+ key: '3'
});
+ });
- it('rewrites locations', function () {
- expect(swg.location).toBe('my-import:root');
- expect(layout.location).toBe('my-import:root');
- expect(fixed.location).toBe('my-import:2');
+ it('have new-style composition', function () {
+ expect(layout.composition).toContain({
+ namespace: 'my-import',
+ key: '1'
});
-
- it('rewrites matched identifiers in objects', function () {
- expect(layout.configuration.layout.panels['my-import:1'])
- .toBeDefined();
- expect(layout.configuration.layout.panels['my-import:3'])
- .toBeDefined();
- expect(layout.configuration.layout.panels['483c00d4-bb1d-4b42-b29a-c58e06b322a0'])
- .not.toBeDefined();
- expect(layout.configuration.layout.panels['20273193-f069-49e9-b4f7-b97a87ed755d'])
- .not.toBeDefined();
- expect(fixed.configuration['fixed-display'].elements[0].id)
- .toBe('my-import:1');
+ expect(layout.composition).toContain({
+ namespace: 'my-import',
+ key: '3'
+ });
+ expect(fixed.composition).toContain({
+ namespace: 'my-import',
+ key: '1'
});
+ });
+
+ it('rewrites locations', function () {
+ expect(swg.location).toBe('my-import:root');
+ expect(layout.location).toBe('my-import:root');
+ expect(fixed.location).toBe('my-import:2');
+ });
+ it('rewrites matched identifiers in objects', function () {
+ expect(layout.configuration.layout.panels['my-import:1'])
+ .toBeDefined();
+ expect(layout.configuration.layout.panels['my-import:3'])
+ .toBeDefined();
+ expect(layout.configuration.layout.panels['483c00d4-bb1d-4b42-b29a-c58e06b322a0'])
+ .not.toBeDefined();
+ expect(layout.configuration.layout.panels['20273193-f069-49e9-b4f7-b97a87ed755d'])
+ .not.toBeDefined();
+ expect(fixed.configuration['fixed-display'].elements[0].id)
+ .toBe('my-import:1');
});
+
});
});
diff --git a/src/plugins/staticRootPlugin/plugin.js b/src/plugins/staticRootPlugin/plugin.js
index 59029eff2..aca8ba62d 100644
--- a/src/plugins/staticRootPlugin/plugin.js
+++ b/src/plugins/staticRootPlugin/plugin.js
@@ -1,52 +1,63 @@
-define([
- './StaticModelProvider'
-], function (
- StaticModelProvider
-) {
- /**
- * Static Root Plugin: takes an export file and exposes it as a new root
- * object.
- */
- function StaticRootPlugin(namespace, exportUrl) {
-
- const rootIdentifier = {
- namespace: namespace,
- key: 'root'
- };
-
- let cachedProvider;
-
- function loadProvider() {
- return fetch(exportUrl)
- .then(function (response) {
- return response.json();
- })
- .then(function (importData) {
- cachedProvider = new StaticModelProvider(importData, rootIdentifier);
-
- return cachedProvider;
- });
- }
-
- function getProvider() {
- if (!cachedProvider) {
- cachedProvider = loadProvider();
- }
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+import StaticModelProvider from './StaticModelProvider';
+
+export default function StaticRootPlugin(options) {
+ const rootIdentifier = {
+ namespace: options.namespace,
+ key: 'root'
+ };
+
+ let cachedProvider;
+
+ function loadProvider() {
+ return fetch(options.exportUrl)
+ .then(function (response) {
+ return response.json();
+ })
+ .then(function (importData) {
+ cachedProvider = new StaticModelProvider(importData, rootIdentifier);
+
+ return cachedProvider;
+ });
+ }
- return Promise.resolve(cachedProvider);
+ function getProvider() {
+ if (!cachedProvider) {
+ cachedProvider = loadProvider();
}
- return function install(openmct) {
- openmct.objects.addRoot(rootIdentifier);
- openmct.objects.addProvider(namespace, {
- get: function (identifier) {
- return getProvider().then(function (provider) {
- return provider.get(identifier);
- });
- }
- });
- };
+ return Promise.resolve(cachedProvider);
}
- return StaticRootPlugin;
-});
+ return function install(openmct) {
+ openmct.objects.addRoot(rootIdentifier);
+ openmct.objects.addProvider(options.namespace, {
+ get: function (identifier) {
+ return getProvider().then(function (provider) {
+ return provider.get(identifier);
+ });
+ }
+ });
+ };
+}
diff --git a/src/plugins/staticRootPlugin/static-provider-test.json b/src/plugins/staticRootPlugin/static-provider-test.json
index 541f8efbc..8c523de4a 100644
--- a/src/plugins/staticRootPlugin/static-provider-test.json
+++ b/src/plugins/staticRootPlugin/static-provider-test.json
@@ -1 +1 @@
-{"openmct":{"a9122832-4b6e-43ea-8219-5359c14c5de8":{"composition":["483c00d4-bb1d-4b42-b29a-c58e06b322a0","d2ac3ae4-0af2-49fe-81af-adac09936215"],"name":"import-provider-test","type":"folder","notes":"test data for import provider.","modified":1508522673278,"location":"mine","persisted":1508522673278},"483c00d4-bb1d-4b42-b29a-c58e06b322a0":{"telemetry":{"period":10,"amplitude":1,"offset":0,"dataRateInHz":1,"values":[{"key":"utc","name":"Time","format":"utc","hints":{"domain":1,"priority":0},"source":"utc"},{"key":"yesterday","name":"Yesterday","format":"utc","hints":{"domain":2,"priority":1},"source":"yesterday"},{"key":"sin","name":"Sine","hints":{"range":1,"priority":2},"source":"sin"},{"key":"cos","name":"Cosine","hints":{"range":2,"priority":3},"source":"cos"}]},"name":"SWG-10","type":"generator","modified":1508522652874,"location":"a9122832-4b6e-43ea-8219-5359c14c5de8","persisted":1508522652874},"d2ac3ae4-0af2-49fe-81af-adac09936215":{"composition":["483c00d4-bb1d-4b42-b29a-c58e06b322a0","20273193-f069-49e9-b4f7-b97a87ed755d"],"name":"Layout","type":"layout","configuration":{"layout":{"panels":{"483c00d4-bb1d-4b42-b29a-c58e06b322a0":{"position":[0,0],"dimensions":[17,8]},"20273193-f069-49e9-b4f7-b97a87ed755d":{"position":[0,8],"dimensions":[17,1],"hasFrame":false}}}},"modified":1508522745580,"location":"a9122832-4b6e-43ea-8219-5359c14c5de8","persisted":1508522745580},"20273193-f069-49e9-b4f7-b97a87ed755d":{"layoutGrid":[64,16],"composition":["483c00d4-bb1d-4b42-b29a-c58e06b322a0"],"name":"FP Test","type":"telemetry.fixed","configuration":{"fixed-display":{"elements":[{"type":"fixed.telemetry","x":0,"y":0,"id":"483c00d4-bb1d-4b42-b29a-c58e06b322a0","stroke":"transparent","color":"","titled":true,"width":8,"height":2,"useGrid":true,"size":"24px"}]}},"modified":1508522717619,"location":"d2ac3ae4-0af2-49fe-81af-adac09936215","persisted":1508522717619}},"rootId":"a9122832-4b6e-43ea-8219-5359c14c5de8"} \ No newline at end of file
+{"openmct":{"a9122832-4b6e-43ea-8219-5359c14c5de8":{"composition":["483c00d4-bb1d-4b42-b29a-c58e06b322a0","d2ac3ae4-0af2-49fe-81af-adac09936215"],"name":"import-provider-test","type":"folder","notes":null,"modified":1508522673278,"location":"mine","persisted":1508522673278},"483c00d4-bb1d-4b42-b29a-c58e06b322a0":{"telemetry":{"period":10,"amplitude":1,"offset":0,"dataRateInHz":1,"values":[{"key":"utc","name":"Time","format":"utc","hints":{"domain":1,"priority":0},"source":"utc"},{"key":"yesterday","name":"Yesterday","format":"utc","hints":{"domain":2,"priority":1},"source":"yesterday"},{"key":"sin","name":"Sine","hints":{"range":1,"priority":2},"source":"sin"},{"key":"cos","name":"Cosine","hints":{"range":2,"priority":3},"source":"cos"}]},"name":"SWG-10","type":"generator","modified":1508522652874,"location":"a9122832-4b6e-43ea-8219-5359c14c5de8","persisted":1508522652874},"d2ac3ae4-0af2-49fe-81af-adac09936215":{"composition":["483c00d4-bb1d-4b42-b29a-c58e06b322a0","20273193-f069-49e9-b4f7-b97a87ed755d"],"name":"Layout","type":"layout","configuration":{"layout":{"panels":{"483c00d4-bb1d-4b42-b29a-c58e06b322a0":{"position":[0,0],"dimensions":[17,8]},"20273193-f069-49e9-b4f7-b97a87ed755d":{"position":[0,8],"dimensions":[17,1],"hasFrame":false}}}},"modified":1508522745580,"location":"a9122832-4b6e-43ea-8219-5359c14c5de8","persisted":1508522745580},"20273193-f069-49e9-b4f7-b97a87ed755d":{"layoutGrid":[64,16],"composition":["483c00d4-bb1d-4b42-b29a-c58e06b322a0"],"name":"FP Test","type":"telemetry.fixed","configuration":{"fixed-display":{"elements":[{"type":"fixed.telemetry","x":0,"y":0,"id":"483c00d4-bb1d-4b42-b29a-c58e06b322a0","stroke":"transparent","color":"","titled":true,"width":8,"height":2,"useGrid":true,"size":"24px"}]}},"modified":1508522717619,"location":"d2ac3ae4-0af2-49fe-81af-adac09936215","persisted":1508522717619}},"rootId":"a9122832-4b6e-43ea-8219-5359c14c5de8"} \ No newline at end of file
diff --git a/src/plugins/summaryWidget/SummaryWidgetViewPolicy.js b/src/plugins/summaryWidget/SummaryWidgetViewPolicy.js
index 3117560c1..8d4db0200 100644
--- a/src/plugins/summaryWidget/SummaryWidgetViewPolicy.js
+++ b/src/plugins/summaryWidget/SummaryWidgetViewPolicy.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/summaryWidget/SummaryWidgetsCompositionPolicy.js b/src/plugins/summaryWidget/SummaryWidgetsCompositionPolicy.js
index 6186290a4..09e002211 100644
--- a/src/plugins/summaryWidget/SummaryWidgetsCompositionPolicy.js
+++ b/src/plugins/summaryWidget/SummaryWidgetsCompositionPolicy.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/summaryWidget/src/Condition.js b/src/plugins/summaryWidget/src/Condition.js
index 997ad67b4..66f9ecbeb 100644
--- a/src/plugins/summaryWidget/src/Condition.js
+++ b/src/plugins/summaryWidget/src/Condition.js
@@ -4,16 +4,16 @@ define([
'./input/KeySelect',
'./input/OperationSelect',
'./eventHelpers',
- 'EventEmitter',
- 'zepto'
+ '../../../utils/template/templateHelpers',
+ 'EventEmitter'
], function (
conditionTemplate,
ObjectSelect,
KeySelect,
OperationSelect,
eventHelpers,
- EventEmitter,
- $
+ templateHelpers,
+ EventEmitter
) {
/**
* Represents an individual condition for a summary widget rule. Manages the
@@ -31,12 +31,13 @@ define([
this.index = index;
this.conditionManager = conditionManager;
- this.domElement = $(conditionTemplate);
+ this.domElement = templateHelpers.convertTemplateToHTML(conditionTemplate)[0];
+
this.eventEmitter = new EventEmitter();
this.supportedCallbacks = ['remove', 'duplicate', 'change'];
- this.deleteButton = $('.t-delete', this.domElement);
- this.duplicateButton = $('.t-duplicate', this.domElement);
+ this.deleteButton = this.domElement.querySelector('.t-delete');
+ this.duplicateButton = this.domElement.querySelector('.t-duplicate');
this.selects = {};
this.valueInputs = [];
@@ -105,9 +106,10 @@ define([
});
Object.values(this.selects).forEach(function (select) {
- $('.t-configuration', self.domElement).append(select.getDOM());
+ self.domElement.querySelector('.t-configuration').append(select.getDOM());
});
- this.listenTo($('.t-value-inputs', this.domElement), 'input', onValueInput);
+
+ this.listenTo(this.domElement.querySelector('.t-value-inputs'), 'input', onValueInput);
}
Condition.prototype.getDOM = function (container) {
@@ -132,7 +134,7 @@ define([
* Hide the appropriate inputs when this is the only condition
*/
Condition.prototype.hideButtons = function () {
- this.deleteButton.hide();
+ this.deleteButton.style.display = 'none';
};
/**
@@ -172,14 +174,14 @@ define([
*/
Condition.prototype.generateValueInputs = function (operation) {
const evaluator = this.conditionManager.getEvaluator();
- const inputArea = $('.t-value-inputs', this.domElement);
+ const inputArea = this.domElement.querySelector('.t-value-inputs');
let inputCount;
let inputType;
let newInput;
let index = 0;
let emitChange = false;
- inputArea.html('');
+ inputArea.innerHTML = '';
this.valueInputs = [];
this.config.values = this.config.values || [];
@@ -189,17 +191,24 @@ define([
while (index < inputCount) {
if (inputType === 'select') {
- newInput = $('<select>' + this.generateSelectOptions() + '</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 = $('<input type = "' + inputType + '" value = "' + value + '"></input>');
+
+ newInput = document.createElement("input");
+ newInput.type = `${inputType}`;
+ newInput.value = `${value}`;
}
- this.valueInputs.push(newInput.get(0));
- inputArea.append(newInput);
+ this.valueInputs.push(newInput);
+ inputArea.appendChild(newInput);
index += 1;
}
diff --git a/src/plugins/summaryWidget/src/ConditionManager.js b/src/plugins/summaryWidget/src/ConditionManager.js
index ff90bc7bc..e50264903 100644
--- a/src/plugins/summaryWidget/src/ConditionManager.js
+++ b/src/plugins/summaryWidget/src/ConditionManager.js
@@ -2,13 +2,11 @@ define ([
'./ConditionEvaluator',
'objectUtils',
'EventEmitter',
- 'zepto',
'lodash'
], function (
ConditionEvaluator,
objectUtils,
EventEmitter,
- $,
_
) {
@@ -232,7 +230,10 @@ define ([
self.eventEmitter.emit('add', obj);
- $('.w-summary-widget').removeClass('s-status-no-data');
+ const summaryWidget = document.querySelector('.w-summary-widget');
+ if (summaryWidget) {
+ summaryWidget.classList.remove('s-status-no-data');
+ }
}
};
@@ -256,7 +257,10 @@ define ([
this.eventEmitter.emit('remove', identifier);
if (_.isEmpty(this.compositionObjs)) {
- $('.w-summary-widget').addClass('s-status-no-data');
+ const summaryWidget = document.querySelector('.w-summary-widget');
+ if (summaryWidget) {
+ summaryWidget.classList.add('s-status-no-data');
+ }
}
};
diff --git a/src/plugins/summaryWidget/src/Rule.js b/src/plugins/summaryWidget/src/Rule.js
index d9217f0e0..0b8f28804 100644
--- a/src/plugins/summaryWidget/src/Rule.js
+++ b/src/plugins/summaryWidget/src/Rule.js
@@ -4,18 +4,18 @@ define([
'./input/ColorPalette',
'./input/IconPalette',
'./eventHelpers',
+ '../../../utils/template/templateHelpers',
'EventEmitter',
- 'lodash',
- 'zepto'
+ 'lodash'
], function (
ruleTemplate,
Condition,
ColorPalette,
IconPalette,
eventHelpers,
+ templateHelpers,
EventEmitter,
- _,
- $
+ _
) {
/**
* An object representing a summary widget rule. Maintains a set of text
@@ -41,7 +41,7 @@ define([
this.widgetDnD = widgetDnD;
this.container = container;
- this.domElement = $(ruleTemplate);
+ this.domElement = templateHelpers.convertTemplateToHTML(ruleTemplate)[0];
this.eventEmitter = new EventEmitter();
this.supportedCallbacks = ['remove', 'duplicate', 'change', 'conditionChange'];
this.conditions = [];
@@ -50,31 +50,32 @@ define([
this.remove = this.remove.bind(this);
this.duplicate = this.duplicate.bind(this);
- this.thumbnail = $('.t-widget-thumb', this.domElement);
- this.thumbnailIcon = $('.js-sw__icon', this.domElement);
- this.thumbnailLabel = $('.c-sw__label', this.domElement);
- this.title = $('.rule-title', this.domElement);
- this.description = $('.rule-description', this.domElement);
- this.trigger = $('.t-trigger', this.domElement);
- this.toggleConfigButton = $('.js-disclosure', this.domElement);
- this.configArea = $('.widget-rule-content', this.domElement);
- this.grippy = $('.t-grippy', this.domElement);
- this.conditionArea = $('.t-widget-rule-config', this.domElement);
- this.jsConditionArea = $('.t-rule-js-condition-input-holder', this.domElement);
- this.deleteButton = $('.t-delete', this.domElement);
- this.duplicateButton = $('.t-duplicate', this.domElement);
- this.addConditionButton = $('.add-condition', this.domElement);
+ 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: $('.t-rule-name-input', this.domElement),
- label: $('.t-rule-label-input', this.domElement),
- message: $('.t-rule-message-input', this.domElement),
- jsCondition: $('.t-rule-js-condition-input', this.domElement)
+ 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);
@@ -94,7 +95,7 @@ define([
function onIconInput(icon) {
self.config.icon = icon;
self.updateDomainObject('icon', icon);
- self.thumbnailIcon.removeClass().addClass(THUMB_ICON_CLASS + ' ' + icon);
+ self.thumbnailIcon.className = `${THUMB_ICON_CLASS + ' ' + icon}`;
self.eventEmitter.emit('change');
}
@@ -106,7 +107,7 @@ define([
*/
function onColorInput(color, property) {
self.config.style[property] = color;
- self.thumbnail.css(property, color);
+ self.thumbnail.style[property] = color;
self.eventEmitter.emit('change');
}
@@ -116,7 +117,10 @@ define([
* @private
*/
function encodeMsg(msg) {
- return $('<div />').text(msg).html();
+ const div = document.createElement('div');
+ div.innerText = msg;
+
+ return div.innerText;
}
/**
@@ -144,9 +148,9 @@ define([
self.config[inputKey] = text;
self.updateDomainObject();
if (inputKey === 'name') {
- self.title.html(text);
+ self.title.innerText = text;
} else if (inputKey === 'label') {
- self.thumbnailLabel.html(text);
+ self.thumbnailLabel.innerText = text;
}
self.eventEmitter.emit('change');
@@ -158,13 +162,14 @@ define([
* @private
*/
function onDragStart(event) {
- $('.t-drag-indicator').each(function () {
+ document.querySelectorAll('.t-drag-indicator').forEach(indicator => {
// eslint-disable-next-line no-invalid-this
- $(this).html($('.widget-rule-header', self.domElement).clone().get(0));
+ const ruleHeader = self.domElement.querySelectorAll('.widget-rule-header')[0].cloneNode(true);
+ indicator.innerHTML = ruleHeader;
});
- self.widgetDnD.setDragImage($('.widget-rule-header', self.domElement).clone().get(0));
+ self.widgetDnD.setDragImage(self.domElement.querySelectorAll('.widget-rule-header')[0].cloneNode(true));
self.widgetDnD.dragStart(self.config.id);
- self.domElement.hide();
+ self.domElement.style.display = 'none';
}
/**
@@ -172,20 +177,31 @@ define([
* @private
*/
function toggleConfig() {
- self.configArea.toggleClass('expanded');
- self.toggleConfigButton.toggleClass('c-disclosure-triangle--expanded');
+ 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;
}
- $('.t-rule-label-input', this.domElement).before(this.iconInput.getDOM());
+ 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.removeClass().addClass(THUMB_ICON_CLASS + ' ' + self.config.icon);
- this.thumbnailLabel.html(self.config.label);
+ 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];
@@ -198,15 +214,17 @@ define([
self.updateDomainObject();
});
- $('.t-style-input', self.domElement).append(input.getDOM());
+ self.domElement.querySelector('.t-style-input').append(input.getDOM());
});
Object.keys(this.textInputs).forEach(function (inputKey) {
- self.textInputs[inputKey].prop('value', self.config[inputKey] || '');
- self.listenTo(self.textInputs[inputKey], 'input', function () {
- // eslint-disable-next-line no-invalid-this
- onTextInput(this, 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);
@@ -217,15 +235,15 @@ define([
this.listenTo(this.toggleConfigButton, 'click', toggleConfig);
this.listenTo(this.trigger, 'change', onTriggerInput);
- this.title.html(self.config.name);
- this.description.html(self.config.description);
- this.trigger.prop('value', self.config.trigger);
+ 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();
- $('.t-drag-indicator').hide();
+ document.querySelector('.t-drag-indicator').style.display = 'none';
}, this);
if (!this.conditionManager.loadCompleted()) {
@@ -233,21 +251,21 @@ define([
}
if (!this.config.expanded) {
- this.configArea.removeClass('expanded');
- this.toggleConfigButton.removeClass('c-disclosure-triangle--expanded');
+ this.configArea.classList.remove('expanded');
+ this.toggleConfigButton.classList.remove('c-disclosure-triangle--expanded');
}
if (this.domainObject.configuration.ruleOrder.length === 2) {
- $('.t-grippy', this.domElement).hide();
+ 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') {
- $('.t-delete', this.domElement).hide();
- $('.t-widget-rule-config', this.domElement).hide();
- $('.t-grippy', this.domElement).hide();
+ 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';
}
}
@@ -304,8 +322,8 @@ define([
* During a rule drag event, show the placeholder element after this rule
*/
Rule.prototype.showDragIndicator = function () {
- $('.t-drag-indicator').hide();
- $('.t-drag-indicator', this.domElement).show();
+ document.querySelector('.t-drag-indicator').style.display = 'none';
+ this.domElement.querySelector('.t-drag-indicator').style.display = '';
};
/**
@@ -397,7 +415,10 @@ define([
const triggerContextStr = self.config.trigger === 'any' ? ' or ' : ' and ';
self.conditions = [];
- $('.t-condition', this.domElement).remove();
+
+ this.domElement.querySelectorAll('.t-condition').forEach(condition => {
+ condition.remove();
+ });
this.config.conditions.forEach(function (condition, index) {
const newCondition = new Condition(condition, index, self.conditionManager);
@@ -408,16 +429,23 @@ define([
});
if (this.config.trigger === 'js') {
- this.jsConditionArea.show();
- this.addConditionButton.hide();
+ if (this.jsConditionArea) {
+ this.jsConditionArea.style.display = '';
+ }
+
+ this.addConditionButton.style.display = 'none';
} else {
- this.jsConditionArea.hide();
- this.addConditionButton.show();
+ if (this.jsConditionArea) {
+ this.jsConditionArea.style.display = 'none';
+ }
+
+ this.addConditionButton.style.display = '';
self.conditions.forEach(function (condition) {
$condition = condition.getDOM();
- $('li:last-of-type', self.conditionArea).before($condition);
+ const lastOfType = self.conditionArea.querySelector('li:last-of-type');
+ lastOfType.parentNode.insertBefore($condition, lastOfType);
if (loopCnt > 0) {
- $('.t-condition-context', $condition).html(triggerContextStr + ' when');
+ $condition.querySelector('.t-condition-context').innerHTML = triggerContextStr + ' when';
}
loopCnt++;
@@ -489,7 +517,7 @@ define([
}
description = (description === '' ? this.config.description : description);
- this.description.html(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 1a5c1ceba..e9c1442bf 100644
--- a/src/plugins/summaryWidget/src/SummaryWidget.js
+++ b/src/plugins/summaryWidget/src/SummaryWidget.js
@@ -5,9 +5,9 @@ define([
'./TestDataManager',
'./WidgetDnD',
'./eventHelpers',
+ '../../../utils/template/templateHelpers',
'objectUtils',
'lodash',
- 'zepto',
'@braintree/sanitize-url'
], function (
widgetTemplate,
@@ -16,9 +16,9 @@ define([
TestDataManager,
WidgetDnD,
eventHelpers,
+ templateHelpers,
objectUtils,
_,
- $,
urlSanitizeLib
) {
@@ -54,20 +54,22 @@ define([
this.activeId = 'default';
this.rulesById = {};
- this.domElement = $(widgetTemplate);
- this.toggleRulesControl = $('.t-view-control-rules', this.domElement);
- this.toggleTestDataControl = $('.t-view-control-test-data', this.domElement);
- this.widgetButton = this.domElement.children('#widget');
+ 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 = $.noop;
+ this.editListenerUnsubscribe = () => {};
- this.outerWrapper = $('.widget-edit-holder', this.domElement);
- this.ruleArea = $('#ruleArea', this.domElement);
- this.configAreaRules = $('.widget-rules-wrapper', this.domElement);
+ this.outerWrapper = this.domElement.querySelector('.widget-edit-holder');
+ this.ruleArea = this.domElement.querySelector('#ruleArea');
+ this.configAreaRules = this.domElement.querySelector('.widget-rules-wrapper');
- this.testDataArea = $('.widget-test-data', this.domElement);
- this.addRuleButton = $('#addRule', this.domElement);
+ 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);
@@ -87,8 +89,17 @@ define([
* @private
*/
function toggleTestData() {
- self.outerWrapper.toggleClass('expanded-widget-test-data');
- self.toggleTestDataControl.toggleClass('c-disclosure-triangle--expanded');
+ 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');
+ }
+
+ 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.listenTo(this.toggleTestDataControl, 'click', toggleTestData);
@@ -98,8 +109,8 @@ define([
* @private
*/
function toggleRules() {
- self.outerWrapper.toggleClass('expanded-widget-rules');
- self.toggleRulesControl.toggleClass('c-disclosure-triangle--expanded');
+ templateHelpers.toggleClass(self.outerWrapper, 'expanded-widget-rules');
+ templateHelpers.toggleClass(self.toggleRulesControl, 'c-disclosure-triangle--expanded');
}
this.listenTo(this.toggleRulesControl, 'click', toggleRules);
@@ -113,15 +124,15 @@ define([
*/
SummaryWidget.prototype.addHyperlink = function (url, openNewTab) {
if (url) {
- this.widgetButton.attr('href', urlSanitizeLib.sanitizeUrl(url));
+ this.widgetButton.href = urlSanitizeLib.sanitizeUrl(url);
} else {
- this.widgetButton.removeAttr('href');
+ this.widgetButton.removeAttribute('href');
}
if (openNewTab === 'newTab') {
- this.widgetButton.attr('target', '_blank');
+ this.widgetButton.target = '_blank';
} else {
- this.widgetButton.removeAttr('target');
+ this.widgetButton.removeAttribute('target');
}
};
@@ -149,8 +160,8 @@ define([
SummaryWidget.prototype.show = function (container) {
const self = this;
this.container = container;
- $(container).append(this.domElement);
- $('.widget-test-data', this.domElement).append(this.testDataManager.getDOM());
+ 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) {
@@ -190,7 +201,7 @@ define([
const self = this;
const ruleOrder = self.domainObject.configuration.ruleOrder;
const rules = self.rulesById;
- self.ruleArea.html('');
+ self.ruleArea.innerHTML = '';
Object.values(ruleOrder).forEach(function (ruleId) {
self.ruleArea.append(rules[ruleId].getDOM());
});
@@ -205,9 +216,9 @@ define([
rules.forEach(function (ruleKey, index, array) {
if (array.length > 2 && index > 0) {
- $('.t-grippy', rulesById[ruleKey].domElement).show();
+ rulesById[ruleKey].domElement.querySelector('.t-grippy').style.display = '';
} else {
- $('.t-grippy', rulesById[ruleKey].domElement).hide();
+ rulesById[ruleKey].domElement.querySelector('.t-grippy').style.display = 'none';
}
});
};
@@ -218,10 +229,10 @@ define([
SummaryWidget.prototype.updateWidget = function () {
const WIDGET_ICON_CLASS = 'c-sw__icon js-sw__icon';
const activeRule = this.rulesById[this.activeId];
- this.applyStyle($('#widget', this.domElement), activeRule.getProperty('style'));
- $('#widget', this.domElement).prop('title', activeRule.getProperty('message'));
- $('#widgetLabel', this.domElement).html(activeRule.getProperty('label'));
- $('#widgetIcon', this.domElement).removeClass().addClass(WIDGET_ICON_CLASS + ' ' + activeRule.getProperty('icon'));
+ 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');
};
/**
@@ -356,7 +367,7 @@ define([
*/
SummaryWidget.prototype.applyStyle = function (elem, style) {
Object.keys(style).forEach(function (propId) {
- elem.css(propId, style[propId]);
+ elem.style[propId] = style[propId];
});
};
diff --git a/src/plugins/summaryWidget/src/TestDataItem.js b/src/plugins/summaryWidget/src/TestDataItem.js
index 32b737a90..ae005c46d 100644
--- a/src/plugins/summaryWidget/src/TestDataItem.js
+++ b/src/plugins/summaryWidget/src/TestDataItem.js
@@ -3,15 +3,15 @@ define([
'./input/ObjectSelect',
'./input/KeySelect',
'./eventHelpers',
- 'EventEmitter',
- 'zepto'
+ '../../../utils/template/templateHelpers',
+ 'EventEmitter'
], function (
itemTemplate,
ObjectSelect,
KeySelect,
eventHelpers,
- EventEmitter,
- $
+ templateHelpers,
+ EventEmitter
) {
/**
@@ -31,12 +31,12 @@ define([
this.index = index;
this.conditionManager = conditionManager;
- this.domElement = $(itemTemplate);
+ this.domElement = templateHelpers.convertTemplateToHTML(itemTemplate)[0];
this.eventEmitter = new EventEmitter();
this.supportedCallbacks = ['remove', 'duplicate', 'change'];
- this.deleteButton = $('.t-delete', this.domElement);
- this.duplicateButton = $('.t-duplicate', this.domElement);
+ this.deleteButton = this.domElement.querySelector('.t-delete');
+ this.duplicateButton = this.domElement.querySelector('.t-duplicate');
this.selects = {};
this.valueInputs = [];
@@ -101,7 +101,7 @@ define([
});
Object.values(this.selects).forEach(function (select) {
- $('.t-configuration', self.domElement).append(select.getDOM());
+ self.domElement.querySelector('.t-configuration').append(select.getDOM());
});
this.listenTo(this.domElement, 'input', onValueInput);
}
@@ -139,7 +139,7 @@ define([
* Hide the appropriate inputs when this is the only item
*/
TestDataItem.prototype.hideButtons = function () {
- this.deleteButton.hide();
+ this.deleteButton.style.display = 'none';
};
/**
@@ -177,17 +177,21 @@ define([
*/
TestDataItem.prototype.generateValueInput = function (key) {
const evaluator = this.conditionManager.getEvaluator();
- const inputArea = $('.t-value-inputs', this.domElement);
+ const inputArea = this.domElement.querySelector('.t-value-inputs');
const dataType = this.conditionManager.getTelemetryPropertyType(this.config.object, key);
const inputType = evaluator.getInputTypeById(dataType);
- inputArea.html('');
+ inputArea.innerHTML = '';
if (inputType) {
if (!this.config.value) {
this.config.value = (inputType === 'number' ? 0 : '');
}
- this.valueInput = $('<input class="sm" type = "' + inputType + '" value = "' + this.config.value + '"> </input>').get(0);
+ 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 819cc5ee3..70240453d 100644
--- a/src/plugins/summaryWidget/src/TestDataManager.js
+++ b/src/plugins/summaryWidget/src/TestDataManager.js
@@ -2,13 +2,13 @@ define([
'./eventHelpers',
'../res/testDataTemplate.html',
'./TestDataItem',
- 'zepto',
+ '../../../utils/template/templateHelpers',
'lodash'
], function (
eventHelpers,
testDataTemplate,
TestDataItem,
- $,
+ templateHelpers,
_
) {
@@ -28,13 +28,13 @@ define([
this.openmct = openmct;
this.evaluator = this.manager.getEvaluator();
- this.domElement = $(testDataTemplate);
+ this.domElement = templateHelpers.convertTemplateToHTML(testDataTemplate)[0];
this.config = this.domainObject.configuration.testDataConfig;
this.testCache = {};
- this.itemArea = $('.t-test-data-config', this.domElement);
- this.addItemButton = $('.add-test-condition', this.domElement);
- this.testDataInput = $('.t-test-data-checkbox', this.domElement);
+ 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
@@ -139,7 +139,10 @@ define([
}
self.items = [];
- $('.t-test-data-item', this.domElement).remove();
+
+ this.domElement.querySelectorAll('.t-test-data-item').forEach(item => {
+ item.remove();
+ });
this.config.forEach(function (item, index) {
const newItem = new TestDataItem(item, index, self.manager);
@@ -150,7 +153,6 @@ define([
});
self.items.forEach(function (item) {
- // $('li:last-of-type', self.itemArea).before(item.getDOM());
self.itemArea.prepend(item.getDOM());
});
diff --git a/src/plugins/summaryWidget/src/WidgetDnD.js b/src/plugins/summaryWidget/src/WidgetDnD.js
index e9ee2f040..90cd3b697 100644
--- a/src/plugins/summaryWidget/src/WidgetDnD.js
+++ b/src/plugins/summaryWidget/src/WidgetDnD.js
@@ -1,11 +1,11 @@
define([
'../res/ruleImageTemplate.html',
'EventEmitter',
- 'zepto'
+ '../../../utils/template/templateHelpers'
], function (
ruleImageTemplate,
EventEmitter,
- $
+ templateHelpers
) {
/**
@@ -19,8 +19,8 @@ define([
this.ruleOrder = ruleOrder;
this.rulesById = rulesById;
- this.imageContainer = $(ruleImageTemplate);
- this.image = $('.t-drag-rule-image', this.imageContainer);
+ this.imageContainer = templateHelpers.convertTemplateToHTML(ruleImageTemplate)[0];
+ this.image = this.imageContainer.querySelector('.t-drag-rule-image');
this.draggingId = '';
this.draggingRulePrevious = '';
this.eventEmitter = new EventEmitter();
@@ -29,18 +29,18 @@ define([
this.drag = this.drag.bind(this);
this.drop = this.drop.bind(this);
- $(this.container).on('mousemove', this.drag);
- $(document).on('mouseup', this.drop);
- $(this.container).before(this.imageContainer);
- $(this.imageContainer).hide();
+ 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).off('mousemove', this.drag);
- $(document).off('mouseup', this.drop);
+ this.container.removeEventListener('mousemove', this.drag);
+ document.removeEventListener('mouseup', this.drop);
};
/**
@@ -81,7 +81,8 @@ define([
let target = '';
ruleOrder.forEach(function (ruleId, index) {
- offset = rulesById[ruleId].getDOM().offset();
+ const ruleDOM = rulesById[ruleId].getDOM();
+ offset = window.innerWidth - (ruleDOM.offsetLeft + ruleDOM.offsetWidth);
y = offset.top;
height = offset.height;
if (index === 0) {
@@ -114,7 +115,7 @@ define([
this.imageContainer.show();
this.imageContainer.offset({
top: event.pageY - this.image.height() / 2,
- left: event.pageX - $('.t-grippy', this.image).width()
+ left: event.pageX - this.image.querySelector('.t-grippy').style.width
});
};
@@ -129,7 +130,7 @@ define([
dragTarget = this.getDropLocation(event);
this.imageContainer.offset({
top: event.pageY - this.image.height() / 2,
- left: event.pageX - $('.t-grippy', this.image).width()
+ left: event.pageX - this.image.querySelector('.t-grippy').style.width
});
if (this.rulesById[dragTarget]) {
this.rulesById[dragTarget].showDragIndicator();
diff --git a/src/plugins/summaryWidget/src/eventHelpers.js b/src/plugins/summaryWidget/src/eventHelpers.js
index d4bbac4c7..75ba9e287 100644
--- a/src/plugins/summaryWidget/src/eventHelpers.js
+++ b/src/plugins/summaryWidget/src/eventHelpers.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/summaryWidget/src/input/ColorPalette.js b/src/plugins/summaryWidget/src/input/ColorPalette.js
index 0bbe23641..2319f9830 100644
--- a/src/plugins/summaryWidget/src/input/ColorPalette.js
+++ b/src/plugins/summaryWidget/src/input/ColorPalette.js
@@ -1,10 +1,8 @@
define([
- './Palette',
- 'zepto'
+ './Palette'
],
function (
- Palette,
- $
+ Palette
) {
//The colors that will be used to instantiate this palette if none are provided
@@ -33,17 +31,16 @@ function (
this.palette.setNullOption('rgba(0,0,0,0)');
- const domElement = $(this.palette.getDOM());
+ const domElement = this.palette.getDOM();
const self = this;
- $('.c-button--menu', domElement).addClass('c-button--swatched');
- $('.t-swatch', domElement).addClass('color-swatch');
- $('.c-palette', domElement).addClass('c-palette--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');
- $('.c-palette__item', domElement).each(function () {
+ domElement.querySelectorAll('.c-palette__item').forEach(item => {
// eslint-disable-next-line no-invalid-this
- const elem = this;
- $(elem).css('background-color', elem.dataset.item);
+ item.style.backgroundColor = item.dataset.item;
});
/**
@@ -53,7 +50,7 @@ function (
*/
function updateSwatch() {
const color = self.palette.getCurrent();
- $('.color-swatch', domElement).css('background-color', color);
+ domElement.querySelector('.color-swatch').style.backgroundColor = color;
}
this.palette.on('change', updateSwatch);
diff --git a/src/plugins/summaryWidget/src/input/IconPalette.js b/src/plugins/summaryWidget/src/input/IconPalette.js
index cdc011d5d..557cc4d95 100644
--- a/src/plugins/summaryWidget/src/input/IconPalette.js
+++ b/src/plugins/summaryWidget/src/input/IconPalette.js
@@ -1,9 +1,7 @@
define([
- './Palette',
- 'zepto'
+ './Palette'
], function (
- Palette,
- $
+ Palette
) {
//The icons that will be used to instantiate this palette if none are provided
const DEFAULT_ICONS = [
@@ -45,20 +43,19 @@ define([
this.icons = icons || DEFAULT_ICONS;
this.palette = new Palette(cssClass, container, this.icons);
- this.palette.setNullOption(' ');
- this.oldIcon = this.palette.current || ' ';
+ this.palette.setNullOption('');
+ this.oldIcon = this.palette.current || '';
- const domElement = $(this.palette.getDOM());
+ const domElement = this.palette.getDOM();
const self = this;
- $('.c-button--menu', domElement).addClass('c-button--swatched');
- $('.t-swatch', domElement).addClass('icon-swatch');
- $('.c-palette', domElement).addClass('c-palette--icon');
+ 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');
- $('.c-palette-item', domElement).each(function () {
+ domElement.querySelectorAll('.c-palette-item').forEach(item => {
// eslint-disable-next-line no-invalid-this
- const elem = this;
- $(elem).addClass(elem.dataset.item);
+ item.classList.add(item.dataset.item);
});
/**
@@ -67,8 +64,11 @@ define([
* @private
*/
function updateSwatch() {
- $('.icon-swatch', domElement).removeClass(self.oldIcon)
- .addClass(self.palette.getCurrent());
+ if (self.oldIcon) {
+ domElement.querySelector('.icon-swatch').classList.remove(self.oldIcon);
+ }
+
+ domElement.querySelector('.icon-swatch').classList.add(self.palette.getCurrent());
self.oldIcon = self.palette.getCurrent();
}
diff --git a/src/plugins/summaryWidget/src/input/Palette.js b/src/plugins/summaryWidget/src/input/Palette.js
index ff1d3b550..96df813de 100644
--- a/src/plugins/summaryWidget/src/input/Palette.js
+++ b/src/plugins/summaryWidget/src/input/Palette.js
@@ -1,13 +1,13 @@
define([
'../eventHelpers',
'../../res/input/paletteTemplate.html',
- 'EventEmitter',
- 'zepto'
+ '../../../../utils/template/templateHelpers',
+ 'EventEmitter'
], function (
eventHelpers,
paletteTemplate,
- EventEmitter,
- $
+ templateHelpers,
+ EventEmitter
) {
/**
* Instantiates a new Open MCT Color Palette input
@@ -28,36 +28,41 @@ define([
this.items = items;
this.container = container;
- this.domElement = $(paletteTemplate);
+ this.domElement = templateHelpers.convertTemplateToHTML(paletteTemplate)[0];
+
this.itemElements = {
- nullOption: $('.c-palette__item-none .c-palette__item', this.domElement)
+ 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 = $('.js-button', this.domElement);
- this.menu = $('.c-menu', this.domElement);
+ this.button = this.domElement.querySelector('.js-button');
+ this.menu = this.domElement.querySelector('.c-menu');
this.hideMenu = this.hideMenu.bind(this);
- self.button.addClass(this.cssClass);
+ if (this.cssClass) {
+ self.button.classList.add(this.cssClass);
+ }
+
self.setNullOption(this.nullOption);
self.items.forEach(function (item) {
- const itemElement = $('<div class = "c-palette__item ' + item + '"'
- + ' data-item = ' + item + '></div>');
- $('.c-palette__items', self.domElement).append(itemElement);
- self.itemElements[item] = itemElement;
+ const itemElement = `<div class = "c-palette__item ${item}" data-item = "${item}"></div>`;
+ const temp = document.createElement('div');
+ temp.innerHTML = itemElement;
+ self.itemElements[item] = temp.firstChild;
+ self.domElement.querySelector('.c-palette__items').appendChild(temp.firstChild);
});
- $('.c-menu', self.domElement).hide();
+ self.domElement.querySelector('.c-menu').style.display = 'none';
- this.listenTo($(document), 'click', this.hideMenu);
- this.listenTo($('.js-button', self.domElement), 'click', function (event) {
+ this.listenTo(window.document, 'click', this.hideMenu);
+ this.listenTo(self.domElement.querySelector('.js-button'), 'click', function (event) {
event.stopPropagation();
- $('.c-menu', self.container).hide();
- $('.c-menu', self.domElement).show();
+ self.container.querySelector('.c-menu').style.display = 'none';
+ self.domElement.querySelector('.c-menu').style.display = '';
});
/**
@@ -70,10 +75,12 @@ define([
const elem = event.currentTarget;
const item = elem.dataset.item;
self.set(item);
- $('.c-menu', self.domElement).hide();
+ self.domElement.querySelector('.c-menu').style.display = 'none';
}
- this.listenTo($('.c-palette__item', self.domElement), 'click', handleItemClick);
+ self.domElement.querySelectorAll('.c-palette__item').forEach(item => {
+ this.listenTo(item, 'click', handleItemClick);
+ });
}
/**
@@ -91,7 +98,7 @@ define([
};
Palette.prototype.hideMenu = function () {
- $('.c-menu', this.domElement).hide();
+ this.domElement.querySelector('.c-menu').style.display = 'none';
};
/**
@@ -141,12 +148,16 @@ define([
* Update the view assoicated with the currently selected item
*/
Palette.prototype.updateSelected = function (item) {
- $('.c-palette__item', this.domElement).removeClass('is-selected');
- this.itemElements[item].addClass('is-selected');
+ 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') {
- $('.t-swatch', this.domElement).addClass('no-selection');
+ this.domElement.querySelector('.t-swatch').classList.add('no-selection');
} else {
- $('.t-swatch', this.domElement).removeClass('no-selection');
+ this.domElement.querySelector('.t-swatch').classList.remove('no-selection');
}
};
@@ -157,14 +168,20 @@ define([
*/
Palette.prototype.setNullOption = function (item) {
this.nullOption = item;
- this.itemElements.nullOption.data('item', 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 () {
- $('.c-palette__item-none', this.domElement).toggle();
+ 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';
+ }
};
return Palette;
diff --git a/src/plugins/summaryWidget/src/input/Select.js b/src/plugins/summaryWidget/src/input/Select.js
index 3f89034ca..676a9791b 100644
--- a/src/plugins/summaryWidget/src/input/Select.js
+++ b/src/plugins/summaryWidget/src/input/Select.js
@@ -1,13 +1,13 @@
define([
'../eventHelpers',
'../../res/input/selectTemplate.html',
- 'EventEmitter',
- 'zepto'
+ '../../../../utils/template/templateHelpers',
+ 'EventEmitter'
], function (
eventHelpers,
selectTemplate,
- EventEmitter,
- $
+ templateHelpers,
+ EventEmitter
) {
/**
@@ -20,7 +20,8 @@ define([
const self = this;
- this.domElement = $(selectTemplate);
+ this.domElement = templateHelpers.convertTemplateToHTML(selectTemplate)[0];
+
this.options = [];
this.eventEmitter = new EventEmitter();
this.supportedCallbacks = ['change'];
@@ -35,12 +36,12 @@ define([
*/
function onChange(event) {
const elem = event.target;
- const value = self.options[$(elem).prop('selectedIndex')];
+ const value = self.options[elem.selectedIndex];
self.eventEmitter.emit('change', value[0]);
}
- this.listenTo($('select', this.domElement), 'change', onChange, this);
+ this.listenTo(this.domElement.querySelector('select'), 'change', onChange, this);
}
/**
@@ -74,16 +75,19 @@ define([
const self = this;
let selectedIndex = 0;
- selectedIndex = $('select', this.domElement).prop('selectedIndex');
- $('option', this.domElement).remove();
+ 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.options.forEach(function (option, index) {
- $('select', self.domElement)
- .append('<option value = "' + option[0] + '" >'
- + option[1] + '</option>');
+ self.domElement.querySelector('select').appendChild(optionElement);
});
- $('select', this.domElement).prop('selectedIndex', selectedIndex);
+ this.domElement.querySelector('select').selectedIndex = selectedIndex;
};
/**
@@ -120,7 +124,7 @@ define([
selectedIndex = index;
}
});
- $('select', this.domElement).prop('selectedIndex', selectedIndex);
+ this.domElement.querySelector('select').selectedIndex = selectedIndex;
selectedOption = this.options[selectedIndex];
this.eventEmitter.emit('change', selectedOption[0]);
@@ -131,17 +135,21 @@ define([
* @return {string}
*/
Select.prototype.getSelected = function () {
- return $('select', this.domElement).prop('value');
+ return this.domElement.querySelector('select').value;
};
Select.prototype.hide = function () {
- $(this.domElement).addClass('hidden');
- $('.equal-to').addClass('hidden');
+ this.domElement.classList.add('hidden');
+ if (this.domElement.querySelector('.equal-to')) {
+ this.domElement.querySelector('.equal-to').classList.add('hidden');
+ }
};
Select.prototype.show = function () {
- $(this.domElement).removeClass('hidden');
- $('.equal-to').removeClass('hidden');
+ this.domElement.classList.remove('hidden');
+ if (this.domElement.querySelector('.equal-to')) {
+ this.domElement.querySelector('.equal-to').classList.remove('hidden');
+ }
};
Select.prototype.destroy = function () {
diff --git a/src/plugins/summaryWidget/src/telemetry/EvaluatorPool.js b/src/plugins/summaryWidget/src/telemetry/EvaluatorPool.js
index 973fc8c06..8b32da536 100644
--- a/src/plugins/summaryWidget/src/telemetry/EvaluatorPool.js
+++ b/src/plugins/summaryWidget/src/telemetry/EvaluatorPool.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/summaryWidget/src/telemetry/EvaluatorPoolSpec.js b/src/plugins/summaryWidget/src/telemetry/EvaluatorPoolSpec.js
index 422f6ec36..7c844e5d1 100644
--- a/src/plugins/summaryWidget/src/telemetry/EvaluatorPoolSpec.js
+++ b/src/plugins/summaryWidget/src/telemetry/EvaluatorPoolSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/summaryWidget/src/telemetry/SummaryWidgetCondition.js b/src/plugins/summaryWidget/src/telemetry/SummaryWidgetCondition.js
index ff8885048..2da92cc7b 100644
--- a/src/plugins/summaryWidget/src/telemetry/SummaryWidgetCondition.js
+++ b/src/plugins/summaryWidget/src/telemetry/SummaryWidgetCondition.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/summaryWidget/src/telemetry/SummaryWidgetConditionSpec.js b/src/plugins/summaryWidget/src/telemetry/SummaryWidgetConditionSpec.js
index 41841247c..03c2d26e1 100644
--- a/src/plugins/summaryWidget/src/telemetry/SummaryWidgetConditionSpec.js
+++ b/src/plugins/summaryWidget/src/telemetry/SummaryWidgetConditionSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/summaryWidget/src/telemetry/SummaryWidgetEvaluator.js b/src/plugins/summaryWidget/src/telemetry/SummaryWidgetEvaluator.js
index 73d11ab40..61e450fbc 100644
--- a/src/plugins/summaryWidget/src/telemetry/SummaryWidgetEvaluator.js
+++ b/src/plugins/summaryWidget/src/telemetry/SummaryWidgetEvaluator.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/summaryWidget/src/telemetry/SummaryWidgetMetadataProvider.js b/src/plugins/summaryWidget/src/telemetry/SummaryWidgetMetadataProvider.js
index b06eb2358..46d546f80 100644
--- a/src/plugins/summaryWidget/src/telemetry/SummaryWidgetMetadataProvider.js
+++ b/src/plugins/summaryWidget/src/telemetry/SummaryWidgetMetadataProvider.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/summaryWidget/src/telemetry/SummaryWidgetRule.js b/src/plugins/summaryWidget/src/telemetry/SummaryWidgetRule.js
index 548c67be1..cf9db6f4f 100644
--- a/src/plugins/summaryWidget/src/telemetry/SummaryWidgetRule.js
+++ b/src/plugins/summaryWidget/src/telemetry/SummaryWidgetRule.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/summaryWidget/src/telemetry/SummaryWidgetRuleSpec.js b/src/plugins/summaryWidget/src/telemetry/SummaryWidgetRuleSpec.js
index 2adca40cb..67f22519c 100644
--- a/src/plugins/summaryWidget/src/telemetry/SummaryWidgetRuleSpec.js
+++ b/src/plugins/summaryWidget/src/telemetry/SummaryWidgetRuleSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/summaryWidget/src/telemetry/SummaryWidgetTelemetryProvider.js b/src/plugins/summaryWidget/src/telemetry/SummaryWidgetTelemetryProvider.js
index 1067b4f6b..2fba41717 100644
--- a/src/plugins/summaryWidget/src/telemetry/SummaryWidgetTelemetryProvider.js
+++ b/src/plugins/summaryWidget/src/telemetry/SummaryWidgetTelemetryProvider.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/summaryWidget/src/telemetry/SummaryWidgetTelemetryProviderSpec.js b/src/plugins/summaryWidget/src/telemetry/SummaryWidgetTelemetryProviderSpec.js
index c51c7fce2..b762db47e 100644
--- a/src/plugins/summaryWidget/src/telemetry/SummaryWidgetTelemetryProviderSpec.js
+++ b/src/plugins/summaryWidget/src/telemetry/SummaryWidgetTelemetryProviderSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/summaryWidget/src/telemetry/operations.js b/src/plugins/summaryWidget/src/telemetry/operations.js
index fef725a9b..091d600a6 100644
--- a/src/plugins/summaryWidget/src/telemetry/operations.js
+++ b/src/plugins/summaryWidget/src/telemetry/operations.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/summaryWidget/test/ConditionManagerSpec.js b/src/plugins/summaryWidget/test/ConditionManagerSpec.js
index f6e696e94..f8de40eb9 100644
--- a/src/plugins/summaryWidget/test/ConditionManagerSpec.js
+++ b/src/plugins/summaryWidget/test/ConditionManagerSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/summaryWidget/test/ConditionSpec.js b/src/plugins/summaryWidget/test/ConditionSpec.js
index 506836334..8b166bf87 100644
--- a/src/plugins/summaryWidget/test/ConditionSpec.js
+++ b/src/plugins/summaryWidget/test/ConditionSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -20,7 +20,7 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
-define(['../src/Condition', 'zepto'], function (Condition, $) {
+define(['../src/Condition'], function (Condition) {
xdescribe('A summary widget condition', function () {
let testCondition;
let mockConfig;
@@ -33,7 +33,7 @@ define(['../src/Condition', 'zepto'], function (Condition, $) {
let generateValuesSpy;
beforeEach(function () {
- mockContainer = $(document.createElement('div'));
+ mockContainer = document.createElement('div');
mockConfig = {
object: 'object1',
@@ -78,7 +78,7 @@ define(['../src/Condition', 'zepto'], function (Condition, $) {
it('exposes a DOM element to represent itself in the view', function () {
mockContainer.append(testCondition.getDOM());
- expect($('.t-condition', mockContainer).get().length).toEqual(1);
+ expect(mockContainer.querySelectorAll('.t-condition').length).toEqual(1);
});
it('responds to a change in its object select', function () {
@@ -111,41 +111,59 @@ define(['../src/Condition', 'zepto'], function (Condition, $) {
});
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('');
- expect($('input', mockContainer).filter('[type=number]').get().length).toEqual(3);
- expect($('input', mockContainer).eq(0).prop('valueAsNumber')).toEqual(1);
- expect($('input', mockContainer).eq(1).prop('valueAsNumber')).toEqual(2);
- expect($('input', mockContainer).eq(2).prop('valueAsNumber')).toEqual(3);
+
+ 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);
mockEvaluator.getInputType.and.returnValue('text');
mockEvaluator.getInputCount.and.returnValue(2);
testCondition.config.values = ['Text I Am', 'Text It Is'];
testCondition.generateValueInputs('');
- expect($('input', mockContainer).filter('[type=text]').get().length).toEqual(2);
- expect($('input', mockContainer).eq(0).prop('value')).toEqual('Text I Am');
- expect($('input', mockContainer).eq(1).prop('value')).toEqual('Text It Is');
+
+ 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');
});
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('');
- expect($('input', mockContainer).eq(0).prop('valueAsNumber')).toEqual(0);
- expect($('input', mockContainer).eq(1).prop('valueAsNumber')).toEqual(0);
- expect($('input', mockContainer).eq(2).prop('valueAsNumber')).toEqual(0);
+
+ 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]);
mockEvaluator.getInputType.and.returnValue('text');
mockEvaluator.getInputCount.and.returnValue(2);
testCondition.config.values = [];
testCondition.generateValueInputs('');
- expect($('input', mockContainer).eq(0).prop('value')).toEqual('');
- expect($('input', mockContainer).eq(1).prop('value')).toEqual('');
+
+ inputs = Array.from(mockContainer.querySelectorAll('input'));
+
+ expect(inputs[0].value).toEqual('');
+ expect(inputs[1].value).toEqual('');
expect(testCondition.config.values).toEqual(['', '']);
});
@@ -154,8 +172,16 @@ define(['../src/Condition', 'zepto'], function (Condition, $) {
mockEvaluator.getInputType.and.returnValue('number');
mockEvaluator.getInputCount.and.returnValue(3);
testCondition.generateValueInputs('');
- $('input', mockContainer).eq(1).prop('value', 9001);
- $('input', mockContainer).eq(1).trigger('input');
+
+ 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]',
diff --git a/src/plugins/summaryWidget/test/RuleSpec.js b/src/plugins/summaryWidget/test/RuleSpec.js
index 4d6c5b714..df5108f7a 100644
--- a/src/plugins/summaryWidget/test/RuleSpec.js
+++ b/src/plugins/summaryWidget/test/RuleSpec.js
@@ -1,4 +1,4 @@
-define(['../src/Rule', 'zepto'], function (Rule, $) {
+define(['../src/Rule'], function (Rule) {
describe('A Summary Widget Rule', function () {
let mockRuleConfig;
let mockDomainObject;
@@ -78,7 +78,7 @@ define(['../src/Rule', 'zepto'], function (Rule, $) {
'dragStart'
]);
- mockContainer = $(document.createElement('div'));
+ mockContainer = document.createElement('div');
removeSpy = jasmine.createSpy('removeCallback');
duplicateSpy = jasmine.createSpy('duplicateCallback');
@@ -99,7 +99,7 @@ define(['../src/Rule', 'zepto'], function (Rule, $) {
it('gets its DOM element', function () {
mockContainer.append(testRule.getDOM());
- expect($('.l-widget-rule', mockContainer).get().length).toBeGreaterThan(0);
+ expect(mockContainer.querySelectorAll('.l-widget-rule').length).toBeGreaterThan(0);
});
it('gets its configuration properties', function () {
@@ -185,7 +185,7 @@ define(['../src/Rule', 'zepto'], function (Rule, $) {
it('builds condition view from condition configuration', function () {
mockContainer.append(testRule.getDOM());
- expect($('.t-condition', mockContainer).get().length).toEqual(2);
+ expect(mockContainer.querySelectorAll('.t-condition').length).toEqual(2);
});
it('responds to input of style properties, and updates the preview', function () {
@@ -196,9 +196,9 @@ define(['../src/Rule', 'zepto'], function (Rule, $) {
testRule.colorInputs.color.set('#999999');
expect(mockRuleConfig.style.color).toEqual('#999999');
- expect(testRule.thumbnail.css('background-color')).toEqual('rgb(67, 67, 67)');
- expect(testRule.thumbnail.css('border-color')).toEqual('rgb(102, 102, 102)');
- expect(testRule.thumbnail.css('color')).toEqual('rgb(153, 153, 153)');
+ 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();
});
@@ -228,8 +228,12 @@ define(['../src/Rule', 'zepto'], function (Rule, $) {
// });
it('allows input for when the rule triggers', function () {
- testRule.trigger.prop('value', 'all');
- testRule.trigger.trigger('change');
+ 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();
});
@@ -247,7 +251,12 @@ define(['../src/Rule', 'zepto'], function (Rule, $) {
});
it('initiates a drag event when its grippy is clicked', function () {
- testRule.grippy.trigger('mousedown');
+ const event = new Event('mousedown', {
+ bubbles: true,
+ cancelable: true
+ });
+ testRule.grippy.dispatchEvent(event);
+
expect(mockWidgetDnD.setDragImage).toHaveBeenCalled();
expect(mockWidgetDnD.dragStart).toHaveBeenCalledWith('mockRule');
});
diff --git a/src/plugins/summaryWidget/test/SummaryWidgetSpec.js b/src/plugins/summaryWidget/test/SummaryWidgetSpec.js
index e75520a20..877b1b366 100644
--- a/src/plugins/summaryWidget/test/SummaryWidgetSpec.js
+++ b/src/plugins/summaryWidget/test/SummaryWidgetSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -20,7 +20,7 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
-define(['../src/SummaryWidget', 'zepto'], function (SummaryWidget, $) {
+define(['../src/SummaryWidget'], function (SummaryWidget) {
xdescribe('The Summary Widget', function () {
let summaryWidget;
let mockDomainObject;
@@ -111,7 +111,7 @@ define(['../src/SummaryWidget', 'zepto'], function (SummaryWidget, $) {
});
it('builds rules and rule placeholders in view from configuration', function () {
- expect($('.l-widget-rule', summaryWidget.ruleArea).get().length).toEqual(2);
+ expect(summaryWidget.ruleArea.querySelectorAll('.l-widget-rule').length).toEqual(2);
});
it('allows initializing a new rule with a particular identifier', function () {
@@ -130,7 +130,7 @@ define(['../src/SummaryWidget', 'zepto'], function (SummaryWidget, $) {
mockDomainObject.configuration.ruleOrder.forEach(function (ruleId) {
expect(mockDomainObject.configuration.ruleConfigById[ruleId]).toBeDefined();
});
- expect($('.l-widget-rule', summaryWidget.ruleArea).get().length).toEqual(6);
+ expect(summaryWidget.ruleArea.querySelectorAll('.l-widget-rule').length).toEqual(6);
});
it('allows duplicating a rule from source configuration', function () {
@@ -186,10 +186,10 @@ define(['../src/SummaryWidget', 'zepto'], function (SummaryWidget, $) {
it('adds hyperlink to the widget button and sets newTab preference', function () {
summaryWidget.addHyperlink('https://www.nasa.gov', 'newTab');
- const widgetButton = $('#widget', mockContainer);
+ const widgetButton = mockContainer.querySelector('#widget');
- expect(widgetButton.attr('href')).toEqual('https://www.nasa.gov');
- expect(widgetButton.attr('target')).toEqual('_blank');
+ 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 9cfdafd60..9b48654f8 100644
--- a/src/plugins/summaryWidget/test/SummaryWidgetViewPolicySpec.js
+++ b/src/plugins/summaryWidget/test/SummaryWidgetViewPolicySpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/summaryWidget/test/TestDataItemSpec.js b/src/plugins/summaryWidget/test/TestDataItemSpec.js
index dffa6c6f2..171753efe 100644
--- a/src/plugins/summaryWidget/test/TestDataItemSpec.js
+++ b/src/plugins/summaryWidget/test/TestDataItemSpec.js
@@ -1,4 +1,4 @@
-define(['../src/TestDataItem', 'zepto'], function (TestDataItem, $) {
+define(['../src/TestDataItem'], function (TestDataItem) {
describe('A summary widget test data item', function () {
let testDataItem;
let mockConfig;
@@ -11,7 +11,7 @@ define(['../src/TestDataItem', 'zepto'], function (TestDataItem, $) {
let generateValueSpy;
beforeEach(function () {
- mockContainer = $(document.createElement('div'));
+ mockContainer = document.createElement('div');
mockConfig = {
object: 'object1',
@@ -56,7 +56,7 @@ define(['../src/TestDataItem', 'zepto'], function (TestDataItem, $) {
it('exposes a DOM element to represent itself in the view', function () {
mockContainer.append(testDataItem.getDOM());
- expect($('.t-test-data-item', mockContainer).get().length).toEqual(1);
+ expect(mockContainer.querySelectorAll('.t-test-data-item').length).toEqual(1);
});
it('responds to a change in its object select', function () {
@@ -80,34 +80,54 @@ define(['../src/TestDataItem', 'zepto'], function (TestDataItem, $) {
});
it('generates a value input of the appropriate type', function () {
+ let inputs;
+
mockContainer.append(testDataItem.getDOM());
mockEvaluator.getInputTypeById.and.returnValue('number');
testDataItem.generateValueInput('');
- expect($('input', mockContainer).filter('[type=number]').get().length).toEqual(1);
- expect($('input', mockContainer).prop('valueAsNumber')).toEqual(1);
+
+ 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);
mockEvaluator.getInputTypeById.and.returnValue('text');
testDataItem.config.value = 'Text I Am';
testDataItem.generateValueInput('');
- expect($('input', mockContainer).filter('[type=text]').get().length).toEqual(1);
- expect($('input', mockContainer).prop('value')).toEqual('Text I Am');
+
+ 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');
});
it('ensures reasonable defaults on values if none are provided', function () {
+ let inputs;
+
mockContainer.append(testDataItem.getDOM());
mockEvaluator.getInputTypeById.and.returnValue('number');
testDataItem.config.value = undefined;
testDataItem.generateValueInput('');
- expect($('input', mockContainer).filter('[type=number]').get().length).toEqual(1);
- expect($('input', mockContainer).prop('valueAsNumber')).toEqual(0);
+
+ 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);
mockEvaluator.getInputTypeById.and.returnValue('text');
testDataItem.config.value = undefined;
testDataItem.generateValueInput('');
- expect($('input', mockContainer).filter('[type=text]').get().length).toEqual(1);
- expect($('input', mockContainer).prop('value')).toEqual('');
+
+ 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('');
});
@@ -115,8 +135,15 @@ define(['../src/TestDataItem', 'zepto'], function (TestDataItem, $) {
mockContainer.append(testDataItem.getDOM());
mockEvaluator.getInputTypeById.and.returnValue('number');
testDataItem.generateValueInput('');
- $('input', mockContainer).prop('value', 9001);
- $('input', mockContainer).trigger('input');
+
+ const event = new Event('input', {
+ bubbles: true,
+ cancelable: true
+ });
+
+ mockContainer.querySelector('input').value = 9001;
+ mockContainer.querySelector('input').dispatchEvent(event);
+
expect(changeSpy).toHaveBeenCalledWith({
value: 9001,
property: 'value',
diff --git a/src/plugins/summaryWidget/test/TestDataManagerSpec.js b/src/plugins/summaryWidget/test/TestDataManagerSpec.js
index 70042250d..59ce37d92 100644
--- a/src/plugins/summaryWidget/test/TestDataManagerSpec.js
+++ b/src/plugins/summaryWidget/test/TestDataManagerSpec.js
@@ -1,4 +1,4 @@
-define(['../src/TestDataManager', 'zepto'], function (TestDataManager, $) {
+define(['../src/TestDataManager'], function (TestDataManager) {
describe('A Summary Widget Rule', function () {
let mockDomainObject;
let mockOpenMCT;
@@ -103,7 +103,7 @@ define(['../src/TestDataManager', 'zepto'], function (TestDataManager, $) {
mockConditionManager.getObjectName.and.returnValue('Object Name');
mockConditionManager.getTelemetryPropertyName.and.returnValue('Property Name');
- mockContainer = $(document.createElement('div'));
+ mockContainer = document.createElement('div');
testDataManager = new TestDataManager(mockDomainObject, mockConditionManager, mockOpenMCT);
});
@@ -114,7 +114,7 @@ define(['../src/TestDataManager', 'zepto'], function (TestDataManager, $) {
it('exposes a DOM element to represent itself in the view', function () {
mockContainer.append(testDataManager.getDOM());
- expect($('.t-widget-test-data-content', mockContainer).get().length).toBeGreaterThan(0);
+ expect(mockContainer.querySelectorAll('.t-widget-test-data-content').length).toBeGreaterThan(0);
});
it('generates a test cache in the format expected by a condition evaluator', function () {
@@ -207,7 +207,7 @@ define(['../src/TestDataManager', 'zepto'], function (TestDataManager, $) {
it('builds item view from item configuration', function () {
mockContainer.append(testDataManager.getDOM());
- expect($('.t-test-data-item', mockContainer).get().length).toEqual(3);
+ expect(mockContainer.querySelectorAll('.t-test-data-item').length).toEqual(3);
});
it('can remove a item from its configuration', function () {
diff --git a/src/plugins/tabs/components/tabs.vue b/src/plugins/tabs/components/tabs.vue
index bc1372b20..8c4edbee9 100644
--- a/src/plugins/tabs/components/tabs.vue
+++ b/src/plugins/tabs/components/tabs.vue
@@ -37,18 +37,21 @@
class="c-tabs-view__tab__label c-object-label"
:class="[tab.status ? `is-status--${tab.status}` : '']"
>
- <div class="c-object-label__type-icon"
- :class="tab.type.definition.cssClass"
+ <div
+ class="c-object-label__type-icon"
+ :class="tab.type.definition.cssClass"
>
- <span class="is-status__indicator"
- :title="`This item is ${tab.status}`"
+ <span
+ class="is-status__indicator"
+ :title="`This item is ${tab.status}`"
></span>
</div>
<span class="c-button__label c-object-label__name">{{ tab.domainObject.name }}</span>
</div>
- <button v-if="isEditing"
- class="icon-x c-click-icon c-tabs-view__tab__close-btn"
- @click="showRemoveDialog(index)"
+ <button
+ v-if="isEditing"
+ class="icon-x c-click-icon c-tabs-view__tab__close-btn"
+ @click="showRemoveDialog(index)"
></button>
</div>
</div>
@@ -363,7 +366,7 @@ export default {
}
this.currentTabIndex = tabIndex;
- this.currentTab = this.tabsList[tabIndex];
+ this.showTab(this.tabsList[tabIndex]);
},
handleWindowResize() {
if (!this.$refs.tabs || !this.$refs.tabsHolder) {
diff --git a/src/plugins/tabs/plugin.js b/src/plugins/tabs/plugin.js
index 56133eedf..dd9e8a4cc 100644
--- a/src/plugins/tabs/plugin.js
+++ b/src/plugins/tabs/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/tabs/pluginSpec.js b/src/plugins/tabs/pluginSpec.js
index f259c7f35..d20797f09 100644
--- a/src/plugins/tabs/pluginSpec.js
+++ b/src/plugins/tabs/pluginSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/tabs/tabs.js b/src/plugins/tabs/tabs.js
index 581f11006..6cd7b39a0 100644
--- a/src/plugins/tabs/tabs.js
+++ b/src/plugins/tabs/tabs.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/telemetryMean/plugin.js b/src/plugins/telemetryMean/plugin.js
index 0ed6f0a01..464104413 100755
--- a/src/plugins/telemetryMean/plugin.js
+++ b/src/plugins/telemetryMean/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/telemetryMean/src/MeanTelemetryProvider.js b/src/plugins/telemetryMean/src/MeanTelemetryProvider.js
index 0fd4df337..2637c4c83 100644
--- a/src/plugins/telemetryMean/src/MeanTelemetryProvider.js
+++ b/src/plugins/telemetryMean/src/MeanTelemetryProvider.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/telemetryMean/src/MeanTelemetryProviderSpec.js b/src/plugins/telemetryMean/src/MeanTelemetryProviderSpec.js
index 62076d970..b3377a81d 100644
--- a/src/plugins/telemetryMean/src/MeanTelemetryProviderSpec.js
+++ b/src/plugins/telemetryMean/src/MeanTelemetryProviderSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT Web, Copyright (c) 2014-2015, United States Government
+ * Open MCT Web, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/telemetryMean/src/MockTelemetryApi.js b/src/plugins/telemetryMean/src/MockTelemetryApi.js
index 52b39be37..a1fec846b 100644
--- a/src/plugins/telemetryMean/src/MockTelemetryApi.js
+++ b/src/plugins/telemetryMean/src/MockTelemetryApi.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/telemetryMean/src/TelemetryAverager.js b/src/plugins/telemetryMean/src/TelemetryAverager.js
index 2a7ce1788..51756c0ea 100644
--- a/src/plugins/telemetryMean/src/TelemetryAverager.js
+++ b/src/plugins/telemetryMean/src/TelemetryAverager.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/telemetryTable/TableConfigurationViewProvider.js b/src/plugins/telemetryTable/TableConfigurationViewProvider.js
index 2ca73e8cb..85dcd59ff 100644
--- a/src/plugins/telemetryTable/TableConfigurationViewProvider.js
+++ b/src/plugins/telemetryTable/TableConfigurationViewProvider.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/telemetryTable/TelemetryTable.js b/src/plugins/telemetryTable/TelemetryTable.js
index 99824b463..33b2d82ab 100644
--- a/src/plugins/telemetryTable/TelemetryTable.js
+++ b/src/plugins/telemetryTable/TelemetryTable.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/telemetryTable/TelemetryTableColumn.js b/src/plugins/telemetryTable/TelemetryTableColumn.js
index 0c777ab13..ab63449da 100644
--- a/src/plugins/telemetryTable/TelemetryTableColumn.js
+++ b/src/plugins/telemetryTable/TelemetryTableColumn.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/telemetryTable/TelemetryTableConfiguration.js b/src/plugins/telemetryTable/TelemetryTableConfiguration.js
index 8e5062669..b21d49d33 100644
--- a/src/plugins/telemetryTable/TelemetryTableConfiguration.js
+++ b/src/plugins/telemetryTable/TelemetryTableConfiguration.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/telemetryTable/TelemetryTableNameColumn.js b/src/plugins/telemetryTable/TelemetryTableNameColumn.js
index 9b37d5fbe..32cb389e7 100644
--- a/src/plugins/telemetryTable/TelemetryTableNameColumn.js
+++ b/src/plugins/telemetryTable/TelemetryTableNameColumn.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2018, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/telemetryTable/TelemetryTableRow.js b/src/plugins/telemetryTable/TelemetryTableRow.js
index 66692912e..0717dccc2 100644
--- a/src/plugins/telemetryTable/TelemetryTableRow.js
+++ b/src/plugins/telemetryTable/TelemetryTableRow.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/telemetryTable/TelemetryTableType.js b/src/plugins/telemetryTable/TelemetryTableType.js
index 17587aedc..3d610a9ae 100644
--- a/src/plugins/telemetryTable/TelemetryTableType.js
+++ b/src/plugins/telemetryTable/TelemetryTableType.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/telemetryTable/TelemetryTableUnitColumn.js b/src/plugins/telemetryTable/TelemetryTableUnitColumn.js
index ffd269e04..b41375363 100644
--- a/src/plugins/telemetryTable/TelemetryTableUnitColumn.js
+++ b/src/plugins/telemetryTable/TelemetryTableUnitColumn.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2018, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/telemetryTable/TelemetryTableViewProvider.js b/src/plugins/telemetryTable/TelemetryTableViewProvider.js
index 72478f095..dd4dc0ad2 100644
--- a/src/plugins/telemetryTable/TelemetryTableViewProvider.js
+++ b/src/plugins/telemetryTable/TelemetryTableViewProvider.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/telemetryTable/ViewActions.js b/src/plugins/telemetryTable/ViewActions.js
index 7881e8dfe..04f204006 100644
--- a/src/plugins/telemetryTable/ViewActions.js
+++ b/src/plugins/telemetryTable/ViewActions.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/telemetryTable/collections/TableRowCollection.js b/src/plugins/telemetryTable/collections/TableRowCollection.js
index e3a66589d..c0dd2d911 100644
--- a/src/plugins/telemetryTable/collections/TableRowCollection.js
+++ b/src/plugins/telemetryTable/collections/TableRowCollection.js
@@ -127,14 +127,14 @@ define(
let j = 0;
while (i < this.rows.length && j < rows.length) {
- const iRow = this.rows[i];
- const jRow = rows[j];
+ const existingRow = this.rows[i];
+ const incomingRow = rows[j];
- if (this.firstRowInSortOrder(iRow, jRow) === iRow) {
- mergedRows.push(iRow);
+ if (this.firstRowInSortOrder(existingRow, incomingRow) === existingRow) {
+ mergedRows.push(existingRow);
i++;
} else {
- mergedRows.push(jRow);
+ mergedRows.push(incomingRow);
j++;
}
}
diff --git a/src/plugins/telemetryTable/components/table-cell.vue b/src/plugins/telemetryTable/components/table-cell.vue
index 46a27d4ff..a1882937f 100644
--- a/src/plugins/telemetryTable/components/table-cell.vue
+++ b/src/plugins/telemetryTable/components/table-cell.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/telemetryTable/components/table-column-header.vue b/src/plugins/telemetryTable/components/table-column-header.vue
index 394258a26..cb501252b 100644
--- a/src/plugins/telemetryTable/components/table-column-header.vue
+++ b/src/plugins/telemetryTable/components/table-column-header.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/telemetryTable/components/table-row.vue b/src/plugins/telemetryTable/components/table-row.vue
index d9cec58ff..b172b4d46 100644
--- a/src/plugins/telemetryTable/components/table-row.vue
+++ b/src/plugins/telemetryTable/components/table-row.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/telemetryTable/components/table.scss b/src/plugins/telemetryTable/components/table.scss
index 512af8c3a..03d54c0f7 100644
--- a/src/plugins/telemetryTable/components/table.scss
+++ b/src/plugins/telemetryTable/components/table.scss
@@ -63,8 +63,9 @@
padding-top: 0;
padding-bottom: 0;
}
- .is-in-small-container & {
- display: none;
+
+ .--width-less-than-600 & {
+ display: none !important;
}
}
}
diff --git a/src/plugins/telemetryTable/components/table.vue b/src/plugins/telemetryTable/components/table.vue
index 27893f3ad..e806d8c1f 100644
--- a/src/plugins/telemetryTable/components/table.vue
+++ b/src/plugins/telemetryTable/components/table.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -20,11 +20,13 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
<template>
-<div class="c-table-wrapper"
- :class="{ 'is-paused': paused }"
+<div
+ class="c-table-wrapper"
+ :class="{ 'is-paused': paused }"
>
- <div v-if="enableLegacyToolbar"
- class="c-table-control-bar c-control-bar"
+ <div
+ v-if="enableLegacyToolbar"
+ class="c-table-control-bar c-control-bar"
>
<button
v-if="allowExport"
@@ -95,8 +97,9 @@
</div>
<!-- alternate controlbar start -->
- <div v-if="marking.useAlternateControlBar"
- class="c-table-control-bar c-control-bar"
+ <div
+ v-if="marking.useAlternateControlBar"
+ class="c-table-control-bar c-control-bar"
>
<div class="c-control-bar__label">
{{ markedRows.length > 1 ? `${markedRows.length} ${marking.rowNamePlural} selected`: `${markedRows.length} ${marking.rowName} selected` }}
@@ -141,7 +144,7 @@
<progress-bar
v-if="loading"
class="c-telemetry-table__progress-bar"
- :model="progressLoad"
+ :model="{progressPerc: undefined}"
/>
<!-- Headers table -->
@@ -382,11 +385,6 @@ export default {
};
},
computed: {
- progressLoad() {
- return {
- progressPerc: undefined
- };
- },
dropTargetStyle() {
return {
top: this.$refs.headersTable.offsetTop + 'px',
@@ -496,6 +494,8 @@ export default {
this.table.tableRows.on('sort', this.updateVisibleRows);
this.table.tableRows.on('filter', this.updateVisibleRows);
+ this.openmct.time.on('bounds', this.boundsChanged);
+
//Default sort
this.sortOptions = this.table.tableRows.sortBy();
this.scrollable = this.$el.querySelector('.js-telemetry-table__body-w');
@@ -510,7 +510,7 @@ export default {
this.table.initialize();
},
- destroyed() {
+ beforeDestroy() {
this.table.off('object-added', this.addObject);
this.table.off('object-removed', this.removeObject);
this.table.off('historical-rows-processed', this.checkForMarkedRows);
@@ -524,6 +524,8 @@ export default {
this.table.configuration.off('change', this.updateConfiguration);
+ this.openmct.time.off('bounds', this.boundsChanged);
+
clearInterval(this.resizePollHandle);
this.table.configuration.destroy();
@@ -817,16 +819,16 @@ export default {
this.visibleRows = [];
this.$nextTick().then(this.updateVisibleRows);
},
- pause(pausedByButton) {
- if (pausedByButton) {
+ pause(byButton) {
+ if (byButton) {
this.pausedByButton = true;
}
this.paused = true;
this.table.pause();
},
- unpause(unpausedByButton) {
- if (unpausedByButton) {
+ unpause(byButtonOrUserBoundsChange) {
+ if (byButtonOrUserBoundsChange) {
this.undoMarkedRows();
this.table.unpause();
this.paused = false;
@@ -841,6 +843,16 @@ export default {
this.isShowingMarkedRowsOnly = false;
},
+ boundsChanged(_bounds, isTick) {
+ if (isTick) {
+ return;
+ }
+
+ // User bounds change.
+ if (this.paused) {
+ this.unpause(true);
+ }
+ },
togglePauseByButton() {
if (this.paused) {
this.unpause(true);
@@ -848,7 +860,7 @@ export default {
this.pause(true);
}
},
- undoMarkedRows(unpause) {
+ undoMarkedRows() {
this.markedRows.forEach(r => r.marked = false);
this.markedRows = [];
},
diff --git a/src/plugins/telemetryTable/plugin.js b/src/plugins/telemetryTable/plugin.js
index 1a4560d9e..80a3e3ec0 100644
--- a/src/plugins/telemetryTable/plugin.js
+++ b/src/plugins/telemetryTable/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/telemetryTable/pluginSpec.js b/src/plugins/telemetryTable/pluginSpec.js
index 6eef2d541..19cdd7e9d 100644
--- a/src/plugins/telemetryTable/pluginSpec.js
+++ b/src/plugins/telemetryTable/pluginSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -133,13 +133,28 @@ describe("the plugin", () => {
let tableViewProvider;
let tableView;
let tableInstance;
+ let mockClock;
- beforeEach(() => {
+ beforeEach(async () => {
openmct.time.timeSystem('utc', {
start: 0,
end: 4
});
+ mockClock = jasmine.createSpyObj("clock", [
+ "on",
+ "off",
+ "currentValue"
+ ]);
+ mockClock.key = 'mockClock';
+ mockClock.currentValue.and.returnValue(1);
+
+ openmct.time.addClock(mockClock);
+ openmct.time.clock('mockClock', {
+ start: 0,
+ end: 4
+ });
+
testTelemetryObject = {
identifier: {
namespace: "",
@@ -195,16 +210,8 @@ describe("the plugin", () => {
'some-other-key': 'some-other-value 3'
}
];
- let telemetryPromiseResolve;
- let telemetryPromise = new Promise((resolve) => {
- telemetryPromiseResolve = resolve;
- });
-
- historicalProvider.request = () => {
- telemetryPromiseResolve(testTelemetry);
- return telemetryPromise;
- };
+ historicalProvider.request = () => Promise.resolve(testTelemetry);
openmct.router.path = [testTelemetryObject];
@@ -215,7 +222,7 @@ describe("the plugin", () => {
tableInstance = tableView.getTable();
- return telemetryPromise.then(() => Vue.nextTick());
+ await Vue.nextTick();
});
afterEach(() => {
@@ -240,13 +247,10 @@ describe("the plugin", () => {
});
- it("Renders a row for every telemetry datum returned", (done) => {
+ it("Renders a row for every telemetry datum returned", async () => {
let rows = element.querySelectorAll('table.c-telemetry-table__body tr');
- Vue.nextTick(() => {
- expect(rows.length).toBe(3);
-
- done();
- });
+ await Vue.nextTick();
+ expect(rows.length).toBe(3);
});
it("Renders a column for every item in telemetry metadata", () => {
@@ -258,7 +262,7 @@ describe("the plugin", () => {
expect(headers[3].innerText).toBe('Another attribute');
});
- it("Supports column reordering via drag and drop", () => {
+ it("Supports column reordering via drag and drop", async () => {
let columns = element.querySelectorAll('tr.c-telemetry-table__headers__labels th');
let fromColumn = columns[0];
let toColumn = columns[1];
@@ -277,54 +281,43 @@ describe("the plugin", () => {
toColumn.dispatchEvent(dragOverEvent);
toColumn.dispatchEvent(dropEvent);
- return Vue.nextTick().then(() => {
- columns = element.querySelectorAll('tr.c-telemetry-table__headers__labels th');
- let firstColumn = columns[0];
- let secondColumn = columns[1];
- let firstColumnText = firstColumn.querySelector('span.c-telemetry-table__headers__label').innerText;
- let secondColumnText = secondColumn.querySelector('span.c-telemetry-table__headers__label').innerText;
-
- expect(fromColumnText).not.toEqual(firstColumnText);
- expect(fromColumnText).toEqual(secondColumnText);
- expect(toColumnText).not.toEqual(secondColumnText);
- expect(toColumnText).toEqual(firstColumnText);
- });
+ await Vue.nextTick();
+ columns = element.querySelectorAll('tr.c-telemetry-table__headers__labels th');
+ let firstColumn = columns[0];
+ let secondColumn = columns[1];
+ let firstColumnText = firstColumn.querySelector('span.c-telemetry-table__headers__label').innerText;
+ let secondColumnText = secondColumn.querySelector('span.c-telemetry-table__headers__label').innerText;
+ expect(fromColumnText).not.toEqual(firstColumnText);
+ expect(fromColumnText).toEqual(secondColumnText);
+ expect(toColumnText).not.toEqual(secondColumnText);
+ expect(toColumnText).toEqual(firstColumnText);
});
- it("Supports filtering telemetry by regular text search", () => {
+ it("Supports filtering telemetry by regular text search", async () => {
tableInstance.tableRows.setColumnFilter("some-key", "1");
+ await Vue.nextTick();
+ let filteredRowElements = element.querySelectorAll('table.c-telemetry-table__body tr');
- return Vue.nextTick().then(() => {
- let filteredRowElements = element.querySelectorAll('table.c-telemetry-table__body tr');
-
- expect(filteredRowElements.length).toEqual(1);
-
- tableInstance.tableRows.setColumnFilter("some-key", "");
-
- return Vue.nextTick().then(() => {
- let allRowElements = element.querySelectorAll('table.c-telemetry-table__body tr');
+ expect(filteredRowElements.length).toEqual(1);
+ tableInstance.tableRows.setColumnFilter("some-key", "");
+ await Vue.nextTick();
- expect(allRowElements.length).toEqual(3);
- });
- });
+ let allRowElements = element.querySelectorAll('table.c-telemetry-table__body tr');
+ expect(allRowElements.length).toEqual(3);
});
- it("Supports filtering using Regex", () => {
+ it("Supports filtering using Regex", async () => {
tableInstance.tableRows.setColumnRegexFilter("some-key", "^some-value$");
+ await Vue.nextTick();
+ let filteredRowElements = element.querySelectorAll('table.c-telemetry-table__body tr');
- return Vue.nextTick().then(() => {
- let filteredRowElements = element.querySelectorAll('table.c-telemetry-table__body tr');
-
- expect(filteredRowElements.length).toEqual(0);
-
- tableInstance.tableRows.setColumnRegexFilter("some-key", "^some-value");
+ expect(filteredRowElements.length).toEqual(0);
- return Vue.nextTick().then(() => {
- let allRowElements = element.querySelectorAll('table.c-telemetry-table__body tr');
+ tableInstance.tableRows.setColumnRegexFilter("some-key", "^some-value");
+ await Vue.nextTick();
+ let allRowElements = element.querySelectorAll('table.c-telemetry-table__body tr');
- expect(allRowElements.length).toEqual(3);
- });
- });
+ expect(allRowElements.length).toEqual(3);
});
it("displays the correct number of column headers when the configuration is mutated", async () => {
@@ -360,5 +353,101 @@ describe("the plugin", () => {
tableRowCells = element.querySelectorAll('table.c-telemetry-table__body > tbody > tr:first-child td');
expect(tableRowCells.length).toEqual(4);
});
+
+ it("Pauses the table when a row is marked", async () => {
+ let firstRow = element.querySelector('table.c-telemetry-table__body > tbody > tr');
+ let clickEvent = createMouseEvent('click');
+
+ // Mark a row
+ firstRow.dispatchEvent(clickEvent);
+
+ await Vue.nextTick();
+
+ // Verify table is paused
+ expect(element.querySelector('div.c-table.is-paused')).not.toBeNull();
+ });
+
+ it("Unpauses the table on user bounds change", async () => {
+ let firstRow = element.querySelector('table.c-telemetry-table__body > tbody > tr');
+ let clickEvent = createMouseEvent('click');
+
+ // Mark a row
+ firstRow.dispatchEvent(clickEvent);
+
+ await Vue.nextTick();
+
+ // Verify table is paused
+ expect(element.querySelector('div.c-table.is-paused')).not.toBeNull();
+
+ const currentBounds = openmct.time.bounds();
+ await Vue.nextTick();
+ const newBounds = {
+ start: currentBounds.start,
+ end: currentBounds.end - 3
+ };
+
+ // Manually change the time bounds
+ openmct.time.bounds(newBounds);
+ await Vue.nextTick();
+
+ // Verify table is no longer paused
+ expect(element.querySelector('div.c-table.is-paused')).toBeNull();
+ });
+
+ it("Unpauses the table on user bounds change if paused by button", async () => {
+ const viewContext = tableView.getViewContext();
+
+ // Pause by button
+ viewContext.togglePauseByButton();
+ await Vue.nextTick();
+
+ // Verify table is paused
+ expect(element.querySelector('div.c-table.is-paused')).not.toBeNull();
+
+ const currentBounds = openmct.time.bounds();
+ await Vue.nextTick();
+
+ const newBounds = {
+ start: currentBounds.start,
+ end: currentBounds.end - 1
+ };
+ // Manually change the time bounds
+ openmct.time.bounds(newBounds);
+
+ await Vue.nextTick();
+
+ // Verify table is no longer paused
+ expect(element.querySelector('div.c-table.is-paused')).toBeNull();
+ });
+
+ it("Does not unpause the table on tick", async () => {
+ const viewContext = tableView.getViewContext();
+
+ // Pause by button
+ viewContext.togglePauseByButton();
+
+ await Vue.nextTick();
+
+ // Verify table displays the correct number of rows
+ let tableRows = element.querySelectorAll('table.c-telemetry-table__body > tbody > tr');
+ expect(tableRows.length).toEqual(3);
+
+ // Verify table is paused
+ expect(element.querySelector('div.c-table.is-paused')).not.toBeNull();
+
+ // Tick the clock
+ openmct.time.tick(1);
+
+ await Vue.nextTick();
+
+ // Verify table is still paused
+ expect(element.querySelector('div.c-table.is-paused')).not.toBeNull();
+
+ await Vue.nextTick();
+
+ // Verify table displays the correct number of rows
+ tableRows = element.querySelectorAll('table.c-telemetry-table__body > tbody > tr');
+ expect(tableRows.length).toEqual(3);
+ });
});
});
diff --git a/src/plugins/timeConductor/Conductor.vue b/src/plugins/timeConductor/Conductor.vue
index e6066e82f..96e316b92 100644
--- a/src/plugins/timeConductor/Conductor.vue
+++ b/src/plugins/timeConductor/Conductor.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT Web, Copyright (c) 2014-2021, United States Government
+ * Open MCT Web, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -30,11 +30,15 @@
]"
>
<div class="c-conductor__time-bounds">
- <conductor-inputs-fixed v-if="isFixed"
- @updated="saveFixedOffsets"
+ <conductor-inputs-fixed
+ v-if="isFixed"
+ :input-bounds="viewBounds"
+ @updated="saveFixedOffsets"
/>
- <conductor-inputs-realtime v-else
- @updated="saveClockOffsets"
+ <conductor-inputs-realtime
+ v-else
+ :input-bounds="viewBounds"
+ @updated="saveClockOffsets"
/>
<ConductorModeIcon class="c-conductor__mode-icon" />
<conductor-axis
diff --git a/src/plugins/timeConductor/ConductorAxis.vue b/src/plugins/timeConductor/ConductorAxis.vue
index 5c84020ce..4b1dac2ad 100644
--- a/src/plugins/timeConductor/ConductorAxis.vue
+++ b/src/plugins/timeConductor/ConductorAxis.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT Web, Copyright (c) 2014-2021, United States Government
+ * Open MCT Web, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/timeConductor/ConductorHistory.vue b/src/plugins/timeConductor/ConductorHistory.vue
index 7b010b0b5..a91accfa2 100644
--- a/src/plugins/timeConductor/ConductorHistory.vue
+++ b/src/plugins/timeConductor/ConductorHistory.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT Web, Copyright (c) 2014-2021, United States Government
+ * Open MCT Web, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -20,8 +20,9 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
<template>
-<div ref="historyButton"
- class="c-ctrl-wrapper c-ctrl-wrapper--menus-up"
+<div
+ ref="historyButton"
+ class="c-ctrl-wrapper c-ctrl-wrapper--menus-up"
>
<div class="c-menu-button c-ctrl-wrapper c-ctrl-wrapper--menus-left">
<button
diff --git a/src/plugins/timeConductor/ConductorInputsFixed.vue b/src/plugins/timeConductor/ConductorInputsFixed.vue
index e98ded7b3..106e4cee8 100644
--- a/src/plugins/timeConductor/ConductorInputsFixed.vue
+++ b/src/plugins/timeConductor/ConductorInputsFixed.vue
@@ -1,6 +1,7 @@
<template>
-<form ref="fixedDeltaInput"
- class="c-conductor__inputs"
+<form
+ ref="fixedDeltaInput"
+ class="c-conductor__inputs"
>
<div
class="c-ctrl-wrapper c-conductor-input c-conductor__start-fixed"
@@ -71,6 +72,12 @@ export default {
default() {
return undefined;
}
+ },
+ inputBounds: {
+ type: Object,
+ default() {
+ return undefined;
+ }
}
},
data() {
@@ -95,6 +102,17 @@ export default {
isUTCBased: timeSystem.isUTCBased
};
},
+ watch: {
+ keyString() {
+ this.setTimeContext();
+ },
+ inputBounds: {
+ handler(newBounds) {
+ this.handleNewBounds(newBounds);
+ },
+ deep: true
+ }
+ },
mounted() {
this.handleNewBounds = _.throttle(this.handleNewBounds, 300);
this.setTimeSystem(JSON.parse(JSON.stringify(this.openmct.time.timeSystem())));
@@ -248,6 +266,8 @@ export default {
input.title = '';
}
+ this.$refs.fixedDeltaInput.reportValidity();
+
return validationResult.valid;
},
startDateSelected(date) {
diff --git a/src/plugins/timeConductor/ConductorInputsRealtime.vue b/src/plugins/timeConductor/ConductorInputsRealtime.vue
index 58ec8d443..c6aee38ee 100644
--- a/src/plugins/timeConductor/ConductorInputsRealtime.vue
+++ b/src/plugins/timeConductor/ConductorInputsRealtime.vue
@@ -1,6 +1,7 @@
<template>
-<form ref="deltaInput"
- class="c-conductor__inputs"
+<form
+ ref="deltaInput"
+ class="c-conductor__inputs"
>
<div
class="c-ctrl-wrapper c-conductor-input c-conductor__start-delta"
@@ -21,7 +22,8 @@
ref="startOffset"
class="c-button c-conductor__delta-button"
title="Set the time offset after now"
- @click.prevent="showTimePopupStart"
+ data-testid="conductor-start-offset-button"
+ @click.prevent.stop="showTimePopupStart"
>
{{ offsets.start }}
</button>
@@ -60,7 +62,8 @@
ref="endOffset"
class="c-button c-conductor__delta-button"
title="Set the time offset preceding now"
- @click.prevent="showTimePopupEnd"
+ data-testid="conductor-end-offset-button"
+ @click.prevent.stop="showTimePopupEnd"
>
{{ offsets.end }}
</button>
@@ -86,6 +89,12 @@ export default {
default() {
return undefined;
}
+ },
+ inputBounds: {
+ type: Object,
+ default() {
+ return undefined;
+ }
}
},
data() {
@@ -115,6 +124,17 @@ export default {
isUTCBased: timeSystem.isUTCBased
};
},
+ watch: {
+ keyString() {
+ this.setTimeContext();
+ },
+ inputBounds: {
+ handler(newBounds) {
+ this.handleNewBounds(newBounds);
+ },
+ deep: true
+ }
+ },
mounted() {
this.handleNewBounds = _.throttle(this.handleNewBounds, 300);
this.setTimeSystem(JSON.parse(JSON.stringify(this.openmct.time.timeSystem())));
diff --git a/src/plugins/timeConductor/ConductorMode.vue b/src/plugins/timeConductor/ConductorMode.vue
index 54bb9bcd8..fbe4fa2f7 100644
--- a/src/plugins/timeConductor/ConductorMode.vue
+++ b/src/plugins/timeConductor/ConductorMode.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT Web, Copyright (c) 2014-2021, United States Government
+ * Open MCT Web, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -20,8 +20,9 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
<template>
-<div ref="modeButton"
- class="c-ctrl-wrapper c-ctrl-wrapper--menus-up"
+<div
+ ref="modeButton"
+ class="c-ctrl-wrapper c-ctrl-wrapper--menus-up"
>
<div class="c-menu-button c-ctrl-wrapper c-ctrl-wrapper--menus-left">
<button
@@ -104,6 +105,7 @@ export default {
name: 'Fixed Timespan',
description: 'Query and explore data that falls between two fixed datetimes.',
cssClass: 'icon-tabular',
+ testId: 'conductor-modeOption-fixed',
onItemClicked: () => this.setOption(key)
};
} else {
@@ -115,6 +117,7 @@ export default {
description: "Monitor streaming data in real-time. The Time "
+ "Conductor and displays will automatically advance themselves based on this clock. " + clock.description,
cssClass: clock.cssClass || 'icon-clock',
+ testId: 'conductor-modeOption-realtime',
onItemClicked: () => this.setOption(key)
};
}
@@ -147,7 +150,8 @@ export default {
if (clockKey === undefined) {
this.openmct.time.stopClock();
} else {
- this.openmct.time.clock(clockKey, configuration.clockOffsets);
+ const offsets = this.openmct.time.clockOffsets() || configuration.clockOffsets;
+ this.openmct.time.clock(clockKey, offsets);
}
},
diff --git a/src/plugins/timeConductor/ConductorModeIcon.vue b/src/plugins/timeConductor/ConductorModeIcon.vue
index 25ff47294..b58e72e6a 100644
--- a/src/plugins/timeConductor/ConductorModeIcon.vue
+++ b/src/plugins/timeConductor/ConductorModeIcon.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
-* Open MCT Web, Copyright (c) 2014-2021, United States Government
+* Open MCT Web, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/timeConductor/ConductorTimeSystem.vue b/src/plugins/timeConductor/ConductorTimeSystem.vue
index 55197834a..d37b206ee 100644
--- a/src/plugins/timeConductor/ConductorTimeSystem.vue
+++ b/src/plugins/timeConductor/ConductorTimeSystem.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT Web, Copyright (c) 2014-2021, United States Government
+ * Open MCT Web, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -20,9 +20,10 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
<template>
-<div v-if="selectedTimeSystem.name"
- ref="timeSystemButton"
- class="c-ctrl-wrapper c-ctrl-wrapper--menus-up"
+<div
+ v-if="selectedTimeSystem.name"
+ ref="timeSystemButton"
+ class="c-ctrl-wrapper c-ctrl-wrapper--menus-up"
>
<button
class="c-button--menu c-time-system-button"
diff --git a/src/plugins/timeConductor/DatePicker.vue b/src/plugins/timeConductor/DatePicker.vue
index 917612441..578923b29 100644
--- a/src/plugins/timeConductor/DatePicker.vue
+++ b/src/plugins/timeConductor/DatePicker.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT Web, Copyright (c) 2014-2021, United States Government
+ * Open MCT Web, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/timeConductor/conductor.scss b/src/plugins/timeConductor/conductor.scss
index 453a7b2ac..7eb6eec8e 100644
--- a/src/plugins/timeConductor/conductor.scss
+++ b/src/plugins/timeConductor/conductor.scss
@@ -185,6 +185,11 @@
&__inputs,
&__time-bounds {
display: flex;
+
+ .c-toggle-switch {
+ // Used in independent Time Conductor
+ flex: 0 0 auto;
+ }
}
&__inputs {
diff --git a/src/plugins/timeConductor/date-picker.scss b/src/plugins/timeConductor/date-picker.scss
index 9ce416ca4..3c447e974 100644
--- a/src/plugins/timeConductor/date-picker.scss
+++ b/src/plugins/timeConductor/date-picker.scss
@@ -69,7 +69,8 @@
}
&.selected {
- background: #1ac6ff; // this should be a variable... CHARLESSSSSS
+ background: $colorKey;
+ color: $colorKeyFg;
}
}
diff --git a/src/plugins/timeConductor/independent/IndependentTimeConductor.vue b/src/plugins/timeConductor/independent/IndependentTimeConductor.vue
index 8af9acd65..c64b0c38d 100644
--- a/src/plugins/timeConductor/independent/IndependentTimeConductor.vue
+++ b/src/plugins/timeConductor/independent/IndependentTimeConductor.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT Web, Copyright (c) 2014-2021, United States Government
+ * Open MCT Web, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -36,25 +36,29 @@
<ConductorModeIcon />
- <div v-if="timeOptions && independentTCEnabled"
- class="c-conductor__controls"
+ <div
+ v-if="timeOptions && independentTCEnabled"
+ class="c-conductor__controls"
>
- <Mode v-if="mode"
- class="c-conductor__mode-select"
- :key-string="domainObject.identifier.key"
- :mode="timeOptions.mode"
- :enabled="independentTCEnabled"
- @modeChanged="saveMode"
+ <Mode
+ v-if="mode"
+ class="c-conductor__mode-select"
+ :key-string="domainObject.identifier.key"
+ :mode="timeOptions.mode"
+ :enabled="independentTCEnabled"
+ @modeChanged="saveMode"
/>
- <conductor-inputs-fixed v-if="isFixed"
- :key-string="domainObject.identifier.key"
- @updated="saveFixedOffsets"
+ <conductor-inputs-fixed
+ v-if="isFixed"
+ :key-string="domainObject.identifier.key"
+ @updated="saveFixedOffsets"
/>
- <conductor-inputs-realtime v-else
- :key-string="domainObject.identifier.key"
- @updated="saveClockOffsets"
+ <conductor-inputs-realtime
+ v-else
+ :key-string="domainObject.identifier.key"
+ @updated="saveClockOffsets"
/>
</div>
</div>
@@ -105,38 +109,49 @@ export default {
watch: {
domainObject: {
handler(domainObject) {
- if (!domainObject.configuration.timeOptions || !this.independentTCEnabled) {
- return;
- }
-
- if (this.timeOptions.start !== domainObject.configuration.timeOptions.start
- || this.timeOptions.end !== domainObject.configuration.timeOptions.end) {
- this.timeOptions = domainObject.configuration.timeOptions;
+ const key = this.openmct.objects.makeKeyString(domainObject.identifier);
+ if (key !== this.keyString) {
+ //domain object has changed
this.destroyIndependentTime();
- this.registerIndependentTimeOffsets();
+
+ this.independentTCEnabled = domainObject.configuration.useIndependentTime === true;
+ this.timeOptions = domainObject.configuration.timeOptions || {
+ clockOffsets: this.openmct.time.clockOffsets(),
+ fixedOffsets: this.openmct.time.bounds()
+ };
+
+ this.initialize();
}
},
deep: true
}
},
mounted() {
- this.setTimeContext();
-
- if (this.timeOptions.mode) {
- this.mode = this.timeOptions.mode;
- } else {
- this.timeOptions.mode = this.mode = this.timeContext.clock() === undefined ? { key: 'fixed' } : { key: Object.create(this.timeContext.clock()).key};
- }
-
- if (this.independentTCEnabled) {
- this.registerIndependentTimeOffsets();
- }
+ this.initialize();
},
beforeDestroy() {
this.stopFollowingTimeContext();
this.destroyIndependentTime();
},
methods: {
+ initialize() {
+ this.keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
+ this.setTimeContext();
+
+ if (this.timeOptions.mode) {
+ this.mode = this.timeOptions.mode;
+ } else {
+ if (this.timeContext.clock() === undefined) {
+ this.timeOptions.mode = this.mode = { key: 'fixed' };
+ } else {
+ this.timeOptions.mode = this.mode = { key: Object.create(this.timeContext.clock()).key};
+ }
+ }
+
+ if (this.independentTCEnabled) {
+ this.registerIndependentTimeOffsets();
+ }
+ },
toggleIndependentTC() {
this.independentTCEnabled = !this.independentTCEnabled;
if (this.independentTCEnabled) {
@@ -209,10 +224,9 @@ export default {
offsets = this.timeOptions.clockOffsets;
}
- const key = this.openmct.objects.makeKeyString(this.domainObject.identifier);
- const timeContext = this.openmct.time.getIndependentContext(key);
+ const timeContext = this.openmct.time.getIndependentContext(this.keyString);
if (!timeContext.hasOwnContext()) {
- this.unregisterIndependentTime = this.openmct.time.addIndependentContext(key, offsets, this.isFixed ? undefined : this.mode.key);
+ this.unregisterIndependentTime = this.openmct.time.addIndependentContext(this.keyString, offsets, this.isFixed ? undefined : this.mode.key);
} else {
if (this.isFixed) {
timeContext.stopClock();
diff --git a/src/plugins/timeConductor/independent/Mode.vue b/src/plugins/timeConductor/independent/Mode.vue
index a7712756b..08c498538 100644
--- a/src/plugins/timeConductor/independent/Mode.vue
+++ b/src/plugins/timeConductor/independent/Mode.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
-* Open MCT Web, Copyright (c) 2014-2021, United States Government
+* Open MCT Web, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -20,14 +20,16 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
<template>
-<div v-if="modes.length > 1"
- ref="modeMenuButton"
- class="c-ctrl-wrapper c-ctrl-wrapper--menus-up"
+<div
+ v-if="modes.length > 1"
+ ref="modeMenuButton"
+ class="c-ctrl-wrapper c-ctrl-wrapper--menus-up"
>
<div class="c-menu-button c-ctrl-wrapper c-ctrl-wrapper--menus-left">
- <button v-if="selectedMode"
- class="c-button--menu c-mode-button"
- @click.prevent.stop="showModesMenu"
+ <button
+ v-if="selectedMode"
+ class="c-button--menu c-mode-button"
+ @click.prevent.stop="showModesMenu"
>
<span class="c-button__label">{{ selectedMode.name }}</span>
</button>
diff --git a/src/plugins/timeConductor/plugin.js b/src/plugins/timeConductor/plugin.js
index 31dcaeed9..4e8c8de77 100644
--- a/src/plugins/timeConductor/plugin.js
+++ b/src/plugins/timeConductor/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/timeConductor/pluginSpec.js b/src/plugins/timeConductor/pluginSpec.js
index a9cfce32e..d39478413 100644
--- a/src/plugins/timeConductor/pluginSpec.js
+++ b/src/plugins/timeConductor/pluginSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/timeConductor/timePopup.vue b/src/plugins/timeConductor/timePopup.vue
index ddaad859f..0d631a672 100644
--- a/src/plugins/timeConductor/timePopup.vue
+++ b/src/plugins/timeConductor/timePopup.vue
@@ -70,8 +70,9 @@
:disabled="isDisabled"
@click.prevent="submit"
></button>
- <button class="c-button icon-x"
- @click.prevent="hide"
+ <button
+ class="c-button icon-x"
+ @click.prevent="hide"
></button>
</div>
</div>
diff --git a/src/plugins/timeConductor/utcMultiTimeFormat.js b/src/plugins/timeConductor/utcMultiTimeFormat.js
index d7ccec548..712ebd87c 100644
--- a/src/plugins/timeConductor/utcMultiTimeFormat.js
+++ b/src/plugins/timeConductor/utcMultiTimeFormat.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/timeline/TimelineCompositionPolicy.js b/src/plugins/timeline/TimelineCompositionPolicy.js
new file mode 100644
index 000000000..b922e0499
--- /dev/null
+++ b/src/plugins/timeline/TimelineCompositionPolicy.js
@@ -0,0 +1,70 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2021, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+const ALLOWED_TYPES = [
+ 'telemetry.plot.overlay',
+ 'telemetry.plot.stacked',
+ 'plan'
+];
+const DISALLOWED_TYPES = [
+ 'telemetry.plot.bar-graph',
+ 'telemetry.plot.scatter-plot'
+];
+export default function TimelineCompositionPolicy(openmct) {
+ function hasNumericTelemetry(domainObject, metadata) {
+ const hasTelemetry = openmct.telemetry.isTelemetryObject(domainObject);
+ if (!hasTelemetry || !metadata) {
+ return false;
+ }
+
+ return metadata.values().length > 0 && hasDomainAndRange(metadata);
+ }
+
+ function hasDomainAndRange(metadata) {
+ return (metadata.valuesForHints(['range']).length > 0
+ && metadata.valuesForHints(['domain']).length > 0);
+ }
+
+ function hasImageTelemetry(domainObject, metadata) {
+ if (!metadata) {
+ return false;
+ }
+
+ return metadata.valuesForHints(['image']).length > 0;
+ }
+
+ return {
+ allow: function (parent, child) {
+ if (parent.type === 'time-strip') {
+ const metadata = openmct.telemetry.getMetadata(child);
+
+ if (!DISALLOWED_TYPES.includes(child.type)
+ && (hasNumericTelemetry(child, metadata) || hasImageTelemetry(child, metadata) || ALLOWED_TYPES.includes(child.type))) {
+ return true;
+ }
+
+ return false;
+ }
+
+ return true;
+ }
+ };
+}
diff --git a/src/plugins/timeline/TimelineObjectView.vue b/src/plugins/timeline/TimelineObjectView.vue
index 8db4f75ea..516d5650f 100644
--- a/src/plugins/timeline/TimelineObjectView.vue
+++ b/src/plugins/timeline/TimelineObjectView.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
-* Open MCT, Copyright (c) 2014-2021, United States Government
+* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -20,23 +20,25 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
<template>
-<swim-lane :icon-class="item.type.definition.cssClass"
- :status="status"
- :min-height="item.height"
- :show-ucontents="item.domainObject.type === 'plan'"
- :span-rows-count="item.rowCount"
+<swim-lane
+ :icon-class="item.type.definition.cssClass"
+ :status="status"
+ :min-height="item.height"
+ :show-ucontents="item.domainObject.type === 'plan'"
+ :span-rows-count="item.rowCount"
>
- <template slot="label">
+ <template #label>
{{ item.domainObject.name }}
</template>
- <object-view
- ref="objectView"
- slot="object"
- class="u-contents"
- :default-object="item.domainObject"
- :object-path="item.objectPath"
- @change-action-collection="setActionCollection"
- />
+ <template #object>
+ <object-view
+ ref="objectView"
+ class="u-contents"
+ :default-object="item.domainObject"
+ :object-path="item.objectPath"
+ @change-action-collection="setActionCollection"
+ />
+ </template>
</swim-lane>
</template>
diff --git a/src/plugins/timeline/TimelineViewLayout.vue b/src/plugins/timeline/TimelineViewLayout.vue
index 0111bf4c3..c92e4ab73 100644
--- a/src/plugins/timeline/TimelineViewLayout.vue
+++ b/src/plugins/timeline/TimelineViewLayout.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
-* Open MCT, Copyright (c) 2014-2021, United States Government
+* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -21,32 +21,36 @@
*****************************************************************************/
<template>
-<div ref="timelineHolder"
- class="c-timeline-holder"
+<div
+ ref="timelineHolder"
+ class="c-timeline-holder"
>
- <swim-lane v-for="timeSystemItem in timeSystems"
- :key="timeSystemItem.timeSystem.key"
+ <swim-lane
+ v-for="timeSystemItem in timeSystems"
+ :key="timeSystemItem.timeSystem.key"
>
- <template slot="label">
+ <template #label>
{{ timeSystemItem.timeSystem.name }}
</template>
- <template slot="object">
- <timeline-axis :bounds="timeSystemItem.bounds"
- :time-system="timeSystemItem.timeSystem"
- :content-height="height"
- :rendering-engine="'svg'"
+ <template #object>
+ <timeline-axis
+ :bounds="timeSystemItem.bounds"
+ :time-system="timeSystemItem.timeSystem"
+ :content-height="height"
+ :rendering-engine="'svg'"
/>
</template>
</swim-lane>
- <div ref="contentHolder"
- class="c-timeline__objects"
+ <div
+ ref="contentHolder"
+ class="c-timeline__objects"
>
<timeline-object-view
v-for="item in items"
:key="item.keyString"
- class="c-timeline__content"
+ class="c-timeline__content js-timeline__content"
:item="item"
/>
</div>
@@ -57,7 +61,7 @@
import TimelineObjectView from './TimelineObjectView.vue';
import TimelineAxis from '../../ui/components/TimeSystemAxis.vue';
import SwimLane from "@/ui/components/swim-lane/SwimLane.vue";
-import { getValidatedPlan } from "../plan/util";
+import { getValidatedData } from "../plan/util";
const unknownObjectType = {
definition: {
@@ -89,15 +93,15 @@ export default {
this.stopFollowingTimeContext();
},
mounted() {
+ this.items = [];
+ this.setTimeContext();
+
if (this.composition) {
this.composition.on('add', this.addItem);
this.composition.on('remove', this.removeItem);
this.composition.on('reorder', this.reorder);
this.composition.load();
}
-
- this.setTimeContext();
- this.getTimeSystems();
},
methods: {
addItem(domainObject) {
@@ -106,7 +110,7 @@ export default {
let objectPath = [domainObject].concat(this.objectPath.slice());
let rowCount = 0;
if (domainObject.type === 'plan') {
- rowCount = Object.keys(getValidatedPlan(domainObject)).length;
+ rowCount = Object.keys(getValidatedData(domainObject)).length;
}
let height = domainObject.type === 'telemetry.plot.stacked' ? `${domainObject.composition.length * 100}px` : '100px';
@@ -161,6 +165,7 @@ export default {
this.stopFollowingTimeContext();
this.timeContext = this.openmct.time.getContextForView(this.objectPath);
+ this.getTimeSystems();
this.updateViewBounds(this.timeContext.bounds());
this.timeContext.on('bounds', this.updateViewBounds);
},
diff --git a/src/plugins/timeline/TimelineViewProvider.js b/src/plugins/timeline/TimelineViewProvider.js
index 9b18bbf5a..1a759bd12 100644
--- a/src/plugins/timeline/TimelineViewProvider.js
+++ b/src/plugins/timeline/TimelineViewProvider.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/timeline/plugin.js b/src/plugins/timeline/plugin.js
index 8e3be8ea6..8e72d0e16 100644
--- a/src/plugins/timeline/plugin.js
+++ b/src/plugins/timeline/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -22,6 +22,7 @@
import TimelineViewProvider from './TimelineViewProvider';
import timelineInterceptor from "./timelineInterceptor";
+import TimelineCompositionPolicy from "./TimelineCompositionPolicy";
export default function () {
return function install(openmct) {
@@ -39,6 +40,8 @@ export default function () {
}
});
timelineInterceptor(openmct);
+ openmct.composition.addPolicy(new TimelineCompositionPolicy(openmct).allow);
+
openmct.objectViews.addProvider(new TimelineViewProvider(openmct));
};
}
diff --git a/src/plugins/timeline/pluginSpec.js b/src/plugins/timeline/pluginSpec.js
index 82a3d804e..0eb77e193 100644
--- a/src/plugins/timeline/pluginSpec.js
+++ b/src/plugins/timeline/pluginSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -20,9 +20,10 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
-import { createOpenMct, resetApplicationState } from "utils/testing";
+import { createOpenMct, resetApplicationState } from "@/utils/testing";
import TimelinePlugin from "./plugin";
import Vue from 'vue';
+import EventEmitter from "EventEmitter";
describe('the plugin', function () {
let objectDef;
@@ -30,6 +31,65 @@ describe('the plugin', function () {
let child;
let openmct;
let mockObjectPath;
+ let mockCompositionForTimelist;
+ let planObject = {
+ identifier: {
+ key: 'test-plan-object',
+ namespace: ''
+ },
+ type: 'plan',
+ id: "test-plan-object",
+ selectFile: {
+ body: JSON.stringify({
+ "TEST-GROUP": [
+ {
+ "name": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua",
+ "start": 1597170002854,
+ "end": 1597171032854,
+ "type": "TEST-GROUP",
+ "color": "fuchsia",
+ "textColor": "black"
+ },
+ {
+ "name": "Sed ut perspiciatis",
+ "start": 1597171132854,
+ "end": 1597171232854,
+ "type": "TEST-GROUP",
+ "color": "fuchsia",
+ "textColor": "black"
+ }
+ ]
+ })
+ }
+ };
+ let timelineObject = {
+ "composition": [],
+ configuration: {
+ useIndependentTime: false,
+ timeOptions: {
+ mode: {
+ key: 'fixed'
+ },
+ fixedOffsets: {
+ start: 10,
+ end: 11
+ },
+ clockOffsets: {
+ start: -(30 * 60 * 1000),
+ end: (30 * 60 * 1000)
+ }
+ }
+ },
+ "name": "Some timestrip",
+ "type": "time-strip",
+ "location": "mine",
+ "modified": 1631005183584,
+ "persisted": 1631005183502,
+ "identifier": {
+ "namespace": "",
+ "key": "b78e7e23-f2b8-4776-b1f0-3ff778f5c8a9"
+ }
+ };
beforeEach((done) => {
mockObjectPath = [
@@ -102,12 +162,7 @@ describe('the plugin', function () {
beforeEach(() => {
testViewObject = {
- id: "test-object",
- identifier: {
- key: "test-object",
- namespace: ''
- },
- type: "time-strip"
+ ...timelineObject
};
const applicableViews = openmct.objectViews.get(testViewObject, mockObjectPath);
@@ -133,30 +188,57 @@ describe('the plugin', function () {
});
});
+ describe('the timeline composition', () => {
+ let timelineDomainObject;
+ let timelineView;
+
+ beforeEach(() => {
+ timelineDomainObject = {
+ ...timelineObject,
+ composition: [
+ {
+ identifier: {
+ key: 'test-plan-object',
+ namespace: ''
+ }
+ }
+ ]
+ };
+
+ mockCompositionForTimelist = new EventEmitter();
+ mockCompositionForTimelist.load = () => {
+ mockCompositionForTimelist.emit('add', planObject);
+
+ return [planObject];
+ };
+
+ spyOn(openmct.composition, 'get').withArgs(timelineDomainObject).and.returnValue(mockCompositionForTimelist);
+
+ openmct.router.path = [timelineDomainObject];
+
+ const applicableViews = openmct.objectViews.get(timelineDomainObject, [timelineDomainObject]);
+ timelineView = applicableViews.find((viewProvider) => viewProvider.key === 'time-strip.view');
+ let view = timelineView.view(timelineDomainObject, [timelineDomainObject]);
+ view.show(child, true);
+
+ return Vue.nextTick();
+ });
+
+ it('loads the plan from composition', () => {
+ return Vue.nextTick(() => {
+ const items = element.querySelectorAll('.js-timeline__content');
+ expect(items.length).toEqual(1);
+ });
+ });
+ });
+
describe('the independent time conductor', () => {
let timelineView;
let testViewObject = {
- id: "test-object",
- identifier: {
- key: "test-object",
- namespace: ''
- },
- type: "time-strip",
+ ...timelineObject,
configuration: {
- useIndependentTime: true,
- timeOptions: {
- mode: {
- key: 'local'
- },
- fixedOffsets: {
- start: 10,
- end: 11
- },
- clockOffsets: {
- start: -(30 * 60 * 1000),
- end: (30 * 60 * 1000)
- }
- }
+ ...timelineObject.configuration,
+ useIndependentTime: true
}
};
@@ -181,30 +263,18 @@ describe('the plugin', function () {
});
});
- describe('the independent time conductor', () => {
+ describe('the independent time conductor - fixed', () => {
let timelineView;
let testViewObject2 = {
+ ...timelineObject,
id: "test-object2",
identifier: {
key: "test-object2",
namespace: ''
},
- type: "time-strip",
configuration: {
- useIndependentTime: true,
- timeOptions: {
- mode: {
- key: 'fixed'
- },
- fixedOffsets: {
- start: 10,
- end: 11
- },
- clockOffsets: {
- start: -(30 * 60 * 1000),
- end: (30 * 60 * 1000)
- }
- }
+ ...timelineObject.configuration,
+ useIndependentTime: true
}
};
@@ -228,4 +298,68 @@ describe('the plugin', function () {
});
});
+ describe("The timestrip composition policy", () => {
+ let testObject;
+ beforeEach(() => {
+ testObject = {
+ ...timelineObject,
+ composition: []
+ };
+ });
+
+ it("allows composition for plots", () => {
+ const testTelemetryObject = {
+ identifier: {
+ namespace: "",
+ key: "test-object"
+ },
+ type: "test-object",
+ name: "Test Object",
+ telemetry: {
+ values: [{
+ key: "some-key",
+ name: "Some attribute",
+ hints: {
+ domain: 1
+ }
+ }, {
+ key: "some-other-key",
+ name: "Another attribute",
+ hints: {
+ range: 1
+ }
+ }]
+ }
+ };
+ const composition = openmct.composition.get(testObject);
+ expect(() => {
+ composition.add(testTelemetryObject);
+ }).not.toThrow();
+ expect(testObject.composition.length).toBe(1);
+ });
+
+ it("allows composition for plans", () => {
+ const composition = openmct.composition.get(testObject);
+ expect(() => {
+ composition.add(planObject);
+ }).not.toThrow();
+ expect(testObject.composition.length).toBe(1);
+ });
+
+ it("disallows composition for non time-based plots", () => {
+ const barGraphObject = {
+ identifier: {
+ namespace: "",
+ key: "test-object"
+ },
+ type: "telemetry.plot.bar-graph",
+ name: "Test Object"
+ };
+ const composition = openmct.composition.get(testObject);
+ expect(() => {
+ composition.add(barGraphObject);
+ }).toThrow();
+ expect(testObject.composition.length).toBe(0);
+ });
+ });
});
diff --git a/src/plugins/timeline/timelineInterceptor.js b/src/plugins/timeline/timelineInterceptor.js
index 3627f99a2..a5b38a67e 100644
--- a/src/plugins/timeline/timelineInterceptor.js
+++ b/src/plugins/timeline/timelineInterceptor.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/timelist/Timelist.vue b/src/plugins/timelist/Timelist.vue
new file mode 100644
index 000000000..58af224c3
--- /dev/null
+++ b/src/plugins/timelist/Timelist.vue
@@ -0,0 +1,465 @@
+<!--
+ Open MCT, Copyright (c) 2014-2022, United States Government
+ as represented by the Administrator of the National Aeronautics and Space
+ Administration. All rights reserved.
+
+ Open MCT is licensed under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0.
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ License for the specific language governing permissions and limitations
+ under the License.
+
+ Open MCT includes source code licensed under additional open source
+ licenses. See the Open Source Licenses file (LICENSES.md) included with
+ this source code distribution or the Licensing information page available
+ at runtime from the About dialog for additional information.
+-->
+
+<template>
+<div
+ ref="timelistHolder"
+ class="c-timelist"
+>
+ <list-view
+ :items="planActivities"
+ :header-items="headerItems"
+ :default-sort="defaultSort"
+ class="sticky"
+ />
+</div>
+</template>
+
+<script>
+import {getValidatedData} from "../plan/util";
+import ListView from '../../ui/components/List/ListView.vue';
+import {getPreciseDuration} from "../../utils/duration";
+import ticker from 'utils/clock/Ticker';
+import {SORT_ORDER_OPTIONS} from "./constants";
+
+import moment from "moment";
+import { v4 as uuid } from 'uuid';
+
+const SCROLL_TIMEOUT = 10000;
+const ROW_HEIGHT = 30;
+const TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss:SSS';
+const headerItems = [
+ {
+ defaultDirection: true,
+ isSortable: true,
+ property: 'start',
+ name: 'Start Time',
+ format: function (value, object) {
+ return `${moment(value).format(TIME_FORMAT)}Z`;
+ }
+ }, {
+ defaultDirection: true,
+ isSortable: true,
+ property: 'end',
+ name: 'End Time',
+ format: function (value, object) {
+ return `${moment(value).format(TIME_FORMAT)}Z`;
+ }
+ }, {
+ defaultDirection: false,
+ property: 'duration',
+ name: 'Time To/From',
+ format: function (value) {
+ let result;
+ if (value < 0) {
+ result = `-${getPreciseDuration(Math.abs(value))}`;
+ } else if (value > 0) {
+ result = `+${getPreciseDuration(value)}`;
+ } else {
+ result = 'Now';
+ }
+
+ return result;
+ }
+ }, {
+ defaultDirection: true,
+ property: 'name',
+ name: 'Activity'
+ }
+];
+
+const defaultSort = {
+ property: 'start',
+ defaultDirection: true
+};
+
+export default {
+ components: {
+ ListView
+ },
+ inject: ['openmct', 'domainObject', 'path', 'composition'],
+ data() {
+ this.planObjects = [];
+
+ return {
+ viewBounds: undefined,
+ height: 0,
+ planActivities: [],
+ headerItems: headerItems,
+ defaultSort: defaultSort
+ };
+ },
+ mounted() {
+ this.isEditing = this.openmct.editor.isEditing();
+ this.timestamp = Date.now();
+ this.getPlanDataAndSetConfig(this.domainObject);
+
+ this.unlisten = this.openmct.objects.observe(this.domainObject, 'selectFile', this.planFileUpdated);
+ this.unlistenConfig = this.openmct.objects.observe(this.domainObject, 'configuration', this.setViewFromConfig);
+ this.removeStatusListener = this.openmct.status.observe(this.domainObject.identifier, this.setStatus);
+ this.status = this.openmct.status.get(this.domainObject.identifier);
+ this.unlistenTicker = ticker.listen(this.clearPreviousActivities);
+ this.openmct.editor.on('isEditing', this.setEditState);
+
+ this.deferAutoScroll = _.debounce(this.deferAutoScroll, 500);
+ this.$el.parentElement.addEventListener('scroll', this.deferAutoScroll, true);
+
+ if (this.composition) {
+ this.composition.on('add', this.addToComposition);
+ this.composition.on('remove', this.removeItem);
+ this.composition.load();
+ }
+ },
+ beforeDestroy() {
+ if (this.unlisten) {
+ this.unlisten();
+ }
+
+ if (this.unlistenConfig) {
+ this.unlistenConfig();
+ }
+
+ if (this.unlistenTicker) {
+ this.unlistenTicker();
+ }
+
+ if (this.removeStatusListener) {
+ this.removeStatusListener();
+ }
+
+ this.openmct.editor.off('isEditing', this.setEditState);
+
+ this.$el.parentElement.removeEventListener('scroll', this.deferAutoScroll, true);
+ if (this.clearAutoScrollDisabledTimer) {
+ clearTimeout(this.clearAutoScrollDisabledTimer);
+ }
+
+ if (this.composition) {
+ this.composition.off('add', this.addToComposition);
+ this.composition.off('remove', this.removeItem);
+ }
+ },
+ methods: {
+ planFileUpdated(selectFile) {
+ this.getPlanData({
+ selectFile,
+ sourceMap: this.domainObject.sourceMap
+ });
+ },
+ getPlanDataAndSetConfig(mutatedObject) {
+ this.getPlanData(mutatedObject);
+ this.setViewFromConfig(mutatedObject.configuration);
+ },
+ setViewFromConfig(configuration) {
+ if (this.isEditing) {
+ this.filterValue = configuration.filter;
+ this.hideAll = false;
+ this.showAll = true;
+ this.listActivities();
+ } else {
+ this.filterValue = configuration.filter;
+ this.setSort();
+ this.setViewBounds();
+ this.listActivities();
+ }
+ },
+ addItem(domainObject) {
+ this.planObjects = [domainObject];
+ this.resetPlanData();
+ if (domainObject.type === 'plan') {
+ this.getPlanDataAndSetConfig({
+ ...this.domainObject,
+ selectFile: domainObject.selectFile
+ });
+ }
+ },
+ addToComposition(telemetryObject) {
+ if (this.planObjects.length > 0) {
+ this.confirmReplacePlan(telemetryObject);
+ } else {
+ this.addItem(telemetryObject);
+ }
+ },
+ confirmReplacePlan(telemetryObject) {
+ const dialog = this.openmct.overlays.dialog({
+ iconClass: 'alert',
+ message: 'This action will replace the current plan. Do you want to continue?',
+ buttons: [
+ {
+ label: 'Ok',
+ emphasis: true,
+ callback: () => {
+ const oldTelemetryObject = this.planObjects[0];
+ this.removeFromComposition(oldTelemetryObject);
+ this.addItem(telemetryObject);
+ dialog.dismiss();
+ }
+ },
+ {
+ label: 'Cancel',
+ callback: () => {
+ this.removeFromComposition(telemetryObject);
+ dialog.dismiss();
+ }
+ }
+ ]
+ });
+ },
+ removeFromComposition(telemetryObject) {
+ this.composition.remove(telemetryObject);
+ },
+ removeItem() {
+ this.planObjects = [];
+ this.resetPlanData();
+ },
+ resetPlanData() {
+ this.planData = {};
+ },
+ getPlanData(domainObject) {
+ this.planData = getValidatedData(domainObject);
+ },
+ setViewBounds() {
+ const pastEventsIndex = this.domainObject.configuration.pastEventsIndex;
+ const currentEventsIndex = this.domainObject.configuration.currentEventsIndex;
+ const futureEventsIndex = this.domainObject.configuration.futureEventsIndex;
+ const pastEventsDuration = this.domainObject.configuration.pastEventsDuration;
+ const pastEventsDurationIndex = this.domainObject.configuration.pastEventsDurationIndex;
+ const futureEventsDuration = this.domainObject.configuration.futureEventsDuration;
+ const futureEventsDurationIndex = this.domainObject.configuration.futureEventsDurationIndex;
+
+ if (pastEventsIndex === 0 && futureEventsIndex === 0 && currentEventsIndex === 0) {
+ //don't show all events
+ this.showAll = false;
+ this.viewBounds = undefined;
+ this.hideAll = true;
+
+ return;
+ }
+
+ this.hideAll = false;
+
+ if (pastEventsIndex === 1 && futureEventsIndex === 1 && currentEventsIndex === 1) {
+ //show all events
+ this.showAll = true;
+ this.viewBounds = undefined;
+
+ return;
+ }
+
+ this.showAll = false;
+
+ this.viewBounds = {};
+
+ this.noCurrent = currentEventsIndex === 0;
+
+ if (pastEventsIndex !== 1) {
+ const pastDurationInMS = this.getDurationInMilliSeconds(pastEventsDuration, pastEventsDurationIndex);
+ this.viewBounds.pastEnd = (timestamp) => {
+ if (pastEventsIndex === 2) {
+ return timestamp - pastDurationInMS;
+ } else if (pastEventsIndex === 0) {
+ return timestamp + 1;
+ }
+ };
+ }
+
+ if (futureEventsIndex !== 1) {
+ const futureDurationInMS = this.getDurationInMilliSeconds(futureEventsDuration, futureEventsDurationIndex);
+ this.viewBounds.futureStart = (timestamp) => {
+ if (futureEventsIndex === 2) {
+ return timestamp + futureDurationInMS;
+ } else if (futureEventsIndex === 0) {
+ return 0;
+ }
+ };
+ }
+ },
+ getDurationInMilliSeconds(duration, durationIndex) {
+ if (duration > 0) {
+ if (durationIndex === 0) {
+ return duration * 1000;
+ } else if (durationIndex === 1) {
+ return duration * 60 * 1000;
+ } else if (durationIndex === 2) {
+ return duration * 60 * 60 * 1000;
+ }
+ }
+ },
+ listActivities() {
+ let groups = Object.keys(this.planData);
+ let activities = [];
+
+ groups.forEach((key) => {
+ activities = activities.concat(this.planData[key]);
+ });
+ activities = activities.filter(this.filterActivities);
+ activities = this.applyStyles(activities);
+ this.setScrollTop();
+ // sort by start time
+ this.planActivities = activities.sort(this.sortByStartTime);
+ },
+ clearPreviousActivities(time) {
+ if (time instanceof Date) {
+ this.timestamp = time.getTime();
+ } else {
+ this.timestamp = time;
+ }
+
+ this.listActivities();
+ },
+ filterActivities(activity, index) {
+
+ const hasFilterMatch = this.filterByName(activity.name);
+
+ if (hasFilterMatch === false || this.hideAll === true) {
+ return false;
+ }
+
+ if (this.showAll === true) {
+ return true;
+ }
+
+ //current event or future start event or past end event
+ const isCurrent = (this.noCurrent === false && this.timestamp >= activity.start && this.timestamp <= activity.end);
+ const isPast = (this.timestamp > activity.end && (this.viewBounds.pastEnd === undefined || activity.end >= this.viewBounds.pastEnd(this.timestamp)));
+ const isFuture = (this.timestamp < activity.start && (this.viewBounds.futureStart === undefined || activity.start <= this.viewBounds.futureStart(this.timestamp)));
+
+ return isCurrent || isPast || isFuture;
+ },
+ filterByName(name) {
+ const filters = this.filterValue.split(',');
+
+ return filters.some((search => {
+ const normalized = search.trim().toLowerCase();
+ const regex = new RegExp(normalized);
+
+ return regex.test(name.toLowerCase());
+ }));
+ },
+ applyStyles(activities) {
+ let firstCurrentActivityIndex = -1;
+ let currentActivitiesCount = 0;
+ const styledActivities = activities.map((activity, index) => {
+ if (this.timestamp >= activity.start && this.timestamp <= activity.end) {
+ activity.cssClass = '--is-current';
+ if (firstCurrentActivityIndex < 0) {
+ firstCurrentActivityIndex = index;
+ }
+
+ currentActivitiesCount = currentActivitiesCount + 1;
+ } else if (this.timestamp < activity.start) {
+ activity.cssClass = '--is-future';
+ } else {
+ activity.cssClass = '--is-past';
+ }
+
+ if (!activity.key) {
+ activity.key = uuid();
+ }
+
+ activity.duration = activity.start - this.timestamp;
+
+ return activity;
+ });
+
+ this.firstCurrentActivityIndex = firstCurrentActivityIndex;
+ this.currentActivitiesCount = currentActivitiesCount;
+
+ return styledActivities;
+ },
+ canAutoScroll() {
+ //this distinguishes between programmatic vs user-triggered scroll events
+ this.autoScrolled = (this.dontAutoScroll !== true);
+
+ return this.autoScrolled;
+ },
+ resetScroll() {
+ if (this.canAutoScroll() === false) {
+ return;
+ }
+
+ this.firstCurrentActivityIndex = -1;
+ this.currentActivitiesCount = 0;
+ this.$el.parentElement.scrollTo({top: 0});
+ this.autoScrolled = false;
+ },
+ setScrollTop() {
+ //scroll to somewhere mid-way of the current activities
+ if (this.firstCurrentActivityIndex > -1) {
+ if (this.canAutoScroll() === false) {
+ return;
+ }
+
+ const scrollOffset = this.currentActivitiesCount > 0 ? Math.floor(this.currentActivitiesCount / 2) : 0;
+ this.$el.parentElement.scrollTo({
+ top: ROW_HEIGHT * (this.firstCurrentActivityIndex + scrollOffset),
+ behavior: "smooth"
+ });
+ this.autoScrolled = false;
+ } else {
+ this.resetScroll();
+ }
+ },
+ deferAutoScroll() {
+ //if this is not a user-triggered event, don't defer auto scrolling
+ if (this.autoScrolled) {
+ this.autoScrolled = false;
+
+ return;
+ }
+
+ this.dontAutoScroll = true;
+ const self = this;
+ if (this.clearAutoScrollDisabledTimer) {
+ clearTimeout(this.clearAutoScrollDisabledTimer);
+ }
+
+ this.clearAutoScrollDisabledTimer = setTimeout(() => {
+ self.dontAutoScroll = false;
+ self.setScrollTop();
+ }, SCROLL_TIMEOUT);
+ },
+ setSort() {
+ const sortOrder = SORT_ORDER_OPTIONS[this.domainObject.configuration.sortOrderIndex];
+ const property = sortOrder.property;
+ const direction = sortOrder.direction.toLowerCase() === 'asc';
+ this.defaultSort = {
+ property,
+ defaultDirection: direction
+ };
+ },
+ sortByStartTime(a, b) {
+ const numA = parseInt(a.start, 10);
+ const numB = parseInt(b.start, 10);
+
+ return numA - numB;
+ },
+ setStatus(status) {
+ this.status = status;
+ },
+ setEditState(isEditing) {
+ this.isEditing = isEditing;
+ this.setViewFromConfig(this.domainObject.configuration);
+ }
+ }
+};
+</script>
diff --git a/src/plugins/legacySupport/legacyRegistrySpec.js b/src/plugins/timelist/TimelistCompositionPolicy.js
index 64707fc64..75b3318a7 100644
--- a/src/plugins/legacySupport/legacyRegistrySpec.js
+++ b/src/plugins/timelist/TimelistCompositionPolicy.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -19,15 +19,16 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
+import {TIMELIST_TYPE} from "@/plugins/timelist/constants";
-define([
- './legacyRegistry',
- './BundleRegistry'
-], function (legacyRegistry, BundleRegistry) {
+export default function TimelistCompositionPolicy(openmct) {
+ return {
+ allow: function (parent, child) {
+ if (parent.type === TIMELIST_TYPE && child.type !== 'plan') {
+ return false;
+ }
- describe("legacyRegistry", function () {
- it("is a BundleRegistry", function () {
- expect(legacyRegistry instanceof BundleRegistry).toBe(true);
- });
- });
-});
+ return true;
+ }
+ };
+}
diff --git a/src/plugins/timelist/TimelistViewProvider.js b/src/plugins/timelist/TimelistViewProvider.js
new file mode 100644
index 000000000..ed2101ccc
--- /dev/null
+++ b/src/plugins/timelist/TimelistViewProvider.js
@@ -0,0 +1,68 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+import Timelist from './Timelist.vue';
+import { TIMELIST_TYPE } from './constants';
+import Vue from 'vue';
+
+export default function TimelistViewProvider(openmct) {
+
+ return {
+ key: 'timelist.view',
+ name: 'Time List',
+ cssClass: 'icon-timelist',
+ canView(domainObject) {
+ return domainObject.type === TIMELIST_TYPE;
+ },
+
+ canEdit(domainObject) {
+ return domainObject.type === TIMELIST_TYPE;
+ },
+
+ view: function (domainObject, objectPath) {
+ let component;
+
+ return {
+ show: function (element) {
+
+ component = new Vue({
+ el: element,
+ components: {
+ Timelist
+ },
+ provide: {
+ openmct,
+ domainObject,
+ path: objectPath,
+ composition: openmct.composition.get(domainObject)
+ },
+ template: '<timelist></timelist>'
+ });
+ },
+ destroy: function () {
+ component.$destroy();
+ component = undefined;
+ }
+ };
+ }
+ };
+}
diff --git a/src/plugins/timelist/constants.js b/src/plugins/timelist/constants.js
new file mode 100644
index 000000000..7d9c978c0
--- /dev/null
+++ b/src/plugins/timelist/constants.js
@@ -0,0 +1,24 @@
+export const SORT_ORDER_OPTIONS = [
+ {
+ label: 'Start ascending',
+ property: 'start',
+ direction: 'ASC'
+ },
+ {
+ label: 'Start descending',
+ property: 'start',
+ direction: 'DESC'
+ },
+ {
+ label: 'End ascending',
+ property: 'end',
+ direction: 'ASC'
+ },
+ {
+ label: 'End descending',
+ property: 'end',
+ direction: 'DESC'
+ }
+];
+
+export const TIMELIST_TYPE = 'timelist';
diff --git a/src/plugins/timelist/inspector/EventProperties.vue b/src/plugins/timelist/inspector/EventProperties.vue
new file mode 100644
index 000000000..0183687a4
--- /dev/null
+++ b/src/plugins/timelist/inspector/EventProperties.vue
@@ -0,0 +1,124 @@
+<template>
+<li class="c-inspect-properties__row">
+ <div
+ class="c-inspect-properties__label"
+ title="Options for future events."
+ >{{ label }}</div>
+ <div
+ v-if="canEdit"
+ class="c-inspect-properties__value"
+ >
+ <select
+ v-model="index"
+ @change="updateForm('index')"
+ >
+ <option
+ v-for="(activityOption, activityKey) in activitiesOptions"
+ :key="activityKey"
+ :value="activityKey"
+ >{{ activityOption }}</option>
+ </select>
+ <input
+ v-if="index === 2"
+ v-model="duration"
+ class="c-input c-input--sm"
+ type="text"
+ @change="updateForm('duration')"
+ >
+ <select
+ v-if="index === 2"
+ v-model="durationIndex"
+ @change="updateForm('durationIndex')"
+ >
+ <option
+ v-for="(durationOption, durationKey) in durationOptions"
+ :key="durationKey"
+ :value="durationKey"
+ >{{ durationOption }}</option>
+ </select>
+ </div>
+ <div
+ v-else
+ class="c-inspect-properties__value"
+ >
+ {{ activitiesOptions[index] }} <span v-if="index > 1">{{ duration }} {{ durationOptions[durationIndex] }}</span>
+ </div>
+</li>
+</template>
+
+<script>
+const ACTIVITIES_OPTIONS = [
+ 'Don\'t show',
+ 'Show all',
+ 'Show starts within',
+ 'Show after end for'
+];
+
+const DURATION_OPTIONS = [
+ 'seconds',
+ 'minutes',
+ 'hours'
+];
+
+export default {
+ inject: ['openmct', 'domainObject'],
+ props: {
+ label: {
+ type: String,
+ required: true
+ },
+ prefix: {
+ type: String,
+ required: true
+ }
+ },
+ data() {
+ return {
+ index: this.domainObject.configuration[`${this.prefix}Index`],
+ durationIndex: this.domainObject.configuration[`${this.prefix}DurationIndex`],
+ duration: this.domainObject.configuration[`${this.prefix}Duration`],
+ activitiesOptions: ACTIVITIES_OPTIONS,
+ durationOptions: DURATION_OPTIONS,
+ isEditing: this.openmct.editor.isEditing()
+ };
+ },
+ computed: {
+ canEdit() {
+ return this.isEditing && !this.domainObject.locked;
+ }
+ },
+ mounted() {
+ if (this.prefix === 'futureEvents') {
+ this.activitiesOptions = ACTIVITIES_OPTIONS.slice(0, 3);
+ } else if (this.prefix === 'pastEvents') {
+ this.activitiesOptions = ACTIVITIES_OPTIONS.filter((item, index) => index !== 2);
+ } else if (this.prefix === 'currentEvents') {
+ this.activitiesOptions = ACTIVITIES_OPTIONS.slice(0, 2);
+ }
+
+ this.openmct.editor.on('isEditing', this.setEditState);
+ },
+ beforeDestroy() {
+ this.openmct.editor.off('isEditing', this.setEditState);
+ },
+ methods: {
+ updateForm(property) {
+ if (!this.isValid()) {
+ return;
+ }
+
+ const capitalized = property.charAt(0).toUpperCase() + property.substr(1);
+ this.$emit('updated', {
+ property: `${this.prefix}${capitalized}`,
+ value: this[property]
+ });
+ },
+ isValid() {
+ return this.index < 2 || (this.durationIndex >= 0 && this.duration > 0);
+ },
+ setEditState(isEditing) {
+ this.isEditing = isEditing;
+ }
+ }
+};
+</script>
diff --git a/src/plugins/timelist/inspector/Filtering.vue b/src/plugins/timelist/inspector/Filtering.vue
new file mode 100644
index 000000000..360305960
--- /dev/null
+++ b/src/plugins/timelist/inspector/Filtering.vue
@@ -0,0 +1,91 @@
+<template>
+<li class="c-inspect-properties__row">
+ <div
+ v-if="canEdit"
+ class="c-inspect-properties__hint span-all"
+ >Filter this view by comma-separated keywords.</div>
+ <div
+ class="c-inspect-properties__label"
+ title="Filter by keyword."
+ >Filters</div>
+ <div
+ v-if="canEdit"
+ class="c-inspect-properties__value"
+ :class="{'form-error': hasError}"
+ >
+ <textarea
+ v-model="filterValue"
+ class="c-input--flex"
+ type="text"
+ @keydown.enter.exact.stop="forceBlur($event)"
+ @keyup="updateForm($event, 'filter')"
+ ></textarea>
+ </div>
+ <div
+ v-else
+ class="c-inspect-properties__value"
+ >
+ {{ filterValue }}
+ </div>
+</li>
+</template>
+
+<script>
+export default {
+ inject: ['openmct', 'domainObject'],
+ data() {
+ return {
+ isEditing: this.openmct.editor.isEditing(),
+ filterValue: this.domainObject.configuration.filter,
+ hasError: false
+ };
+ },
+ computed: {
+ canEdit() {
+ return this.isEditing && !this.domainObject.locked;
+ }
+ },
+ mounted() {
+ this.openmct.editor.on('isEditing', this.setEditState);
+ },
+ beforeDestroy() {
+ this.openmct.editor.off('isEditing', this.setEditState);
+ },
+ methods: {
+ setEditState(isEditing) {
+ this.isEditing = isEditing;
+ if (!this.isEditing && this.hasError) {
+ this.filterValue = this.domainObject.configuration.filter;
+ this.hasError = false;
+ }
+ },
+ forceBlur(event) {
+ event.target.blur();
+ },
+ updateForm(event, property) {
+ if (!this.isValid()) {
+ this.hasError = true;
+
+ return;
+ }
+
+ this.hasError = false;
+
+ this.$emit('updated', {
+ property,
+ value: this.filterValue.replace(/,(\s)*$/, '')
+ });
+ },
+ isValid() {
+ // Test for any word character, any whitespace character or comma
+ if (this.filterValue === '') {
+ return true;
+ }
+
+ const regex = new RegExp(/^([a-zA-Z0-9_\-\s,])+$/g);
+
+ return regex.test(this.filterValue);
+ }
+ }
+};
+</script>
diff --git a/src/plugins/timelist/inspector/TimeListInspectorViewProvider.js b/src/plugins/timelist/inspector/TimeListInspectorViewProvider.js
new file mode 100644
index 000000000..b3407c4a4
--- /dev/null
+++ b/src/plugins/timelist/inspector/TimeListInspectorViewProvider.js
@@ -0,0 +1,70 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+import TimelistPropertiesView from "./TimelistPropertiesView.vue";
+import { TIMELIST_TYPE } from '../constants';
+import Vue from 'vue';
+
+export default function TimeListInspectorViewProvider(openmct) {
+ return {
+ key: 'timelist-inspector',
+ name: 'Timelist Inspector View',
+ canView: function (selection) {
+ if (selection.length === 0 || selection[0].length === 0) {
+ return false;
+ }
+
+ let context = selection[0][0].context;
+
+ return context && context.item
+ && context.item.type === TIMELIST_TYPE;
+ },
+ view: function (selection) {
+ let component;
+
+ return {
+ show: function (element) {
+ component = new Vue({
+ el: element,
+ components: {
+ TimelistPropertiesView: TimelistPropertiesView
+ },
+ provide: {
+ openmct,
+ domainObject: selection[0][0].context.item
+ },
+ template: '<timelist-properties-view></timelist-properties-view>'
+ });
+ },
+ destroy: function () {
+ if (component) {
+ component.$destroy();
+ component = undefined;
+ }
+ }
+ };
+ },
+ priority: function () {
+ return 1;
+ }
+ };
+}
diff --git a/src/plugins/timelist/inspector/TimelistPropertiesView.vue b/src/plugins/timelist/inspector/TimelistPropertiesView.vue
new file mode 100644
index 000000000..57a1747b7
--- /dev/null
+++ b/src/plugins/timelist/inspector/TimelistPropertiesView.vue
@@ -0,0 +1,146 @@
+<!--
+ Open MCT, Copyright (c) 2014-2022, United States Government
+ as represented by the Administrator of the National Aeronautics and Space
+ Administration. All rights reserved.
+
+ Open MCT is licensed under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0.
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ License for the specific language governing permissions and limitations
+ under the License.
+
+ Open MCT includes source code licensed under additional open source
+ licenses. See the Open Source Licenses file (LICENSES.md) included with
+ this source code distribution or the Licensing information page available
+ at runtime from the About dialog for additional information.
+-->
+
+<template>
+<div class="c-timelist-properties">
+ <div class="c-inspect-properties">
+ <ul class="c-inspect-properties__section">
+ <div
+ class="c-inspect-properties_header"
+ title="'Timeframe options'"
+ >Timeframe</div>
+ <li class="c-inspect-properties__row">
+ <div
+ v-if="canEdit"
+ class="c-inspect-properties__hint span-all"
+ >These settings are not previewed and will be applied after editing is completed.</div>
+ <div
+ class="c-inspect-properties__label"
+ title="Sort order of the timelist."
+ >Sort Order</div>
+ <div
+ v-if="canEdit"
+ class="c-inspect-properties__value"
+ >
+ <select
+ v-model="sortOrderIndex"
+ @change="updateSortOrder()"
+ >
+ <option
+ v-for="(sortOrderOption, index) in sortOrderOptions"
+ :key="index"
+ :value="index"
+ >{{ sortOrderOption.label }}</option>
+ </select>
+ </div>
+ <div
+ v-else
+ class="c-inspect-properties__value"
+ >
+ {{ sortOrderOptions[sortOrderIndex].label }}
+ </div>
+ </li>
+ <event-properties
+ v-for="type in eventTypes"
+ :key="type.prefix"
+ :label="type.label"
+ :prefix="type.prefix"
+ @updated="eventPropertiesUpdated"
+ />
+ </ul>
+ </div>
+ <div class="c-inspect-properties">
+ <ul class="c-inspect-properties__section">
+ <div
+ class="c-inspect-properties_header"
+ title="'Filters'"
+ >Filtering</div>
+ <filtering @updated="eventPropertiesUpdated" />
+ </ul>
+ </div>
+</div>
+</template>
+
+<script>
+
+import EventProperties from './EventProperties.vue';
+import { SORT_ORDER_OPTIONS } from '../constants';
+import Filtering from './Filtering.vue';
+
+const EVENT_TYPES = [
+ {
+ label: 'Future Events',
+ prefix: 'futureEvents'
+ },
+ {
+ label: 'Current Events',
+ prefix: 'currentEvents'
+ },
+ {
+ label: 'Past Events',
+ prefix: 'pastEvents'
+ }
+];
+
+export default {
+ components: {
+ Filtering,
+ EventProperties
+ },
+ inject: ['openmct', 'domainObject'],
+ data() {
+ return {
+ sortOrderIndex: this.domainObject.configuration.sortOrderIndex,
+ sortOrderOptions: SORT_ORDER_OPTIONS,
+ eventTypes: EVENT_TYPES,
+ isEditing: this.openmct.editor.isEditing()
+ };
+ },
+ computed: {
+ canEdit() {
+ return this.isEditing && !this.domainObject.locked;
+ }
+ },
+ mounted() {
+ this.openmct.editor.on('isEditing', this.setEditState);
+ },
+ beforeDestroy() {
+ this.openmct.editor.off('isEditing', this.setEditState);
+ },
+ methods: {
+ setEditState(isEditing) {
+ this.isEditing = isEditing;
+ },
+ updateSortOrder() {
+ this.updateProperty('sortOrderIndex', this.sortOrderIndex);
+ },
+ updateProperty(key, value) {
+ this.openmct.objects.mutate(this.domainObject, `configuration.${key}`, value);
+ },
+ eventPropertiesUpdated(data) {
+ const key = data.property;
+ const value = data.value;
+ this.updateProperty(key, value);
+ }
+ }
+};
+</script>
diff --git a/src/plugins/timelist/plugin.js b/src/plugins/timelist/plugin.js
new file mode 100644
index 000000000..8597f5a54
--- /dev/null
+++ b/src/plugins/timelist/plugin.js
@@ -0,0 +1,70 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+import TimelistViewProvider from './TimelistViewProvider';
+import { TIMELIST_TYPE } from './constants';
+import TimeListInspectorViewProvider from "./inspector/TimeListInspectorViewProvider";
+import TimelistCompositionPolicy from "@/plugins/timelist/TimelistCompositionPolicy";
+
+export default function () {
+ return function install(openmct) {
+ openmct.types.addType(TIMELIST_TYPE, {
+ name: 'Time List',
+ key: TIMELIST_TYPE,
+ description: 'A configurable, time-ordered list view of activities for a compatible mission plan file.',
+ creatable: true,
+ cssClass: 'icon-timelist',
+ form: [
+ {
+ name: 'Upload Plan (JSON File)',
+ key: 'selectFile',
+ control: 'file-input',
+ text: 'Select File...',
+ type: 'application/json',
+ property: [
+ "selectFile"
+ ]
+ }
+ ],
+ initialize: function (domainObject) {
+ domainObject.configuration = {
+ sortOrderIndex: 0,
+ futureEventsIndex: 0,
+ futureEventsDurationIndex: 0,
+ futureEventsDuration: 20,
+ currentEventsIndex: 1,
+ currentEventsDurationIndex: 0,
+ currentEventsDuration: 20,
+ pastEventsIndex: 0,
+ pastEventsDurationIndex: 0,
+ pastEventsDuration: 20,
+ filter: ''
+ };
+ domainObject.composition = [];
+ }
+ });
+ openmct.objectViews.addProvider(new TimelistViewProvider(openmct));
+ openmct.inspectorViews.addProvider(new TimeListInspectorViewProvider(openmct));
+ openmct.composition.addPolicy(new TimelistCompositionPolicy(openmct).allow);
+
+ };
+}
diff --git a/src/plugins/timelist/pluginSpec.js b/src/plugins/timelist/pluginSpec.js
new file mode 100644
index 000000000..1b117e00f
--- /dev/null
+++ b/src/plugins/timelist/pluginSpec.js
@@ -0,0 +1,379 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+import {createOpenMct, resetApplicationState} from "utils/testing";
+import TimelistPlugin from "./plugin";
+import { TIMELIST_TYPE } from "./constants";
+import Vue from 'vue';
+import moment from "moment";
+import EventEmitter from "EventEmitter";
+
+const LIST_ITEM_CLASS = '.js-table__body .js-list-item';
+const LIST_ITEM_VALUE_CLASS = '.js-list-item__value';
+const LIST_ITEM_BODY_CLASS = '.js-table__body th';
+
+describe('the plugin', function () {
+ let timelistDefinition;
+ let element;
+ let child;
+ let openmct;
+ let appHolder;
+ let originalRouterPath;
+ let mockComposition;
+ let now = Date.now();
+ let twoHoursPast = now - (1000 * 60 * 60 * 2);
+ let oneHourPast = now - (1000 * 60 * 60);
+ let twoHoursFuture = now + (1000 * 60 * 60 * 2);
+ let planObject = {
+ identifier: {
+ key: 'test-plan-object',
+ namespace: ''
+ },
+ type: 'plan',
+ id: "test-plan-object",
+ selectFile: {
+ body: JSON.stringify({
+ "TEST-GROUP": [
+ {
+ "name": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua",
+ "start": twoHoursPast,
+ "end": oneHourPast,
+ "type": "TEST-GROUP",
+ "color": "fuchsia",
+ "textColor": "black"
+ },
+ {
+ "name": "Sed ut perspiciatis",
+ "start": now,
+ "end": twoHoursFuture,
+ "type": "TEST-GROUP",
+ "color": "fuchsia",
+ "textColor": "black"
+ }
+ ]
+ })
+ }
+ };
+
+ beforeEach((done) => {
+ appHolder = document.createElement('div');
+ appHolder.style.width = '640px';
+ appHolder.style.height = '480px';
+
+ openmct = createOpenMct();
+ openmct.install(new TimelistPlugin());
+
+ timelistDefinition = openmct.types.get(TIMELIST_TYPE).definition;
+
+ element = document.createElement('div');
+ element.style.width = '640px';
+ element.style.height = '480px';
+ child = document.createElement('div');
+ child.style.width = '640px';
+ child.style.height = '480px';
+ element.appendChild(child);
+
+ originalRouterPath = openmct.router.path;
+
+ mockComposition = new EventEmitter();
+ mockComposition.load = () => {
+ mockComposition.emit('add', planObject);
+
+ return Promise.resolve([planObject]);
+ };
+
+ spyOn(openmct.composition, 'get').and.returnValue(mockComposition);
+
+ openmct.on('start', done);
+ openmct.start(appHolder);
+ });
+
+ afterEach(() => {
+ openmct.router.path = originalRouterPath;
+
+ return resetApplicationState(openmct);
+ });
+
+ let mockTimelistObject = {
+ name: 'Timelist',
+ key: TIMELIST_TYPE,
+ creatable: true
+ };
+
+ it('defines a timelist object type with the correct key', () => {
+ expect(timelistDefinition.key).toEqual(mockTimelistObject.key);
+ });
+
+ it('is creatable', () => {
+ expect(timelistDefinition.creatable).toEqual(mockTimelistObject.creatable);
+ });
+
+ describe('the timelist view', () => {
+ it('provides a timelist view', () => {
+ const testViewObject = {
+ id: "test-object",
+ type: TIMELIST_TYPE
+ };
+ openmct.router.path = [testViewObject];
+
+ const applicableViews = openmct.objectViews.get(testViewObject, [testViewObject]);
+ let timelistView = applicableViews.find((viewProvider) => viewProvider.key === 'timelist.view');
+ expect(timelistView).toBeDefined();
+ });
+ });
+
+ describe('the timelist view displays activities', () => {
+ let timelistDomainObject;
+ let timelistView;
+
+ beforeEach(() => {
+ timelistDomainObject = {
+ identifier: {
+ key: 'test-object',
+ namespace: ''
+ },
+ type: TIMELIST_TYPE,
+ id: "test-object",
+ configuration: {
+ sortOrderIndex: 0,
+ futureEventsIndex: 1,
+ futureEventsDurationIndex: 0,
+ futureEventsDuration: 0,
+ currentEventsIndex: 1,
+ currentEventsDurationIndex: 0,
+ currentEventsDuration: 0,
+ pastEventsIndex: 1,
+ pastEventsDurationIndex: 0,
+ pastEventsDuration: 0,
+ filter: ''
+ },
+ selectFile: {
+ body: JSON.stringify({
+ "TEST-GROUP": [
+ {
+ "name": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua",
+ "start": twoHoursPast,
+ "end": oneHourPast,
+ "type": "TEST-GROUP",
+ "color": "fuchsia",
+ "textColor": "black"
+ },
+ {
+ "name": "Sed ut perspiciatis",
+ "start": now,
+ "end": twoHoursFuture,
+ "type": "TEST-GROUP",
+ "color": "fuchsia",
+ "textColor": "black"
+ }
+ ]
+ })
+ }
+ };
+
+ openmct.router.path = [timelistDomainObject];
+
+ const applicableViews = openmct.objectViews.get(timelistDomainObject, [timelistDomainObject]);
+ timelistView = applicableViews.find((viewProvider) => viewProvider.key === 'timelist.view');
+ let view = timelistView.view(timelistDomainObject, []);
+ view.show(child, true);
+
+ return Vue.nextTick();
+ });
+
+ it('displays the activities', () => {
+ const items = element.querySelectorAll(LIST_ITEM_CLASS);
+ expect(items.length).toEqual(2);
+ });
+
+ it('displays the activity headers', () => {
+ const headers = element.querySelectorAll(LIST_ITEM_BODY_CLASS);
+ expect(headers.length).toEqual(4);
+ });
+
+ it('displays activity details', (done) => {
+ Vue.nextTick(() => {
+ const itemEls = element.querySelectorAll(LIST_ITEM_CLASS);
+ const itemValues = itemEls[0].querySelectorAll(LIST_ITEM_VALUE_CLASS);
+ expect(itemValues.length).toEqual(4);
+ expect(itemValues[3].innerHTML.trim()).toEqual('Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua');
+ expect(itemValues[0].innerHTML.trim()).toEqual(`${moment(twoHoursPast).format('YYYY-MM-DD HH:mm:ss:SSS')}Z`);
+ expect(itemValues[1].innerHTML.trim()).toEqual(`${moment(oneHourPast).format('YYYY-MM-DD HH:mm:ss:SSS')}Z`);
+
+ done();
+ });
+ });
+ });
+
+ describe('the timelist composition', () => {
+ let timelistDomainObject;
+ let timelistView;
+
+ beforeEach(() => {
+ timelistDomainObject = {
+ identifier: {
+ key: 'test-object',
+ namespace: ''
+ },
+ type: TIMELIST_TYPE,
+ id: "test-object",
+ configuration: {
+ sortOrderIndex: 0,
+ futureEventsIndex: 1,
+ futureEventsDurationIndex: 0,
+ futureEventsDuration: 0,
+ currentEventsIndex: 1,
+ currentEventsDurationIndex: 0,
+ currentEventsDuration: 0,
+ pastEventsIndex: 1,
+ pastEventsDurationIndex: 0,
+ pastEventsDuration: 0,
+ filter: ''
+ },
+ composition: [{
+ identifier: {
+ key: 'test-plan-object',
+ namespace: ''
+ }
+ }]
+ };
+
+ openmct.router.path = [timelistDomainObject];
+
+ const applicableViews = openmct.objectViews.get(timelistDomainObject, [timelistDomainObject]);
+ timelistView = applicableViews.find((viewProvider) => viewProvider.key === 'timelist.view');
+ let view = timelistView.view(timelistDomainObject, [timelistDomainObject]);
+ view.show(child, true);
+
+ return Vue.nextTick();
+ });
+
+ it('loads the plan from composition', () => {
+ return Vue.nextTick(() => {
+ const items = element.querySelectorAll(LIST_ITEM_CLASS);
+ expect(items.length).toEqual(2);
+ });
+ });
+ });
+
+ describe('filters', () => {
+ let timelistDomainObject;
+ let timelistView;
+
+ beforeEach(() => {
+ timelistDomainObject = {
+ identifier: {
+ key: 'test-object',
+ namespace: ''
+ },
+ type: TIMELIST_TYPE,
+ id: "test-object",
+ configuration: {
+ sortOrderIndex: 0,
+ futureEventsIndex: 1,
+ futureEventsDurationIndex: 0,
+ futureEventsDuration: 0,
+ currentEventsIndex: 1,
+ currentEventsDurationIndex: 0,
+ currentEventsDuration: 0,
+ pastEventsIndex: 1,
+ pastEventsDurationIndex: 0,
+ pastEventsDuration: 0,
+ filter: 'perspiciatis'
+ },
+ composition: [{
+ identifier: {
+ key: 'test-plan-object',
+ namespace: ''
+ }
+ }]
+ };
+
+ openmct.router.path = [timelistDomainObject];
+
+ const applicableViews = openmct.objectViews.get(timelistDomainObject, [timelistDomainObject]);
+ timelistView = applicableViews.find((viewProvider) => viewProvider.key === 'timelist.view');
+ let view = timelistView.view(timelistDomainObject, [timelistDomainObject]);
+ view.show(child, true);
+
+ return Vue.nextTick();
+ });
+
+ it('activities', () => {
+ return Vue.nextTick(() => {
+ const items = element.querySelectorAll(LIST_ITEM_CLASS);
+ expect(items.length).toEqual(1);
+ });
+ });
+ });
+
+ describe('time filtering - past', () => {
+ let timelistDomainObject;
+ let timelistView;
+
+ beforeEach(() => {
+ timelistDomainObject = {
+ identifier: {
+ key: 'test-object',
+ namespace: ''
+ },
+ type: TIMELIST_TYPE,
+ id: "test-object",
+ configuration: {
+ sortOrderIndex: 0,
+ futureEventsIndex: 1,
+ futureEventsDurationIndex: 0,
+ futureEventsDuration: 0,
+ currentEventsIndex: 1,
+ currentEventsDurationIndex: 0,
+ currentEventsDuration: 0,
+ pastEventsIndex: 0,
+ pastEventsDurationIndex: 0,
+ pastEventsDuration: 0,
+ filter: ''
+ },
+ composition: [{
+ identifier: {
+ key: 'test-plan-object',
+ namespace: ''
+ }
+ }]
+ };
+
+ openmct.router.path = [timelistDomainObject];
+
+ const applicableViews = openmct.objectViews.get(timelistDomainObject, [timelistDomainObject]);
+ timelistView = applicableViews.find((viewProvider) => viewProvider.key === 'timelist.view');
+ let view = timelistView.view(timelistDomainObject, [timelistDomainObject]);
+ view.show(child, true);
+
+ return Vue.nextTick();
+ });
+
+ it('hides past events', () => {
+ return Vue.nextTick(() => {
+ const items = element.querySelectorAll(LIST_ITEM_CLASS);
+ expect(items.length).toEqual(1);
+ });
+ });
+ });
+});
diff --git a/src/adapter/actions/LegacyActionAdapter.js b/src/plugins/timelist/timelist.scss
index d7f80333a..eea87d114 100644
--- a/src/adapter/actions/LegacyActionAdapter.js
+++ b/src/plugins/timelist/timelist.scss
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -20,18 +20,36 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
-import LegacyContextMenuAction from './LegacyContextMenuAction';
+.c-timelist {
+ & .nowMarker.hasCurrent {
+ height: 2px;
+ position: absolute;
+ z-index: 10;
+ background: cyan;
+ width: 100%;
+ }
-export default function LegacyActionAdapter(openmct, legacyActions) {
- function contextualCategoryOnly(action) {
- if (action.category === 'contextual' || (Array.isArray(action.category) && action.category.includes('contextual'))) {
- return true;
- }
+ .c-list-item {
+ /* Time Lists */
- return false;
+ &.--is-current {
+ background-color: $colorCurrentBg;
+ border-top: 1px solid $colorCurrentBorder !important;
+ color: $colorCurrentFg;
+ font-weight: bold;
}
- legacyActions.filter(contextualCategoryOnly)
- .map(LegacyAction => new LegacyContextMenuAction(openmct, LegacyAction))
- .forEach(openmct.actions.register);
+ &.--is-future {
+ background-color: $colorFutureBg;
+ border-top-color: $colorFutureBorder !important;
+ color: $colorFutureFg;
+ }
+
+ &__value {
+ &.--duration {
+ width: 5%;
+ }
+ }
+ }
+
}
diff --git a/src/plugins/timer/TimerViewProvider.js b/src/plugins/timer/TimerViewProvider.js
index 5799e4563..376e24a0f 100644
--- a/src/plugins/timer/TimerViewProvider.js
+++ b/src/plugins/timer/TimerViewProvider.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2009-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/timer/actions/PauseTimerAction.js b/src/plugins/timer/actions/PauseTimerAction.js
index 4fa730ea4..d729e8b21 100644
--- a/src/plugins/timer/actions/PauseTimerAction.js
+++ b/src/plugins/timer/actions/PauseTimerAction.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2009-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -43,14 +43,19 @@ export default class PauseTimerAction {
this.openmct.objects.mutate(domainObject, 'configuration', newConfiguration);
}
- appliesTo(objectPath) {
+ appliesTo(objectPath, view = {}) {
const domainObject = objectPath[0];
if (!domainObject || !domainObject.configuration) {
return;
}
+ // Use object configuration timerState for viewless context menus,
+ // otherwise manually show/hide based on the view's timerState
+ const viewKey = view.key;
const { timerState } = domainObject.configuration;
- return domainObject.type === 'timer' && timerState === 'started';
+ return viewKey
+ ? domainObject.type === 'timer'
+ : domainObject.type === 'timer' && timerState === 'started';
}
}
diff --git a/src/plugins/timer/actions/RestartTimerAction.js b/src/plugins/timer/actions/RestartTimerAction.js
index a60e9e4ca..23faf4db2 100644
--- a/src/plugins/timer/actions/RestartTimerAction.js
+++ b/src/plugins/timer/actions/RestartTimerAction.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2009-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -44,14 +44,19 @@ export default class RestartTimerAction {
this.openmct.objects.mutate(domainObject, 'configuration', newConfiguration);
}
- appliesTo(objectPath) {
+ appliesTo(objectPath, view = {}) {
const domainObject = objectPath[0];
if (!domainObject || !domainObject.configuration) {
return;
}
+ // Use object configuration timerState for viewless context menus,
+ // otherwise manually show/hide based on the view's timerState
+ const viewKey = view.key;
const { timerState } = domainObject.configuration;
- return domainObject.type === 'timer' && timerState !== 'stopped';
+ return viewKey
+ ? domainObject.type === 'timer'
+ : domainObject.type === 'timer' && timerState !== 'stopped';
}
}
diff --git a/src/plugins/timer/actions/StartTimerAction.js b/src/plugins/timer/actions/StartTimerAction.js
index d925dcc60..cdd01f3e6 100644
--- a/src/plugins/timer/actions/StartTimerAction.js
+++ b/src/plugins/timer/actions/StartTimerAction.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2009-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -63,14 +63,19 @@ export default class StartTimerAction {
newConfiguration.pausedTime = undefined;
this.openmct.objects.mutate(domainObject, 'configuration', newConfiguration);
}
- appliesTo(objectPath) {
+ appliesTo(objectPath, view = {}) {
const domainObject = objectPath[0];
if (!domainObject || !domainObject.configuration) {
return;
}
+ // Use object configuration timerState for viewless context menus,
+ // otherwise manually show/hide based on the view's timerState
+ const viewKey = view.key;
const { timerState } = domainObject.configuration;
- return domainObject.type === 'timer' && timerState !== 'started';
+ return viewKey
+ ? domainObject.type === 'timer'
+ : domainObject.type === 'timer' && timerState !== 'started';
}
}
diff --git a/src/plugins/timer/actions/StopTimerAction.js b/src/plugins/timer/actions/StopTimerAction.js
index d7654f29c..6ed672bb5 100644
--- a/src/plugins/timer/actions/StopTimerAction.js
+++ b/src/plugins/timer/actions/StopTimerAction.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2009-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -44,14 +44,19 @@ export default class StopTimerAction {
this.openmct.objects.mutate(domainObject, 'configuration', newConfiguration);
}
- appliesTo(objectPath) {
+ appliesTo(objectPath, view = {}) {
const domainObject = objectPath[0];
if (!domainObject || !domainObject.configuration) {
return;
}
+ // Use object configuration timerState for viewless context menus,
+ // otherwise manually show/hide based on the view's timerState
+ const viewKey = view.key;
const { timerState } = domainObject.configuration;
- return domainObject.type === 'timer' && timerState !== 'stopped';
+ return viewKey
+ ? domainObject.type === 'timer'
+ : domainObject.type === 'timer' && timerState !== 'stopped';
}
}
diff --git a/src/plugins/timer/components/Timer.vue b/src/plugins/timer/components/Timer.vue
index ee5210229..b137649f4 100644
--- a/src/plugins/timer/components/Timer.vue
+++ b/src/plugins/timer/components/Timer.vue
@@ -1,5 +1,5 @@
<!--
- Open MCT, Copyright (c) 2009-2021, United States Government
+ Open MCT, Copyright (c) 2014-2022, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
@@ -32,10 +32,11 @@
:class="[{'hide': timerState === 'stopped' }]"
@click="restartTimer"
></button>
- <button :title="timerStateButtonText"
- class="c-timer__ctrl-pause-play c-icon-button c-icon-button--major"
- :class="[timerStateButtonIcon]"
- @click="toggleStateButton"
+ <button
+ :title="timerStateButtonText"
+ class="c-timer__ctrl-pause-play c-icon-button c-icon-button--major"
+ :class="[timerStateButtonIcon]"
+ @click="toggleStateButton"
></button>
</div>
<div
@@ -178,6 +179,15 @@ export default {
return timerSign;
}
},
+ watch: {
+ timerState() {
+ if (!this.viewActionsCollection) {
+ return;
+ }
+
+ this.showOrHideAvailableActions();
+ }
+ },
mounted() {
this.$nextTick(() => {
if (this.configuration && this.configuration.timerState === undefined) {
@@ -189,6 +199,9 @@ export default {
this.unlisten = ticker.listen(() => {
this.openmct.objects.refresh(this.domainObject);
});
+
+ this.viewActionsCollection = this.openmct.actions.getActionsCollection(this.objectPath, this.currentView);
+ this.showOrHideAvailableActions();
});
},
beforeDestroy() {
@@ -227,6 +240,22 @@ export default {
if (action) {
action.invoke(this.objectPath, this.currentView);
}
+ },
+ showOrHideAvailableActions() {
+ switch (this.timerState) {
+ case 'started':
+ this.viewActionsCollection.hide(['timer.start']);
+ this.viewActionsCollection.show(['timer.stop', 'timer.pause', 'timer.restart']);
+ break;
+ case 'paused':
+ this.viewActionsCollection.hide(['timer.pause']);
+ this.viewActionsCollection.show(['timer.stop', 'timer.start', 'timer.restart']);
+ break;
+ case 'stopped':
+ this.viewActionsCollection.hide(['timer.stop', 'timer.pause', 'timer.restart']);
+ this.viewActionsCollection.show(['timer.start']);
+ break;
+ }
}
}
};
diff --git a/src/plugins/timer/plugin.js b/src/plugins/timer/plugin.js
index a9105903d..d4398eeac 100644
--- a/src/plugins/timer/plugin.js
+++ b/src/plugins/timer/plugin.js
@@ -1,6 +1,6 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2009-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/timer/pluginSpec.js b/src/plugins/timer/pluginSpec.js
index 5d1715224..b8660345a 100644
--- a/src/plugins/timer/pluginSpec.js
+++ b/src/plugins/timer/pluginSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2009-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -67,6 +67,10 @@ describe("Timer plugin:", () => {
});
}
+ afterEach(() => {
+ return resetApplicationState(openmct);
+ });
+
describe("should still work if it's in the old format", () => {
let timerViewProvider;
let timerView;
@@ -96,6 +100,7 @@ describe("Timer plugin:", () => {
timerViewProvider = applicableViews.find(viewProvider => viewProvider.key === 'timer.view');
spyOn(openmct.objects, 'get').and.returnValue(Promise.resolve(timerViewObject));
+ spyOn(openmct.objects, 'save').and.returnValue(Promise.resolve(true));
mutableTimerObject = await openmct.objects.getMutable(timerViewObject.identifier);
@@ -173,8 +178,6 @@ describe("Timer plugin:", () => {
if (appHolder) {
appHolder.remove();
}
-
- return resetApplicationState(openmct);
});
it("has name as Timer", () => {
diff --git a/platform/commonUI/general/res/templates/controls/action-button.html b/src/plugins/userIndicator/components/UserIndicator.vue
index 9e233430b..6c6671a94 100644
--- a/platform/commonUI/general/res/templates/controls/action-button.html
+++ b/src/plugins/userIndicator/components/UserIndicator.vue
@@ -1,5 +1,5 @@
<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
+ Open MCT, Copyright (c) 2014-2022, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
@@ -19,11 +19,36 @@
this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information.
-->
-<a class="s-button key-{{parameters.action.getMetadata().key}} {{parameters.action.getMetadata().cssClass}}"
- ng-class="{ labeled: parameters.labeled }"
- title="{{parameters.action.getMetadata().description}}"
- ng-click="parameters.action.perform()">
- <span class="title-label" ng-if="parameters.labeled">
- {{parameters.action.getMetadata().name}}
+
+<template>
+<div class="c-indicator icon-person c-indicator--clickable">
+ <span class="label c-indicator__label">
+ {{ userName }}
</span>
-</a>
+</div>
+</template>
+
+<script>
+
+export default {
+ inject: ['openmct'],
+ data() {
+ return {
+ userName: undefined,
+ loggedIn: false
+ };
+ },
+
+ mounted() {
+ this.getUserInfo();
+ },
+ methods: {
+ getUserInfo() {
+ this.openmct.user.getCurrentUser().then((user) => {
+ this.userName = user.getName();
+ this.loggedIn = this.openmct.user.isLoggedIn();
+ });
+ }
+ }
+};
+</script>
diff --git a/platform/core/test/services/NowSpec.js b/src/plugins/userIndicator/plugin.js
index b083fd453..25afad9eb 100644
--- a/platform/core/test/services/NowSpec.js
+++ b/src/plugins/userIndicator/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -20,28 +20,37 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
-define(
- ["../../src/services/Now"],
- function (Now) {
+import UserIndicator from './components/UserIndicator.vue';
+import Vue from 'vue';
- describe("The 'now' service", function () {
- var now = new Now();
+export default function UserIndicatorPlugin() {
- it("reports system time", function () {
- var a = Date.now(),
- b = now(),
- c = Date.now();
-
- // Clock could, in principle, tick between evaluating the
- // expressions above. We can't predict or prevent this but
- // want the test to be stable, so we only verify that now()
- // returns a value that makes sense given a previous and
- // subsequent measurement from Date.now()
- expect(a <= b).toBeTruthy();
- expect(b <= c).toBeTruthy();
- expect(b).toBeDefined();
- });
+ function addIndicator(openmct) {
+ const userIndicator = new Vue ({
+ components: {
+ UserIndicator
+ },
+ provide: {
+ openmct: openmct
+ },
+ template: '<UserIndicator />'
+ });
+ openmct.indicators.add({
+ key: 'user-indicator',
+ element: userIndicator.$mount().$el,
+ priority: openmct.priority.HIGH
});
}
-);
+
+ return function install(openmct) {
+ if (openmct.user.hasProvider()) {
+ addIndicator(openmct);
+ } else {
+ // back up if user provider added after indicator installed
+ openmct.user.on('providerAdded', () => {
+ addIndicator(openmct);
+ });
+ }
+ };
+}
diff --git a/src/plugins/userIndicator/pluginSpec.js b/src/plugins/userIndicator/pluginSpec.js
new file mode 100644
index 000000000..67492f2f4
--- /dev/null
+++ b/src/plugins/userIndicator/pluginSpec.js
@@ -0,0 +1,100 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * 'License'); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+import {
+ createOpenMct,
+ resetApplicationState
+} from 'utils/testing';
+import Vue from 'vue';
+import ExampleUserProvider from '../../../example/exampleUser/ExampleUserProvider';
+
+const USERNAME = 'Coach McGuirk';
+
+describe('The User Indicator plugin', () => {
+ let openmct;
+ let element;
+ let child;
+ let appHolder;
+ let userIndicator;
+ let provider;
+
+ beforeEach((done) => {
+ appHolder = document.createElement('div');
+ appHolder.style.width = '640px';
+ appHolder.style.height = '480px';
+ document.body.appendChild(appHolder);
+
+ element = document.createElement('div');
+ child = document.createElement('div');
+ element.appendChild(child);
+
+ openmct = createOpenMct();
+ openmct.on('start', done);
+ openmct.start(appHolder);
+ });
+
+ afterEach(() => {
+ return resetApplicationState(openmct);
+ });
+
+ it('will not show, if there is no user provider', () => {
+ userIndicator = openmct.indicators.indicatorObjects
+ .find(indicator => indicator.key === 'user-indicator');
+
+ expect(userIndicator).toBe(undefined);
+ });
+
+ describe('with a user provider installed', () => {
+
+ beforeEach(() => {
+ provider = new ExampleUserProvider(openmct);
+ provider.autoLogin(USERNAME);
+
+ openmct.user.setProvider(provider);
+
+ return Vue.nextTick();
+ });
+
+ it('exists', () => {
+ userIndicator = openmct.indicators.indicatorObjects
+ .find(indicator => indicator.key === 'user-indicator').element;
+
+ const hasClockIndicator = userIndicator !== null && userIndicator !== undefined;
+ expect(hasClockIndicator).toBe(true);
+ });
+
+ it('contains the logged in user name', (done) => {
+ openmct.user.getCurrentUser().then(async (user) => {
+ await Vue.nextTick();
+
+ userIndicator = openmct.indicators.indicatorObjects
+ .find(indicator => indicator.key === 'user-indicator').element;
+
+ const userName = userIndicator.textContent.trim();
+
+ expect(user.name).toEqual(USERNAME);
+ expect(userName).toContain(USERNAME);
+ }).finally(done);
+ });
+
+ });
+});
diff --git a/src/plugins/utcTimeSystem/LocalClock.js b/src/plugins/utcTimeSystem/LocalClock.js
index d71262e87..41e4d4a3d 100644
--- a/src/plugins/utcTimeSystem/LocalClock.js
+++ b/src/plugins/utcTimeSystem/LocalClock.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT Web, Copyright (c) 2014-2015, United States Government
+ * Open MCT Web, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/utcTimeSystem/UTCTimeFormat.js b/src/plugins/utcTimeSystem/UTCTimeFormat.js
index bcf7ee805..14a4362ca 100644
--- a/src/plugins/utcTimeSystem/UTCTimeFormat.js
+++ b/src/plugins/utcTimeSystem/UTCTimeFormat.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2016, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/utcTimeSystem/UTCTimeSystem.js b/src/plugins/utcTimeSystem/UTCTimeSystem.js
index e092b6454..a1ed6e3b5 100644
--- a/src/plugins/utcTimeSystem/UTCTimeSystem.js
+++ b/src/plugins/utcTimeSystem/UTCTimeSystem.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT Web, Copyright (c) 2014-2015, United States Government
+ * Open MCT Web, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/utcTimeSystem/plugin.js b/src/plugins/utcTimeSystem/plugin.js
index 94b4e50f9..24484b818 100644
--- a/src/plugins/utcTimeSystem/plugin.js
+++ b/src/plugins/utcTimeSystem/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT Web, Copyright (c) 2014-2015, United States Government
+ * Open MCT Web, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/utcTimeSystem/pluginSpec.js b/src/plugins/utcTimeSystem/pluginSpec.js
index 0a1f0f023..0422e95d5 100644
--- a/src/plugins/utcTimeSystem/pluginSpec.js
+++ b/src/plugins/utcTimeSystem/pluginSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/viewDatumAction/ViewDatumAction.js b/src/plugins/viewDatumAction/ViewDatumAction.js
index 82d87e7fb..9222be93d 100644
--- a/src/plugins/viewDatumAction/ViewDatumAction.js
+++ b/src/plugins/viewDatumAction/ViewDatumAction.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/viewDatumAction/plugin.js b/src/plugins/viewDatumAction/plugin.js
index d5822c394..91f1ddf3b 100644
--- a/src/plugins/viewDatumAction/plugin.js
+++ b/src/plugins/viewDatumAction/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/viewDatumAction/pluginSpec.js b/src/plugins/viewDatumAction/pluginSpec.js
index 81f6a873f..8133e8413 100644
--- a/src/plugins/viewDatumAction/pluginSpec.js
+++ b/src/plugins/viewDatumAction/pluginSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/viewLargeAction/plugin.js b/src/plugins/viewLargeAction/plugin.js
index 0da5a29ae..9c4f7d6c5 100644
--- a/src/plugins/viewLargeAction/plugin.js
+++ b/src/plugins/viewLargeAction/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/viewLargeAction/viewLargeAction.js b/src/plugins/viewLargeAction/viewLargeAction.js
index 810522871..9d5891fff 100644
--- a/src/plugins/viewLargeAction/viewLargeAction.js
+++ b/src/plugins/viewLargeAction/viewLargeAction.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -38,41 +38,41 @@ export default class ViewLargeAction {
}
invoke(objectPath, view) {
- const parentElement = view.parentElement;
- let childElement = parentElement && parentElement.firstChild;
+ performance.mark('viewlarge.start');
+ const childElement = view?.parentElement?.firstChild;
if (!childElement) {
const message = "ViewLargeAction: missing element";
this.openmct.notifications.error(message);
throw new Error(message);
}
- this._expand(objectPath, childElement);
+ this._expand(objectPath, view);
}
- appliesTo(objectPath, view = {}) {
- const parentElement = view.parentElement;
- const element = parentElement && parentElement.firstChild;
- const viewLargeAction = element && !element.classList.contains('js-main-container')
- && !this.openmct.router.isNavigatedObject(objectPath);
+ appliesTo(objectPath, view) {
+ const childElement = view?.parentElement?.firstChild;
- return viewLargeAction;
+ return childElement && !childElement.classList.contains('js-main-container')
+ && !this.openmct.router.isNavigatedObject(objectPath);
}
- _expand(objectPath, childElement) {
- const parentElement = childElement.parentElement;
+ _expand(objectPath, view) {
+ const element = this._getPreview(objectPath, view);
this.overlay = this.openmct.overlays.overlay({
- element: this._getPreview(objectPath),
+ element,
size: 'large',
autoHide: false,
- onDestroy() {
- parentElement.append(childElement);
+ onDestroy: () => {
+ this.preview.$destroy();
+ this.preview = undefined;
+ delete this.preview;
}
});
}
- _getPreview(objectPath) {
- const preview = new Vue({
+ _getPreview(objectPath, view) {
+ this.preview = new Vue({
components: {
Preview
},
@@ -80,9 +80,14 @@ export default class ViewLargeAction {
openmct: this.openmct,
objectPath
},
- template: '<Preview></Preview>'
+ data() {
+ return {
+ view
+ };
+ },
+ template: '<Preview :existing-view="view"></Preview>'
});
- return preview.$mount().$el;
+ return this.preview.$mount().$el;
}
}
diff --git a/src/plugins/webPage/WebPageViewProvider.js b/src/plugins/webPage/WebPageViewProvider.js
index 2c305225a..f1c29e778 100644
--- a/src/plugins/webPage/WebPageViewProvider.js
+++ b/src/plugins/webPage/WebPageViewProvider.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/webPage/plugin.js b/src/plugins/webPage/plugin.js
index 2a0c381b1..517997f78 100644
--- a/src/plugins/webPage/plugin.js
+++ b/src/plugins/webPage/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/plugins/webPage/pluginSpec.js b/src/plugins/webPage/pluginSpec.js
new file mode 100644
index 000000000..eab1c968b
--- /dev/null
+++ b/src/plugins/webPage/pluginSpec.js
@@ -0,0 +1,106 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+import { createOpenMct, resetApplicationState } from "utils/testing";
+import WebPagePlugin from "./plugin";
+
+function getView(openmct, domainObj, objectPath) {
+ const applicableViews = openmct.objectViews.get(domainObj, objectPath);
+ const webpageView = applicableViews.find((viewProvider) => viewProvider.key === 'webPage');
+
+ return webpageView.view(domainObj);
+}
+
+function destroyView(view) {
+ return view.destroy();
+}
+
+describe("The web page plugin", function () {
+ let mockDomainObject;
+ let mockDomainObjectPath;
+ let openmct;
+ let element;
+ let child;
+ let view;
+
+ beforeEach((done) => {
+ mockDomainObjectPath = [
+ {
+ name: 'mock webpage',
+ type: 'webpage',
+ identifier: {
+ key: 'mock-webpage',
+ namespace: ''
+ }
+ }
+ ];
+
+ mockDomainObject = {
+ displayFormat: "",
+ name: "Unnamed WebPage",
+ type: "webPage",
+ location: "f69c21ac-24ef-450c-8e2f-3d527087d285",
+ modified: 1627483839783,
+ url: "123",
+ displayText: "123",
+ persisted: 1627483839783,
+ id: "3d9c243d-dffb-446b-8474-d9931a99d679",
+ identifier: {
+ namespace: "",
+ key: "3d9c243d-dffb-446b-8474-d9931a99d679"
+ }
+ };
+
+ openmct = createOpenMct();
+ openmct.install(new WebPagePlugin());
+
+ element = document.createElement('div');
+ element.style.width = '640px';
+ element.style.height = '480px';
+ child = document.createElement('div');
+ child.style.width = '640px';
+ child.style.height = '480px';
+ element.appendChild(child);
+
+ openmct.on('start', done);
+ openmct.startHeadless();
+
+ });
+
+ afterEach(() => {
+ destroyView(view);
+
+ return resetApplicationState(openmct);
+ });
+
+ describe('the view', () => {
+ beforeEach(() => {
+ view = getView(openmct, mockDomainObject, mockDomainObjectPath);
+ view.show(child, true);
+ });
+
+ it('provides a view', () => {
+ expect(view).toBeDefined();
+ });
+ });
+
+});
diff --git a/src/selection/Selection.js b/src/selection/Selection.js
index af1db59a4..30e1f8d3c 100644
--- a/src/selection/Selection.js
+++ b/src/selection/Selection.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/start.frag b/src/start.frag
deleted file mode 100644
index 13dfcca7d..000000000
--- a/src/start.frag
+++ /dev/null
@@ -1,45 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
- * as represented by the Administrator of the National Aeronautics and Space
- * Administration. All rights reserved.
- *
- * Open MCT is licensed under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * Open MCT includes source code licensed under additional open source
- * licenses. See the Open Source Licenses file (LICENSES.md) included with
- * this source code distribution or the Licensing information page available
- * at runtime from the About dialog for additional information.
- *
- * Open MCT https://nasa.github.io/openmct/
- * Version: @@version
- * Built: @@timestamp
- * Revision: @@revision
- * Branch: @@branch
- *
- * @preserve
- *****************************************************************************/
-
-(function (root, factory) {
- if (typeof define === 'function' && define.amd) {
- define([], factory);
- } else if (typeof exports === 'object') {
- module.exports = factory();
- } else {
- root.openmct = factory();
- }
-}(this, function() {
- var BUILD_CONSTANTS = {
- version: "@@version",
- timestamp: "@@timestamp",
- revision: "@@revision",
- branch: "@@branch"
- };
diff --git a/src/styles/_about.scss b/src/styles/_about.scss
index 54fb15c21..160f7cc00 100644
--- a/src/styles/_about.scss
+++ b/src/styles/_about.scss
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/styles/_constants-espresso.scss b/src/styles/_constants-espresso.scss
index a31b793ab..684a3a7fe 100644
--- a/src/styles/_constants-espresso.scss
+++ b/src/styles/_constants-espresso.scss
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -94,7 +94,7 @@ $shellPanePad: $interiorMargin, 7px;
$drawerBg: lighten($colorBodyBg, 5%);
$drawerFg: lighten($colorBodyFg, 5%);
$sideBarBg: $drawerBg;
-$sideBarHeaderBg: rgba($colorBodyFg, 0.2);
+$sideBarHeaderBg: rgba($colorBodyFg, 0.1);
$sideBarHeaderFg: rgba($colorBodyFg, 0.7);
// Status colors, mainly used for messaging and item ancillary symbols
@@ -164,7 +164,7 @@ $borderMissing: 1px dashed $colorAlert !important;
$editUIColor: $uiColor; // Base color
$editUIColorBg: $editUIColor;
$editUIColorFg: #fff;
-$editUIColorHov: pullForward(saturate($uiColor, 10%), 20%); // Hover color when $editUIColor is applied as a base color
+$editUIColorHov: pullForward(saturate($uiColor, 10%), 10%); // Hover color when $editUIColor is applied as a base color
$editUIBaseColor: #344b8d; // Base color, toolbar bg
$editUIBaseColorHov: pullForward($editUIBaseColor, 20%);
$editUIBaseColorFg: #ffffff; // Toolbar button icon colors, etc.
@@ -178,11 +178,10 @@ $editFrameColor: $browseFrameColor; // Solid or dotted border applied to non-sel
$editFrameBorder: 1px dotted $editFrameColor;
$editFrameColorHov: $editUIColor; // Solid border hover on frames; hover should not be applied to selected objects
$editFrameBorderHov: 1px solid $editFrameColorHov; // Hover on selectable frames
-$editFrameColorSelected: #ccc; // Border of selected frames
+$editFrameColorSelected: #ffefc2; // Border of selected frames while editing
$editFrameColorHandleBg: $colorBodyBg; // Resize handle 'offset' color to make handle standout
$editFrameColorHandleFg: $editFrameColorSelected; // Resize handle main color
$editFrameSelectedShdw: rgba(black, 0.4) 0 1px 5px 1px;
-$editFrameSelectedBorder: 1px solid $editFrameColorHov; // Selected frame element
$editFrameMovebarColorBg: $editFrameColor; // Movebar bg color
$editFrameMovebarColorFg: pullForward($editFrameMovebarColorBg, 20%); // Grippy lines, container size text
$editFrameHovMovebarColorBg: pullForward($editFrameMovebarColorBg, 10%); // Hover style
@@ -191,6 +190,7 @@ $editFrameSelectedMovebarColorBg: pullForward($editFrameMovebarColorBg, 15%); //
$editFrameSelectedMovebarColorFg: pullForward($editFrameMovebarColorFg, 15%);
$editFrameMovebarH: 10px; // Height of move bar in layout frame
$editMarqueeBorder: 1px dashed $editFrameColorSelected;
+$editFrameSelectedBorder: $editMarqueeBorder; // Selected frame element
// Icons
$colorIconAlias: #4af6f3;
@@ -245,7 +245,8 @@ $colorMenuHovBg: rgba($colorKey, 0.5);
$colorMenuHovFg: $colorBodyFgEm;
$colorMenuHovIc: $colorMenuHovFg;
$colorMenuElementHilite: pullForward($colorMenuBg, 10%);
-$shdwMenu: rgba(black, 0.5) 0 1px 5px;
+$shdwMenu: rgba(black, 0.8) 0 2px 10px;
+$shdwMenuInner: inset 0 0 0 1px rgba(white, 0.2);
$shdwMenuText: none;
$menuItemPad: $interiorMargin, floor($interiorMargin * 1.25);
@@ -269,7 +270,6 @@ $colorFormSectionHeaderBg: rgba(#000, 0.1);
$colorFormSectionHeaderFg: rgba($colorBodyFg, 0.8);
$colorInputBg: rgba(black, 0.2);
$colorInputFg: $colorBodyFg;
-$colorInputPlaceholder: pushBack($colorBodyFg, 20%);
$colorFormText: pushBack($colorBodyFg, 10%);
$colorInputIcon: pushBack($colorBodyFg, 25%);
$colorFieldHint: pullForward($colorBodyFg, 40%);
@@ -345,7 +345,7 @@ $shdwItemText: none;
$colorTabBorder: pullForward($colorBodyBg, 10%);
$colorTabBodyBg: $colorBodyBg;
$colorTabBodyFg: pullForward($colorBodyFg, 20%);
-$colorTabHeaderBg: rgba($colorBodyFg, 0.15);
+$colorTabHeaderBg: #575757;
$colorTabHeaderFg: $colorBodyFg;
$colorTabHeaderBorder: $colorBodyBg;
$colorTabGroupHeaderBg: pullForward($colorBodyBg, 5%);
@@ -366,6 +366,24 @@ $legendHoverValueBg: rgba($colorBodyFg, 0.2);
$legendTableHeadBg: $colorTabHeaderBg;
$colorPlotLimitLineBg: rgba($colorBodyBg, 0.2);
+// Gauges
+$colorGaugeBg: pullForward($colorBodyBg, 5%); // Gauge radial area background, meter background
+$colorGaugeValue: rgba(#fff, 0.3); // Gauge value graphic (radial sweep, bar) color
+$colorGaugeTextValue: #fff; // Radial gauge text value
+$colorGaugeMeterTextValue: $colorGaugeTextValue; // Meter text value, overlaid on value bar
+$colorGaugeRange: $colorBodyFg; // Range text color
+$colorGaugeLimitHigh: rgba($colorLimitRedBg, 0.4);
+$colorGaugeLimitLow: $colorGaugeLimitHigh;
+$transitionTimeGauge: 150ms; // CSS transition time used to smooth needle gauge and meter value transitions
+$marginGaugeMeterValue: 10%; // Margin between meter value bar and bounds edges
+// Time Strip and Lists
+$colorCurrentBg: rgba($colorStatusAlert, 0.3);
+$colorCurrentFg: pullForward($colorBodyFg, 20%);
+$colorCurrentBorder: $colorBodyBg;
+$colorFutureBg: rgba($colorKey, 0.2);
+$colorFutureFg: $colorCurrentFg;
+$colorFutureBorder: $colorCurrentBorder;
+
// Tree
$colorTreeBg: transparent;
$colorItemTreeHoverBg: rgba(#fff, 0.1);
diff --git a/src/styles/_constants-maelstrom.scss b/src/styles/_constants-maelstrom.scss
index 144f84e6c..9077592ea 100644
--- a/src/styles/_constants-maelstrom.scss
+++ b/src/styles/_constants-maelstrom.scss
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -168,7 +168,7 @@ $borderMissing: 1px dashed $colorAlert !important;
$editUIColor: $uiColor; // Base color
$editUIColorBg: $editUIColor;
$editUIColorFg: #fff;
-$editUIColorHov: pullForward(saturate($uiColor, 10%), 20%); // Hover color when $editUIColor is applied as a base color
+$editUIColorHov: pullForward(saturate($uiColor, 10%), 10%); // Hover color when $editUIColor is applied as a base color
$editUIBaseColor: #344b8d; // Base color, toolbar bg
$editUIBaseColorHov: pullForward($editUIBaseColor, 20%);
$editUIBaseColorFg: #ffffff; // Toolbar button icon colors, etc.
@@ -182,11 +182,10 @@ $editFrameColor: $browseFrameColor; // Solid or dotted border applied to non-sel
$editFrameBorder: 1px dotted $editFrameColor;
$editFrameColorHov: $editUIColor; // Solid border hover on frames; hover should not be applied to selected objects
$editFrameBorderHov: 1px solid $editFrameColorHov; // Hover on selectable frames
-$editFrameColorSelected: #ccc; // Border of selected frames
+$editFrameColorSelected: #ffefc2; // Border of selected frames while editing
$editFrameColorHandleBg: $colorBodyBg; // Resize handle 'offset' color to make handle standout
$editFrameColorHandleFg: $editFrameColorSelected; // Resize handle main color
$editFrameSelectedShdw: rgba(black, 0.4) 0 1px 5px 1px;
-$editFrameSelectedBorder: 1px solid $editFrameColorHov; // Selected frame element
$editFrameMovebarColorBg: $editFrameColor; // Movebar bg color
$editFrameMovebarColorFg: pullForward($editFrameMovebarColorBg, 20%); // Grippy lines, container size text
$editFrameHovMovebarColorBg: pullForward($editFrameMovebarColorBg, 10%); // Hover style
@@ -195,6 +194,7 @@ $editFrameSelectedMovebarColorBg: pullForward($editFrameMovebarColorBg, 15%); //
$editFrameSelectedMovebarColorFg: pullForward($editFrameMovebarColorFg, 15%);
$editFrameMovebarH: 10px; // Height of move bar in layout frame
$editMarqueeBorder: 1px dashed $editFrameColorSelected;
+$editFrameSelectedBorder: $editMarqueeBorder; // Selected frame element
// Icons
$colorIconAlias: #4af6f3;
@@ -249,7 +249,8 @@ $colorMenuHovBg: rgba($colorKey, 0.5);
$colorMenuHovFg: $colorBodyFgEm;
$colorMenuHovIc: $colorMenuHovFg;
$colorMenuElementHilite: pullForward($colorMenuBg, 10%);
-$shdwMenu: rgba(black, 0.5) 0 1px 5px;
+$shdwMenu: rgba(black, 0.8) 0 2px 10px;
+$shdwMenuInner: inset 0 0 0 1px rgba(white, 0.2);
$shdwMenuText: none;
$menuItemPad: $interiorMargin, floor($interiorMargin * 1.25);
@@ -273,7 +274,6 @@ $colorFormSectionHeaderBg: rgba(#000, 0.1);
$colorFormSectionHeaderFg: rgba($colorBodyFg, 0.8);
$colorInputBg: rgba(black, 0.2);
$colorInputFg: $colorBodyFg;
-$colorInputPlaceholder: pushBack($colorBodyFg, 20%);
$colorFormText: pushBack($colorBodyFg, 10%);
$colorInputIcon: pushBack($colorBodyFg, 25%);
$colorFieldHint: pullForward($colorBodyFg, 40%);
@@ -349,7 +349,7 @@ $shdwItemText: none;
$colorTabBorder: pullForward($colorBodyBg, 10%);
$colorTabBodyBg: $colorBodyBg;
$colorTabBodyFg: pullForward($colorBodyFg, 20%);
-$colorTabHeaderBg: rgba($colorBodyFg, 0.15);
+$colorTabHeaderBg: #575757;
$colorTabHeaderFg: $colorBodyFg;
$colorTabHeaderBorder: $colorBodyBg;
$colorTabGroupHeaderBg: pullForward($colorBodyBg, 5%);
@@ -370,6 +370,24 @@ $legendHoverValueBg: rgba($colorBodyFg, 0.2);
$legendTableHeadBg: rgba($colorBodyFg, 0.15);
$colorPlotLimitLineBg: rgba($colorBodyBg, 0.2);
+// Gauges
+$colorGaugeBg: pullForward($colorBodyBg, 5%); // Gauge radial area background, meter background
+$colorGaugeValue: rgba(#fff, 0.3); // Gauge value graphic (radial sweep, bar) color
+$colorGaugeTextValue: #fff; // Radial gauge text value
+$colorGaugeMeterTextValue: $colorGaugeTextValue; // Meter text value, overlaid on value bar
+$colorGaugeRange: $colorBodyFg; // Range text color
+$colorGaugeLimitHigh: rgba($colorLimitRedBg, 0.4);
+$colorGaugeLimitLow: $colorGaugeLimitHigh;
+$transitionTimeGauge: 150ms; // CSS transition time used to smooth needle gauge and meter value transitions
+$marginGaugeMeterValue: 10%; // Margin between meter value bar and bounds edges
+// Time Strip and Lists
+$colorCurrentBg: rgba($colorStatusAlert, 0.3);
+$colorCurrentFg: pullForward($colorBodyFg, 20%);
+$colorCurrentBorder: #fff;
+$colorFutureBg: rgba($colorKey, 0.2);
+$colorFutureFg: $colorCurrentFg;
+$colorFutureBorder: $colorCurrentBorder;
+
// Tree
$colorTreeBg: transparent;
$colorItemTreeHoverBg: rgba(#fff, 0.03);
diff --git a/src/styles/_constants-mobile.scss b/src/styles/_constants-mobile.scss
index 832f6eaaf..b9bbc40ad 100644
--- a/src/styles/_constants-mobile.scss
+++ b/src/styles/_constants-mobile.scss
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/styles/_constants-snow.scss b/src/styles/_constants-snow.scss
index f40fea3cd..1e88d4928 100644
--- a/src/styles/_constants-snow.scss
+++ b/src/styles/_constants-snow.scss
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -178,11 +178,10 @@ $editFrameColor: $browseFrameColor; // Solid or dotted border applied to non-sel
$editFrameBorder: 1px dotted $editFrameColor;
$editFrameColorHov: $editUIColor; // Solid border hover on frames; hover should not be applied to selected objects
$editFrameBorderHov: 1px solid $editFrameColorHov; // Hover on selectable frames
-$editFrameColorSelected: #333; // Border of selected frames
+$editFrameColorSelected: #ff7c00; // Border of selected frames
$editFrameColorHandleBg: $colorBodyBg; // Resize handle 'offset' color to make handle standout
$editFrameColorHandleFg: $editFrameColorSelected; // Resize handle main color
$editFrameSelectedShdw: rgba(black, 0.5) 0 1px 5px 2px;
-$editFrameSelectedBorder: 1px dashed $editFrameColorSelected; // Selected frame element
$editFrameMovebarColorBg: $editFrameColor; // Movebar bg color
$editFrameMovebarColorFg: pullForward($editFrameMovebarColorBg, 20%); // Grippy lines, container size text
$editFrameHovMovebarColorBg: pullForward($editFrameMovebarColorBg, 10%); // Hover style
@@ -191,6 +190,7 @@ $editFrameSelectedMovebarColorBg: pullForward($editFrameMovebarColorBg, 15%); //
$editFrameSelectedMovebarColorFg: pullForward($editFrameMovebarColorFg, 15%);
$editFrameMovebarH: 10px; // Height of move bar in layout frame
$editMarqueeBorder: 1px dashed $editFrameColorSelected;
+$editFrameSelectedBorder: 1px dashed $editMarqueeBorder; // Selected frame element
// Icons
$colorIconAlias: #4af6f3;
@@ -245,7 +245,8 @@ $colorMenuHovBg: $colorMenuIc;
$colorMenuHovFg: $colorMenuBg;
$colorMenuHovIc: $colorMenuBg;
$colorMenuElementHilite: darken($colorMenuBg, 10%);
-$shdwMenu: rgba(black, 0.5) 0 1px 5px;
+$shdwMenu: rgba(black, 0.8) 0 2px 10px;
+$shdwMenuInner: none;
$shdwMenuText: none;
$menuItemPad: $interiorMargin, floor($interiorMargin * 1.25);
@@ -269,7 +270,6 @@ $colorFormSectionHeaderBg: rgba(#000, 0.05);
$colorFormSectionHeaderFg: rgba($colorBodyFg, 0.5);
$colorInputBg: $colorGenBg;
$colorInputFg: $colorBodyFg;
-$colorInputPlaceholder: pushBack($colorBodyFg, 20%);
$colorFormText: pushBack($colorBodyFg, 10%);
$colorInputIcon: pushBack($colorBodyFg, 25%);
$colorFieldHint: pullForward($colorBodyFg, 40%);
@@ -345,7 +345,7 @@ $shdwItemText: none;
$colorTabBorder: pullForward($colorBodyBg, 10%);
$colorTabBodyBg: $colorBodyBg;
$colorTabBodyFg: pullForward($colorBodyFg, 20%);
-$colorTabHeaderBg: rgba($colorBodyFg, 0.2);
+$colorTabHeaderBg: #e2e2e2;
$colorTabHeaderFg: $colorBodyFg;
$colorTabHeaderBorder: $colorBodyBg;
$colorTabGroupHeaderBg: pullForward($colorBodyBg, 5%);
@@ -366,6 +366,24 @@ $legendHoverValueBg: rgba($colorBodyFg, 0.2);
$legendTableHeadBg: rgba($colorBodyFg, 0.15);
$colorPlotLimitLineBg: rgba($colorBodyBg, 0.4);
+// Gauges
+$colorGaugeBg: pullForward($colorBodyBg, 20%); // Gauge radial area background, meter background
+$colorGaugeValue: rgba(#000, 0.3); // Gauge value graphic (radial sweep, bar) color
+$colorGaugeTextValue: pullForward($colorBodyFg, 20%); // Radial gauge text value
+$colorGaugeMeterTextValue: $colorGaugeTextValue; // Meter text value, overlaid on value bar
+$colorGaugeRange: $colorBodyFg; // Range text color
+$colorGaugeLimitHigh: rgba($colorLimitRedBg, 0.2);
+$colorGaugeLimitLow: $colorGaugeLimitHigh;
+$transitionTimeGauge: 150ms; // CSS transition time used to smooth needle gauge and meter value transitions
+$marginGaugeMeterValue: 10%; // Margin between meter value bar and bounds edges
+// Time Strip and Lists
+$colorCurrentBg: rgba($colorStatusAlert, 0.3);
+$colorCurrentFg: pullForward($colorBodyFg, 20%);
+$colorCurrentBorder: #fff;
+$colorFutureBg: rgba($colorKey, 0.2);
+$colorFutureFg: $colorCurrentFg;
+$colorFutureBorder: $colorCurrentBorder;
+
// Tree
$colorTreeBg: transparent;
$colorItemTreeHoverBg: rgba(black, 0.07);
diff --git a/src/styles/_constants.scss b/src/styles/_constants.scss
index 4d9096714..8164a49c7 100755
--- a/src/styles/_constants.scss
+++ b/src/styles/_constants.scss
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -156,6 +156,13 @@ $glyph-icon-notebook-page: '\e92c';
$glyph-icon-unlocked: '\e92d';
$glyph-icon-circle: '\e92e';
$glyph-icon-draft: '\e92f';
+$glyph-icon-circle-slash: '\e930';
+$glyph-icon-question-mark: '\e931';
+$glyph-icon-status-poll-check: '\e932';
+$glyph-icon-status-poll-caution: '\e933';
+$glyph-icon-status-poll-circle-slash: '\e934';
+$glyph-icon-status-poll-question-mark: '\e935';
+$glyph-icon-status-poll-edit: '\e936';
$glyph-icon-arrows-right-left: '\ea00';
$glyph-icon-arrows-up-down: '\ea01';
$glyph-icon-bullet: '\ea02';
@@ -263,6 +270,9 @@ $glyph-icon-telemetry-aggregate: '\eb2b';
$glyph-icon-bar-chart: '\eb2c';
$glyph-icon-map: '\eb2d';
$glyph-icon-plan: '\eb2e';
+$glyph-icon-timelist: '\eb2f';
+$glyph-icon-notebook-shift-log: '\eb31';
+$glyph-icon-plot-scatter: '\eb30';
/************************** GLYPHS AS DATA URI */
// Only objects have been converted, for use in Create menu and folder views
@@ -270,9 +280,6 @@ $bg-icon-alert-rect: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://
$bg-icon-alert-triangle: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath d='M499.1 424.4L287.8 54.6c-17.5-30.6-46-30.6-63.5 0L12.9 424.4C-4.6 455 9.9 480 45.1 480h421.7c35.3 0 49.8-25 32.3-55.6zM288 448h-64v-64h64v64zm10.9-192L280 352h-48l-18.9-96V128H299v128z' fill='%23000000'/%3e%3c/svg%3e");
$bg-icon-bell: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cg fill='%23000000'%3e%3cpath d='M256 512c53 0 96-43 96-96H160c0 53 43 96 96 96zM448 224v-32C448 86 362 0 256 0S64 86 64 192v32c0 35.3-28.7 64-64 64v64h512v-64c-35.3 0-64-28.7-64-64z'/%3e%3c/g%3e%3c/svg%3e");
$bg-icon-info: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath d='M256 0C114.6 0 0 114.6 0 256s114.6 256 256 256 256-114.6 256-256S397.4 0 256 0zm0 64c35.3 0 64 28.7 64 64s-28.7 64-64 64-64-28.7-64-64 28.7-64 64-64zm96 352H160v-64h32V224h128v128h32v64z' fill='%23000000'/%3e%3c/svg%3e");
-$bg-icon-activity: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23000000' d='M288 32H160l160 160H174.872C152.74 153.742 111.377 128 64 128H0v256h64c47.377 0 88.74-25.742 110.872-64H320L160 480h128l224-224L288 32z'/%3e%3c/svg%3e");
-$bg-icon-activity-mode: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23000000' d='M256 0C148.6 0 56.6 66.2 18.6 160H64c28.4 0 54 12.4 71.5 32H256l-96-96h128l160 160-160 160H160l96-96H135.5C118 339.6 92.4 352 64 352H18.6c38 93.8 129.9 160 237.4 160 141.4 0 256-114.6 256-256S397.4 0 256 0z'/%3e%3c/svg%3e");
-$bg-icon-autoflow-tabular: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23000000' d='M96 0C43.2 0 0 43.2 0 96v320c0 52.8 43.2 96 96 96h32V0H96zM192 0h128v512H192zM416 0h-32v352h128V96c0-52.8-43.2-96-96-96z'/%3e%3c/svg%3e");
$bg-icon-plus: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23000000' d='M480,192H320V32A32.1,32.1,0,0,0,288,0H224a32.1,32.1,0,0,0-32,32V192H32A32.1,32.1,0,0,0,0,224v64a32.1,32.1,0,0,0,32,32H192V480a32.1,32.1,0,0,0,32,32h64a32.1,32.1,0,0,0,32-32V320H480a32.1,32.1,0,0,0,32-32V224A32.1,32.1,0,0,0,480,192Z' transform='translate(0)'/%3e%3c/svg%3e");
$bg-icon-grippy-ew: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath d='M416 0v512h-64V0zM288 0v512h-64V0zM160 0v512H96V0z'/%3e%3c/svg%3e");
$bg-icon-chain-links: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath d='M479.2 32.8C457.3 10.9 428.7 0 400 0c-28.7 0-57.3 10.9-79.2 32.8l-64 64c-37 37-42.7 93.5-17 136.5l-6.4 6.4C215.7 229.3 195.9 224 176 224c-28.7 0-57.3 10.9-79.2 32.8l-64 64c-43.7 43.7-43.7 114.7 0 158.4C54.7 501.1 83.3 512 112 512c28.7 0 57.3-10.9 79.2-32.8l64-64c37-37 42.7-93.5 17-136.5l6.4-6.4c17.6 10.5 37.5 15.8 57.3 15.8 28.7 0 57.3-10.9 79.2-32.8l64-64c43.8-43.8 43.8-114.8.1-158.5zM209.9 369.9l-64 64c-9 9.1-21.1 14.1-33.9 14.1-12.8 0-24.9-5-33.9-14.1-18.7-18.7-18.7-49.2 0-67.9l64-64c9.1-9.1 21.1-14.1 33.9-14.1 2.8 0 5.6.3 8.4.7l-27.8 27.8c-5.2 5.2-8.1 12.1-8.1 19.4s2.9 14.3 8.1 19.4c5.2 5.2 12.1 8.1 19.4 8.1s14.3-2.9 19.4-8.1l27.8-27.8c2.7 15.2-1.8 31.1-13.3 42.5zm224-224l-64 64c-9 9.1-21.1 14.1-33.9 14.1-2.8 0-5.6-.3-8.4-.7l27.8-27.8c5.2-5.2 8.1-12.1 8.1-19.4s-2.9-14.3-8.1-19.4c-5.2-5.2-12.1-8.1-19.4-8.1s-14.3 2.9-19.4 8.1l-27.8 27.8c-2.6-14.9 1.8-30.8 13.3-42.3l64-64C375.1 69 387.2 64 400 64s24.9 5 33.9 14.1c18.8 18.7 18.8 49.1 0 67.8z'/%3e%3c/svg%3e");
@@ -295,19 +302,17 @@ $bg-icon-session: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www
$bg-icon-tabular: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23000000' d='M448 0H64C28.8 0 0 28.8 0 64v384c0 35.2 28.8 64 64 64h384c35.2 0 64-28.8 64-64V64c0-35.2-28.8-64-64-64zM320 224H192v-96h128v96zm-128 32h128v96H192v-96zm-32 96H32v-96h128v96zm0-224v96H32v-96h128zM64 480c-8.5 0-16.5-3.3-22.6-9.4S32 456.5 32 448v-64h128v96H64zm128 0v-96h128v96H192zm288-32c0 8.5-3.3 16.5-9.4 22.6S456.5 480 448 480h-96v-96h128v64zm0-96H352v-96h128v96zm0-128H352v-96h128v96z'/%3e%3c/svg%3e");
$bg-icon-tabular-lad: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23000000' d='M448 0H64C28.7.1.1 28.7 0 64v384c.1 35.3 28.7 63.9 64 64h384c35.3-.1 63.9-28.7 64-64V64c-.1-35.3-28.7-63.9-64-64zM32 128h128v96H32v-96zm0 128h128v96H32v-96zm32 224c-17.6-.1-31.9-14.4-32-32v-64h128v96H64zm128 0v-96h128v96H192zm288-32c-.1 17.6-14.4 31.9-32 32h-96v-96h128v64zm0-192v96H192v-96h32v-32h-32v-96h288v96h-32v32h32z'/%3e%3cpath fill='%23000000' d='M391.2 273.7L336 246.1V160c0-8.8-7.2-16-16-16s-16 7.2-16 16v105.9l72.8 36.4c7.9 4 17.5.8 21.5-7.2 4-7.8.8-17.5-7.1-21.4z'/%3e%3c/svg%3e");
$bg-icon-tabular-lad-set: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23000000' d='M64 384V96c-35.3.1-63.9 28.7-64 64v288c.1 35.3 28.7 63.9 64 64h288c35.3-.1 63.9-28.7 64-64H128c-35.3-.1-63.9-28.7-64-64z'/%3e%3cpath fill='%23000000' d='M448 0H160c-35.3.1-63.9 28.7-64 64v288c.1 35.3 28.7 63.9 64 64h288c35.3-.1 63.9-28.7 64-64V64c-.1-35.3-28.7-63.9-64-64zM128 96h96v64h-96V96zm0 96h96v96h-96v-96zm32 192c-17.6-.1-31.9-14.4-32-32v-32h96v64h-64zm96 0v-64h96v64h-96zm224-32c-.1 17.6-14.4 31.9-32 32h-64v-64h96v32zm0-64H256V96h224v192z'/%3e%3cpath fill='%23000000' d='M416 240c8.8 0 16-7.2 16-16 0-6.9-4.4-13-10.9-15.2L384 196.5V144c0-8.8-7.2-16-16-16s-16 7.2-16 16v75.5l58.9 19.6c1.7.6 3.4.9 5.1.9z'/%3e%3c/svg%3e");
-$bg-icon-tabular-realtime: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23000000' d='M0 64v384c0 35.2 28.8 64 64 64h288c35.2 0 64-28.8 64-64V340c-19.8 7.8-41.4 12-64 12-35.4 0-68.4-10.5-96-28.6V352h-96v-96h35.3c-5.2-10.1-9.4-20.8-12.6-32H160v-96h22.7C203.6 54.2 271.6 0 352 0H64C28.8 0 0 28.8 0 64zm288 320h96v64c0 8.5-3.3 16.5-9.4 22.6S360.5 480 352 480h-64v-96zm-160 96H64c-8.5 0-16.5-3.3-22.6-9.4S32 456.5 32 448v-64h96v96zm0-128H32v-96h96v96zm32 32h96v96h-96v-96zm-32-160H32v-96h96v96z'/%3e%3cpath fill='%23000000' d='M192 160c0 88.4 71.6 160 160 160s160-71.6 160-160S440.4 0 352 0 192 71.6 192 160zm49.7 39.8L227 187.5c-1.4-6.4-2.3-12.9-2.7-19.6 15.1-.1 30.1-5 41.9-14.8l39.6-33c7.5-6.2 21.1-6.2 28.6 0l39.6 33c2.8 2.3 5.7 4.3 8.8 6.1-23-11.7-52.7-9.2-72.8 7.5l-39.6 33c-7.6 6.3-21.2 6.3-28.7.1zM352 288c-36.7 0-69.7-15.4-93-40.1 14.2-.6 28.1-5.5 39.2-14.7l39.6-33c7.5-6.2 21.1-6.2 28.6 0l39.6 33c11 9.2 25 14.1 39.2 14.7-23.5 24.7-56.5 40.1-93.2 40.1zm125.9-151.3c1.4 7.5 2.1 15.3 2.1 23.3 0 9.4-1 18.6-3 27.5l-14.7 12.3c-7.5 6.2-21.1 6.2-28.6 0l-39.6-33c-2.8-2.3-5.7-4.3-8.8-6.1 23 11.7 52.7 9.2 72.8-7.5l19.8-16.5zM352 32c46.4 0 87.1 24.7 109.5 61.7l-31.2 26c-7.5 6.2-21.1 6.2-28.6 0l-39.6-33c-23.6-19.7-60.6-19.7-84.3 0l-39.6 33c-2.5 2.1-5.7 3.5-9.1 4.2C244.7 70.8 293.8 32 352 32z'/%3e%3c/svg%3e");
$bg-icon-tabular-scrolling: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23000000' d='M32 0C14.4 0 0 14.4 0 32v96h224V0H32zM512 128V32c0-17.6-14.4-32-32-32H288v128h224zM0 192v96c0 17.6 14.4 32 32 32h192V192H0zM480 320c17.6 0 32-14.4 32-32v-96H288v128h192zM256 512L128 384h256z'/%3e%3c/svg%3e");
$bg-icon-telemetry: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23000000' d='M16 315.83c7.14-2.81 27.22-23.77 46.48-73C83.71 188.64 120.64 124 176 124c26.2 0 50.71 14.58 72.85 43.34 18.67 24.25 32.42 54.46 40.67 75.54 19.26 49.19 39.34 70.15 46.48 73 7.14-2.81 27.22-23.77 46.48-73 18.7-47.75 49.57-103.57 94.47-116.23A255.87 255.87 0 0 0 256 0C114.62 0 0 114.62 0 256a257.18 257.18 0 0 0 5 50.52c4.77 5.39 8.61 8.37 11 9.31z'/%3e%3cpath fill='%23000000' d='M496 196.17c-7.14 2.81-27.22 23.76-46.48 73C428.29 323.36 391.36 388 336 388c-26.2 0-50.71-14.58-72.85-43.34-18.67-24.25-32.42-54.46-40.67-75.54-19.26-49.19-39.34-70.15-46.48-73-7.14 2.81-27.22 23.76-46.48 73-18.7 47.75-49.57 103.57-94.47 116.23A255.87 255.87 0 0 0 256 512c141.38 0 256-114.62 256-256a257.18 257.18 0 0 0-5-50.52c-4.77-5.39-8.61-8.37-11-9.31z'/%3e%3c/svg%3e");
$bg-icon-timeline: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23000000' d='M416 0H96C43.2 0 0 43.2 0 96v320c0 52.8 43.2 96 96 96h320c52.8 0 96-43.2 96-96V96c0-52.8-43.2-96-96-96ZM64 160V96h128v64Zm64 64h192v64H128Zm320 192H224v-64h224Zm0-128h-64v-64h64Zm0-128H256V96h192Z'/%3e%3c/svg%3e");
$bg-icon-timer: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23000000' d='M288 73.3V32.01a32 32 0 0 0-32-32h-64a32 32 0 0 0-32 32V73.3C67.48 100.84 0 186.54 0 288.01c0 123.71 100.29 224 224 224s224-100.29 224-224c0-101.48-67.5-187.2-160-214.71zm-54 224.71l-131.88 105.5A167.4 167.4 0 0 1 56 288.01c0-92.64 75.36-168 168-168 3.36 0 6.69.11 10 .31v177.69z'/%3e%3c/svg%3e");
-$bg-icon-topic: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23000000' d='M227.18 238.32l43.15-43.15a25.18 25.18 0 0 1 35.36 0l43.15 43.15a94.42 94.42 0 0 0 35.18 22.25V174.5l-28.82-28.82a95.11 95.11 0 0 0-134.35 0l-43.15 43.15a25.18 25.18 0 0 1-35.36 0L128 174.5v86.07a95.11 95.11 0 0 0 99.18-22.25z'/%3e%3cpath fill='%23000000' d='M252.82 273.68l-43.15 43.15a25.18 25.18 0 0 1-35.36 0l-43.15-43.15c-1-1-2.1-2-3.18-3v98.68a95.11 95.11 0 0 0 131.18-3l43.15-43.15a25.18 25.18 0 0 1 35.36 0l43.15 43.15c1 1 2.1 2 3.18 3v-98.68a95.11 95.11 0 0 0-131.18 3z'/%3e%3cpath fill='%23000000' d='M416 0h-64v96h63.83l.17.17v319.66l-.17.17H352v96h64c52.8 0 96-43.2 96-96V96c0-52.8-43.2-96-96-96zM160 416H96.17l-.17-.17V96.17l.17-.17H160V0H96C43.2 0 0 43.2 0 96v320c0 52.8 43.2 96 96 96h64v-96z'/%3e%3c/svg%3e");
$bg-icon-box-with-dashed-lines: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23000000' d='M0 192h64v128H0zM64 64.11l.11-.11H160V0H64A64.19 64.19 0 0 0 0 64v96h64V64.11zM64 447.89V352H0v96a64.19 64.19 0 0 0 64 64h96v-64H64.11zM192 0h128v64H192zM448 447.89l-.11.11H352v64h96a64.19 64.19 0 0 0 64-64v-96h-64v95.89zM448 0h-96v64h95.89l.11.11V160h64V64a64.19 64.19 0 0 0-64-64zM448 192h64v128h-64zM192 448h128v64H192zM128 128h256v256H128z'/%3e%3c/svg%3e");
-$bg-icon-summary-widget: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23000000' d='M448 0H64C28.8 0 0 28.8 0 64v384c0 35.2 28.8 64 64 64h384c35.2 0 64-28.8 64-64V64c0-35.2-28.8-64-64-64zm-24.1 305.2l-41.3 71.6-94.8-65.8 9.6 115h-82.7l9.6-115-94.8 65.8-41.3-71.6L192.5 256 88.1 206.8l41.3-71.6 94.8 65.8-9.6-115h82.7l-9.6 115 94.8-65.8 41.3 71.6L319.5 256l104.4 49.2z'/%3e%3c/svg%3e");
-$bg-icon-notebook: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23000000' d='M448 55.4c0-39.9-27.7-63.7-61.5-52.7L0 128h448V55.4zM448 160H0v288c0 35.2 28.8 64 64 64h384c35.2 0 64-28.8 64-64V224c0-35.2-28.8-64-64-64zm-32 256H224V256h192v160z'/%3e%3c/svg%3e");
+$bg-icon-summary-widget: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23000000' d='M416 0H96C43.2 0 0 43.2 0 96v320c0 52.8 43.2 96 96 96h320c52.8 0 96-43.2 96-96V96c0-52.8-43.2-96-96-96zM256 384L64 256l192-128 192 128z'/%3e%3c/svg%3e");
+$bg-icon-notebook: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512' xml:space='preserve'%3e%3cpath d='M448 55.4c0-39.9-27.7-63.7-61.5-52.7L0 128h448V55.4zM448 160H0v288c0 35.2 28.8 64 64 64h384c35.2 0 64-28.8 64-64V224c0-35.2-28.8-64-64-64zm-32 256H224V256h192v160z'/%3e%3c/svg%3e");
$bg-icon-tabs-view: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23000000' d='M0 448a64.2 64.2 0 0 0 64 64h384a64.2 64.2 0 0 0 64-64V144H256L230.9 31.2C227.1 14.1 209.6 0 192 0H64A64.2 64.2 0 0 0 0 64zm416-64H96V256h320z'/%3e%3cpath d='M240 0c17.6 0 35.1 14.1 38.9 31.2l18 80.8H512V64a64.2 64.2 0 0 0-64-64z'/%3e%3c/svg%3e");
$bg-icon-flexible-layout: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath d='M0 416c0 52.8 43.2 96 96 96h32V224H0zM0 96v64h128V0H96C43.2 0 0 43.2 0 96zM384 512h32c52.8 0 96-43.2 96-96v-64H384zM192 0h128v512H192zM416 0h-32v288h128V96c0-52.8-43.2-96-96-96z'/%3e%3c/svg%3e");
$bg-icon-generator-telemetry: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23000000' d='M76 236.9c5.4-2.1 20.4-17.8 34.9-54.7C126.8 141.5 154.5 93 196 93c19.7 0 38 10.9 54.6 32.5 14 18.2 24.4 40.8 30.5 56.7 14.5 36.9 29.5 52.6 34.9 54.7 5.4-2.1 20.4-17.8 34.9-54.7S388 104.5 421.7 95A192 192 0 0 0 256 0C150 0 64 86 64 192a197.2 197.2 0 0 0 3.7 37.9c3.6 4 6.5 6.3 8.3 7zM442.3 238.5A192.9 192.9 0 0 0 448 192a197.2 197.2 0 0 0-3.7-37.9c-3.6-4-6.5-6.3-8.3-7-5.4 2.1-20.4 17.8-34.9 54.7-10.9 27.9-27.3 59.5-50 76.6z'/%3e%3cpath d='M256 320l67.5-29.5a60.3 60.3 0 0 1-7.5.5c-19.7 0-38-10.9-54.6-32.5-14-18.2-24.4-40.8-30.5-56.7-14.5-36.9-29.5-52.6-34.9-54.7-5.4 2.1-20.4 17.8-34.9 54.7-8.2 21.1-19.6 44.2-34.4 61.6z'/%3e%3cpath d='M512 240L256 352 0 240v160l256 112 256-112V240z'/%3e%3c/svg%3e");
-$bg-icon-generator-events: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23000000' d='M160 96h192v32H160zM160 224h192v32H160zM160 160h160v32H160z'/%3e%3cpath d='M128 64.1h256V264l64-28V64a64.2 64.2 0 0 0-64-64H128a64.2 64.2 0 0 0-64 64v172l64 28zM329.1 288H182.9l73.1 32 73.1-32z'/%3e%3cpath d='M256 352L0 240v160l256 112 256-112V240L256 352z'/%3e%3c/svg%3e");
+$bg-icon-generator-events: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath d='M160 96h192v32H160zM160 224h192v32H160zM160 160h160v32H160z'/%3e%3cpath d='M128 64.1h256V264l64-28V64a64.2 64.2 0 0 0-64-64H128a64.2 64.2 0 0 0-64 64v172l64 28zM329.1 288H182.9l73.1 32 73.1-32z'/%3e%3cpath d='M256 352L0 240v160l256 112 256-112V240L256 352z'/%3e%3c/svg%3e");
$bg-icon-gauge: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath d='M256 0C114.6 0 0 114.6 0 256c0 113.2 73.5 209.2 175.3 243L304 256v251.5C422.4 485 512 381 512 256 512 114.6 397.4 0 256 0zm121.4 263.9a159.8 159.8 0 0 0-242.8 0l-73-62.5c4.3-5 8.7-9.8 13.4-14.4a255.9 255.9 0 0 1 362 0c4.7 4.6 9.1 9.4 13.4 14.4z'/%3e%3c/svg%3e");
$bg-icon-spectra: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23000000' d='M384 352H128l51.2-89.6L0 288v127c0 53.3 43.7 97 97 97h318c53.4 0 97-43.7 97-97v-31l-162.9-93.1zM415 0H97C43.7 0 0 43.6 0 97v159l200-30.1 56-97.9 54.9 96H512V97a97.2 97.2 0 00-97-97zM512 320v-32l-192-32 192 64z'/%3e%3c/svg%3e");
$bg-icon-spectra-telemetry: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23000000' d='M256 128l54.9 96H510C494.3 97.7 386.5 0 256 0 114.6 0 0 114.6 0 256l200-30.1zM384 352H128l51.2-89.6L2 287.7C17.6 414.1 125.4 512 256 512c100.8 0 188-58.3 229.8-143l-136.7-78.1zM320 256l192 64v-32l-192-32z'/%3e%3c/svg%3e");
@@ -317,3 +322,6 @@ $bg-icon-condition-widget: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='h
$bg-icon-bar-chart: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23000000' d='M416 0H96C43.2 0 0 43.2 0 96v320c0 52.8 43.2 96 96 96h320c52.8 0 96-43.2 96-96V96c0-52.8-43.2-96-96-96ZM133.82 448H64V224h69.82Zm104.73 0h-69.82V64h69.82Zm104.72 0h-69.82V288h69.82ZM448 448h-69.82V128H448Z'/%3e%3c/svg%3e");
$bg-icon-map: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23000000' d='M448 32.7 384 64v448l64-31.3c35.2-17.21 64-60.1 64-95.3v-320c0-35.2-28.8-49.91-64-32.7ZM160 456l193.6 48.4v-448L160 8v448zM129.6.4 128 0 64 31.3C28.8 48.51 0 91.4 0 126.6v320c0 35.2 28.8 49.91 64 32.7l64-31.3 1.6.4Z'/%3e%3c/svg%3e");
$bg-icon-plan: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cg data-name='Layer 2'%3e%3cg data-name='Layer 1'%3e%3cpath fill='%23000000' d='M128 96V64a64.19 64.19 0 0 1 64-64h128a64.19 64.19 0 0 1 64 64v32Z'/%3e%3cpath fill='%23000000' d='M416 64v64H96V64c-52.8 0-96 43.2-96 96v256c0 52.8 43.2 96 96 96h320c52.8 0 96-43.2 96-96V160c0-52.8-43.2-96-96-96ZM64 288v-64h128v64Zm256 128H128v-64h192Zm128 0h-64v-64h64Zm0-128H256v-64h192Z'/%3e%3c/g%3e%3c/g%3e%3c/svg%3e");
+$bg-icon-timelist: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cg data-name='Layer 2'%3e%3cpath d='M448 0H64A64.19 64.19 0 0 0 0 64v384a64.19 64.19 0 0 0 64 64h384a64.19 64.19 0 0 0 64-64V64a64.19 64.19 0 0 0-64-64ZM213.47 266.73a24 24 0 0 1-32.2 10.74L104 238.83V128a24 24 0 0 1 48 0v81.17l50.73 25.36a24 24 0 0 1 10.74 32.2ZM448 448H288v-64h160Zm0-96H288v-64h160Zm0-96H288v-64h160Zm0-96H288V96h160Z' data-name='Layer 1'/%3e%3c/g%3e%3c/svg%3e");
+$bg-icon-plot-scatter: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cg data-name='Layer 2'%3e%3cpath d='M96 0C43.2 0 0 43.2 0 96v320c0 52.8 43.2 96 96 96h320c52.8 0 96-43.2 96-96V96c0-52.8-43.2-96-96-96ZM64 176a48 48 0 1 1 48 48 48 48 0 0 1-48-48Zm80 240a48 48 0 1 1 48-48 48 48 0 0 1-48 48Zm128-96a48 48 0 1 1 48-48 48 48 0 0 1-48 48Zm0-160a48 48 0 1 1 48-48 48 48 0 0 1-48 48Zm128 256a48 48 0 1 1 48-48 48 48 0 0 1-48 48Z' data-name='Layer 1'/%3e%3c/g%3e%3c/svg%3e");
+$bg-icon-notebook-shift-log: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath d='M448 55.36c0-39.95-27.69-63.66-61.54-52.68L0 128h448V55.36ZM448 160H0v288c0 35.2 28.8 64 64 64h384c35.2 0 64-28.8 64-64V224c0-35.2-28.8-64-64-64ZM128 416H64v-64h64v64Zm0-96H64v-64h64v64Zm320 96H192v-64h256v64Zm0-96H192v-64h256v64Z'/%3e%3c/svg%3e");
diff --git a/src/styles/_controls.scss b/src/styles/_controls.scss
index 67a884bc1..3e356036d 100644
--- a/src/styles/_controls.scss
+++ b/src/styles/_controls.scss
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -22,6 +22,71 @@
@use 'sass:math';
+/******************************************************** CONTROL-SPECIFIC MIXINS */
+@mixin menuOuter() {
+ border-radius: $basicCr;
+ box-shadow: $shdwMenuInner, $shdwMenu;
+ background: $colorMenuBg;
+ color: $colorMenuFg;
+ //filter: $filterMenu; // 2022: causing all kinds of weird visual bugs in Chrome
+ text-shadow: $shdwMenuText;
+ padding: $interiorMarginSm;
+ //box-shadow: $shdwMenu;
+ display: flex;
+ flex-direction: column;
+ position: absolute;
+ z-index: 100;
+
+ > * {
+ flex: 0 0 auto;
+ }
+}
+
+@mixin menuPositioning() {
+ display: flex;
+ flex-direction: column;
+ position: absolute;
+ z-index: 100;
+
+ > * {
+ flex: 0 0 auto;
+ }
+}
+
+@mixin menuInner() {
+ li {
+ @include cControl();
+ justify-content: start;
+ cursor: pointer;
+ display: flex;
+ padding: nth($menuItemPad, 1) nth($menuItemPad, 2);
+ transition: $transIn;
+ white-space: nowrap;
+
+ @include hover {
+ background: $colorMenuHovBg;
+ color: $colorMenuHovFg;
+ &:before {
+ color: $colorMenuHovIc;
+ }
+ }
+
+ &:not(.c-menu--no-icon &) {
+ &:before {
+ color: $colorMenuIc;
+ font-size: 1em;
+ margin-right: $interiorMargin;
+ min-width: 1em;
+ }
+
+ &:not([class*='icon']):before {
+ content: ''; // Enable :before so that menu items without an icon still indent properly
+ }
+
+ }
+ }
+}
+
/******************************************************** BUTTONS */
// Optionally can include icon in :before via markup
button {
@@ -273,6 +338,7 @@ input, textarea {
input[type=text],
input[type=search],
input[type=number],
+input[type=password],
input[type=date],
textarea {
@include reactive-input();
@@ -332,6 +398,47 @@ input[type=number]::-webkit-outer-spin-button {
// Small inputs, like small numerics
width: 40px;
}
+
+ &--autocomplete {
+ &__wrapper {
+ display: inline-flex;
+ flex-direction: row;
+ align-items: center;
+ }
+
+ &__input {
+ min-width: 100px;
+
+ // Fend off from afford-arrow
+ min-height: 2em;
+ padding-right: 2.5em !important;
+ }
+
+ &__options {
+ @include menuOuter();
+ @include menuInner();
+ display: flex;
+
+ ul {
+ flex: 1 1 auto;
+ overflow: auto;
+ }
+
+ li {
+ &:before {
+ color: var(--optionIconColor) !important;
+ font-size: 0.8em !important;
+ }
+ }
+ }
+
+ &__afford-arrow {
+ font-size: 0.8em;
+ position: absolute;
+ right: 2px;
+ z-index: 2;
+ }
+ }
}
input[type=number].c-input-number--no-spinners {
@@ -383,6 +490,10 @@ select {
&__row {
> * + * { margin-left: $interiorMargin; }
}
+
+ li {
+ white-space: nowrap;
+ }
}
/******************************************************** TABS */
@@ -469,61 +580,9 @@ select {
}
/******************************************************** MENUS */
-@mixin menuOuter() {
- border-radius: $basicCr;
- background: $colorMenuBg;
- filter: $filterMenu;
- text-shadow: $shdwMenuText;
- padding: $interiorMarginSm;
- box-shadow: $shdwMenu;
- display: flex;
- flex-direction: column;
- position: absolute;
- z-index: 100;
-
- > * {
- flex: 0 0 auto;
- }
-}
-
-@mixin menuInner() {
- li {
- @include cControl();
- justify-content: start;
- color: $colorMenuFg;
- cursor: pointer;
- display: flex;
- padding: nth($menuItemPad, 1) nth($menuItemPad, 2);
- transition: $transIn;
- white-space: nowrap;
-
- @include hover {
- background: $colorMenuHovBg;
- color: $colorMenuHovFg;
- &:before {
- color: $colorMenuHovIc;
- }
- }
-
- &:before {
- color: $colorMenuIc;
- font-size: 1em;
- margin-right: $interiorMargin;
- min-width: 1em;
- }
-
- &:not([class*='icon']):before {
- content: ''; // Enable :before so that menu items without an icon still indent properly
- }
-
- .menus-no-icon & {
- &:before { display: none; }
- }
- }
-}
-
.c-menu {
@include menuOuter();
+ @include menuPositioning();
@include menuInner();
&__section-hint {
@@ -547,6 +606,7 @@ select {
.c-super-menu {
// Two column layout, menu items on left with detail of hover element on right
@include menuOuter();
+ @include menuPositioning();
display: flex;
padding: $interiorMarginLg;
flex-direction: row;
@@ -992,6 +1052,14 @@ input[type="range"] {
display: inline-flex;
align-items: center;
}
+
+ [class*='--menus-aligned'] {
+ // Contains top level elements that hold dropdown menus
+ // Top level elements use display: contents to allow their menus to compactly align
+ // 03-18-22: used in ImageControls.vue
+ display: flex;
+ flex-direction: row;
+ }
}
.c-local-controls {
diff --git a/src/styles/_forms.scss b/src/styles/_forms.scss
index 104352fe0..08a4ceca5 100644
--- a/src/styles/_forms.scss
+++ b/src/styles/_forms.scss
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -70,8 +70,24 @@
padding: $formTBPad $formLRPad;
text-transform: uppercase;
}
+
+ &--sub-grid {
+ // 3 columns: <req> <label> <input/control>
+ display: grid;
+ grid-column-gap: $interiorMargin;
+ grid-template-columns: 20px max-content 1fr;
+ grid-row-gap: $interiorMargin;
+ margin-top: $interiorMarginLg;
+ width: max-content;
+
+ .c-form__row {
+ display: contents;
+ }
+ }
}
+
+
.c-form-row {
align-items: start;
@@ -306,39 +322,6 @@
}
}
-.autocomplete {
- input {
- width: 226px;
- padding: 5px 0px 5px 7px;
- }
- .icon-arrow-down {
- position: absolute;
- top: 8px;
- left: 210px;
- font-size: 10px;
- cursor: pointer;
- }
- .autocompleteOptions {
- border: 1px solid $colorFormLines;
- border-radius: 5px;
- width: 224px;
- max-height: 170px;
- overflow-y: auto;
- overflow-x: hidden;
- li {
- border: 1px solid $colorFormLines;
- padding: 8px 0px 8px 5px;
- .optionText {
- cursor: pointer;
- }
- }
- .optionPreSelected {
- background-color: $colorInspectorSectionHeaderBg;
- color: $colorInspectorSectionHeaderFg;
- }
- }
-}
-
/********* COMPACT FORM */
// ul > li > label, control
// Make a new UL for each form section
diff --git a/src/styles/_global.scss b/src/styles/_global.scss
index 7b4ff1870..46ab0ad66 100644
--- a/src/styles/_global.scss
+++ b/src/styles/_global.scss
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -229,7 +229,7 @@ body.desktop .has-local-controls {
@include abs();
}
-.c-grid {
+.c-grid .c-grid {
pointer-events: none;
&__x { @include bgTicks($editUIGridColorFg, 'x'); }
@@ -256,7 +256,7 @@ body.desktop .has-local-controls {
}
::placeholder {
- opacity: 0.5;
+ opacity: 0.7;
font-style: italic;
}
@@ -349,3 +349,22 @@ body.desktop .has-local-controls {
pointer-events: none !important;
cursor: default !important;
}
+
+/******************************************************** RESPONSIVE CONTAINERS */
+@mixin responsiveContainerWidths($dimension) {
+ // 3/21/22: `--width-less-than*` classes set in ObjectView.vue
+ .--show-if-less-than-#{$dimension} {
+ // Hide anything that displays within a given width by default.
+ // `display` property must be set within a more specific class
+ // for the particular item to be displayed.
+ display: none !important
+ }
+
+ .--width-less-than-#{$dimension} {
+ .--hide-if-less-than-#{$dimension} { display: none; }
+ }
+}
+
+//.--hide-by-default { display: none !important; }
+@include responsiveContainerWidths('220');
+@include responsiveContainerWidths('600');
diff --git a/src/styles/_glyphs.scss b/src/styles/_glyphs.scss
index 5aac57fdd..866c40c40 100755
--- a/src/styles/_glyphs.scss
+++ b/src/styles/_glyphs.scss
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -87,6 +87,13 @@
.icon-unlocked { @include glyphBefore($glyph-icon-unlocked); }
.icon-circle { @include glyphBefore($glyph-icon-circle); }
.icon-draft { @include glyphBefore($glyph-icon-draft); }
+.icon-question-mark { @include glyphBefore($glyph-icon-question-mark); }
+.icon-circle-slash { @include glyphBefore($glyph-icon-circle-slash); }
+.icon-status-poll-check { @include glyphBefore($glyph-icon-status-poll-check); }
+.icon-status-poll-caution { @include glyphBefore($glyph-icon-status-poll-caution); }
+.icon-status-poll-circle-slash { @include glyphBefore($glyph-icon-status-poll-circle-slash); }
+.icon-status-poll-question-mark { @include glyphBefore($glyph-icon-status-poll-question-mark); }
+.icon-status-poll-edit { @include glyphBefore($glyph-icon-status-poll-edit); }
.icon-arrows-right-left { @include glyphBefore($glyph-icon-arrows-right-left); }
.icon-arrows-up-down { @include glyphBefore($glyph-icon-arrows-up-down); }
.icon-bullet { @include glyphBefore($glyph-icon-bullet); }
@@ -194,6 +201,9 @@
.icon-bar-chart { @include glyphBefore($glyph-icon-bar-chart); }
.icon-map { @include glyphBefore($glyph-icon-map); }
.icon-plan { @include glyphBefore($glyph-icon-plan); }
+.icon-timelist { @include glyphBefore($glyph-icon-timelist); }
+.icon-notebook-shift-log { @include glyphBefore($glyph-icon-notebook-shift-log); }
+.icon-plot-scatter { @include glyphBefore($glyph-icon-plot-scatter); }
/************************** 12 PX CLASSES */
// TODO: sync with 16px redo as of 10/25/18
@@ -209,9 +219,6 @@
.bg-icon-alert-triangle { @include glyphBg($bg-icon-alert-triangle); }
.bg-icon-bell { @include glyphBg($bg-icon-bell); }
.bg-icon-info { @include glyphBg($bg-icon-info); }
-.bg-icon-activity { @include glyphBg($bg-icon-activity); }
-.bg-icon-activity-mode { @include glyphBg($bg-icon-activity-mode); }
-.bg-icon-autoflow-tabular { @include glyphBg($bg-icon-autoflow-tabular); }
.bg-icon-plus { @include glyphBg($bg-icon-plus); }
.bg-icon-grippy-ew { @include glyphBg($bg-icon-grippy-ew); }
.bg-icon-chain-links { @include glyphBg($bg-icon-chain-links); }
@@ -234,12 +241,10 @@
.bg-icon-tabular { @include glyphBg($bg-icon-tabular); }
.bg-icon-tabular-lad { @include glyphBg($bg-icon-tabular-lad); }
.bg-icon-tabular-lad-set { @include glyphBg($bg-icon-tabular-lad-set); }
-.bg-icon-tabular-realtime { @include glyphBg($bg-icon-tabular-realtime); }
.bg-icon-tabular-scrolling { @include glyphBg($bg-icon-tabular-scrolling); }
.bg-icon-telemetry { @include glyphBg($bg-icon-telemetry); }
.bg-icon-timeline { @include glyphBg($bg-icon-timeline); }
.bg-icon-timer { @include glyphBg($bg-icon-timer); }
-.bg-icon-topic { @include glyphBg($bg-icon-topic); }
.bg-icon-box-with-dashed-lines { @include glyphBg($bg-icon-box-with-dashed-lines); }
.bg-icon-summary-widget { @include glyphBg($bg-icon-summary-widget); }
.bg-icon-notebook { @include glyphBg($bg-icon-notebook); }
@@ -256,3 +261,6 @@
.bg-icon-bar-chart { @include glyphBg($bg-icon-bar-chart); }
.bg-icon-map { @include glyphBg($bg-icon-map); }
.bg-icon-plan { @include glyphBg($bg-icon-plan); }
+.bg-icon-timelist { @include glyphBg($bg-icon-timelist); }
+.bg-icon-plot-scatter { @include glyphBg($bg-icon-plot-scatter); }
+.bg-icon-notebook-shift-log { @include glyphBg($bg-icon-notebook-shift-log); }
diff --git a/src/styles/_legacy-messages.scss b/src/styles/_legacy-messages.scss
index f476d65cc..b23296287 100644
--- a/src/styles/_legacy-messages.scss
+++ b/src/styles/_legacy-messages.scss
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/styles/_legacy-plots.scss b/src/styles/_legacy-plots.scss
index a24295cc4..a5faf3ec5 100644
--- a/src/styles/_legacy-plots.scss
+++ b/src/styles/_legacy-plots.scss
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -65,7 +65,6 @@ mct-plot {
.c-plot {
@include abs($mainViewPad);
display: flex;
- flex-direction: column;
overflow: hidden;
min-height: $plotMinH;
@@ -83,11 +82,18 @@ mct-plot {
}
.c-plot--stacked-container {
+ border: 1px solid transparent;
display: flex;
flex: 1 1 auto;
flex-direction: column;
min-height: $plotMinH;
overflow: hidden;
+
+ &[s-selected] {
+ .is-editing & {
+ border: $editMarqueeBorder;
+ }
+ }
}
;
@@ -118,7 +124,7 @@ mct-plot {
}
}
- .is-in-small-container & {
+ .--width-less-than-600 & {
.c-control-bar {
display: none;
}
@@ -160,6 +166,10 @@ mct-plot {
bottom: 0;
left: 0;
}
+ .alt-pressed {
+ // When alt is being pressed and user is hovering over the plot, set the cursor
+ @include cursorGrab();
+ }
.gl-plot-axis-area.gl-plot-x {
top: auto;
@@ -494,7 +504,7 @@ mct-plot {
margin-bottom: $interiorMarginSm;
}
- .is-in-small-container & {
+ .--width-less-than-600 & {
&.is-legend-hidden {
display: none;
}
@@ -745,6 +755,12 @@ mct-plot {
overflow: hidden;
}
+/***************** SCATTER PLOTS */
+.c-scatter-chart {
+ flex: 1 1 auto;
+ overflow: hidden;
+}
+
/***************** CURSOR GUIDES */
[class*='c-cursor-guide'] {
box-shadow: $shdwCursorGuide;
diff --git a/src/styles/_legacy.scss b/src/styles/_legacy.scss
index bb45728fc..3288aabad 100644
--- a/src/styles/_legacy.scss
+++ b/src/styles/_legacy.scss
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -36,10 +36,6 @@
align-items: center;
}
- &__value {
- color: $colorBodyFgEm;
- }
-
.c-frame & {
// When in a Display or Flexible Layout
@include abs();
@@ -49,6 +45,11 @@
.c-clock {
> * + * { margin-left: $interiorMargin; }
+
+ &__timezone-selection .c-menu {
+ // Menu for selecting timezones in properties dialog
+ max-height: 200px;
+ }
}
.c-timer {
@@ -66,7 +67,7 @@
}
&__direction {
- font-size: 0.9rem !important;
+ font-size: 0.7em !important;
margin-right: $interiorMargin;
}
diff --git a/src/styles/_mixins.scss b/src/styles/_mixins.scss
index 4bb4b6177..08eafaa94 100644
--- a/src/styles/_mixins.scss
+++ b/src/styles/_mixins.scss
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -250,6 +250,12 @@
width: $plotSwatchD;
}
+@mixin dropDownArrowBg() {
+ background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10'%3e%3cpath fill='%23#{svgColorFromHex($colorSelectArw)}' d='M5 5l5-5H0z'/%3e%3c/svg%3e");
+ background-repeat: no-repeat, no-repeat;
+ background-position: right .4em top 80%, 0 0;
+}
+
@mixin noColor() {
// A "no fill/stroke" selection option. Used in palettes.
$c: red;
diff --git a/src/styles/_status.scss b/src/styles/_status.scss
index 612b23a31..c22caaadc 100644
--- a/src/styles/_status.scss
+++ b/src/styles/_status.scss
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/styles/_table.scss b/src/styles/_table.scss
index 501e70fd5..d6c85206d 100644
--- a/src/styles/_table.scss
+++ b/src/styles/_table.scss
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -90,7 +90,7 @@ div.c-table {
flex: 1 1 auto;
}
- .is-in-small-container & {
+ .--width-less-than-600 & {
&:not(.is-paused) {
.c-table-control-bar {
display: none;
diff --git a/src/styles/fonts/Open MCT Symbols 16px.json b/src/styles/fonts/Open MCT Symbols 16px.json
index 9772b8828..11cc38737 100644
--- a/src/styles/fonts/Open MCT Symbols 16px.json
+++ b/src/styles/fonts/Open MCT Symbols 16px.json
@@ -2,7 +2,7 @@
"metadata": {
"name": "Open MCT Symbols 16px",
"lastOpened": 0,
- "created": 1637023732727
+ "created": 1651949568729
},
"iconSets": [
{
@@ -11,7 +11,7 @@
"order": 77,
"id": 47,
"name": "icon-alert-rect-v2",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59648,
"tempChar": ""
},
@@ -19,7 +19,7 @@
"order": 76,
"id": 48,
"name": "icon-alert-triangle-v2",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59649,
"tempChar": ""
},
@@ -27,7 +27,7 @@
"order": 20,
"id": 112,
"name": "icon-arrow-up",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59650,
"tempChar": ""
},
@@ -35,7 +35,7 @@
"order": 36,
"id": 94,
"name": "icon-arrow-double-up",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59651,
"tempChar": ""
},
@@ -43,7 +43,7 @@
"order": 143,
"id": 96,
"name": "icon-arrow-tall-up",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59652,
"tempChar": ""
},
@@ -51,7 +51,7 @@
"order": 21,
"id": 111,
"name": "icon-arrow-right",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59653,
"tempChar": ""
},
@@ -59,7 +59,7 @@
"order": 102,
"id": 18,
"name": "icon-arrow-right-equilateral",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59654,
"tempChar": ""
},
@@ -67,7 +67,7 @@
"order": 19,
"id": 113,
"name": "icon-arrow-down",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59655,
"tempChar": ""
},
@@ -75,7 +75,7 @@
"order": 37,
"id": 93,
"name": "icon-arrow-double-down",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59656,
"tempChar": ""
},
@@ -83,7 +83,7 @@
"order": 35,
"id": 95,
"name": "icon-arrow-tall-down",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59657,
"tempChar": ""
},
@@ -91,7 +91,7 @@
"order": 18,
"id": 114,
"name": "icon-arrow-left",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59658,
"tempChar": ""
},
@@ -99,7 +99,7 @@
"order": 17,
"id": 115,
"name": "icon-asterisk",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59659,
"tempChar": ""
},
@@ -107,7 +107,7 @@
"order": 128,
"id": 59,
"name": "icon-bell",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59660,
"tempChar": ""
},
@@ -115,7 +115,7 @@
"order": 123,
"id": 103,
"name": "icon-box-round-corners",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59661,
"tempChar": ""
},
@@ -123,7 +123,7 @@
"order": 129,
"id": 58,
"name": "icon-box-with-arrow-cursor",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59662,
"tempChar": ""
},
@@ -131,7 +131,7 @@
"order": 142,
"id": 120,
"name": "icon-check",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59663,
"tempChar": ""
},
@@ -139,7 +139,7 @@
"order": 25,
"id": 107,
"name": "icon-connectivity",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59664,
"tempChar": ""
},
@@ -147,7 +147,7 @@
"order": 44,
"id": 84,
"name": "icon-database-in-brackets",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59665,
"tempChar": ""
},
@@ -155,7 +155,7 @@
"order": 75,
"id": 49,
"name": "icon-eye-open",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59666,
"tempChar": ""
},
@@ -163,7 +163,7 @@
"order": 5,
"id": 129,
"name": "icon-gear",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59667,
"tempChar": ""
},
@@ -171,7 +171,7 @@
"order": 66,
"id": 60,
"name": "icon-hourglass",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59668,
"tempChar": ""
},
@@ -179,7 +179,7 @@
"order": 65,
"id": 61,
"name": "icon-info",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59669,
"tempChar": ""
},
@@ -187,7 +187,7 @@
"order": 53,
"id": 75,
"name": "icon-link",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59670,
"tempChar": ""
},
@@ -195,7 +195,7 @@
"order": 42,
"id": 86,
"name": "icon-lock",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59671,
"tempChar": ""
},
@@ -203,7 +203,7 @@
"order": 49,
"id": 79,
"name": "icon-minus",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59672,
"tempChar": ""
},
@@ -211,7 +211,7 @@
"order": 22,
"id": 110,
"name": "icon-people",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59673,
"tempChar": ""
},
@@ -219,7 +219,7 @@
"order": 46,
"id": 82,
"name": "icon-person",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59674,
"tempChar": ""
},
@@ -227,7 +227,7 @@
"order": 38,
"id": 92,
"name": "icon-plus",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59675,
"tempChar": ""
},
@@ -235,7 +235,7 @@
"order": 164,
"id": 137,
"name": "icon-plus-in-rect",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59676,
"tempChar": ""
},
@@ -243,7 +243,7 @@
"order": 6,
"id": 128,
"name": "icon-trash",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59677,
"tempChar": ""
},
@@ -251,7 +251,7 @@
"order": 140,
"id": 122,
"name": "icon-x-heavy",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59678,
"tempChar": ""
},
@@ -259,7 +259,7 @@
"order": 78,
"id": 46,
"name": "icon-brackets",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59679,
"tempChar": ""
},
@@ -267,7 +267,7 @@
"order": 93,
"id": 27,
"name": "icon-crosshair",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59680,
"tempChar": ""
},
@@ -275,7 +275,7 @@
"order": 91,
"id": 31,
"name": "icon-grippy",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59681,
"tempChar": ""
},
@@ -283,7 +283,7 @@
"order": 121,
"id": 118,
"name": "icon-grid",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59682,
"tempChar": ""
},
@@ -291,7 +291,7 @@
"order": 154,
"id": 136,
"name": "icon-grippy-ew",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59683,
"tempChar": ""
},
@@ -299,7 +299,7 @@
"order": 152,
"id": 135,
"name": "icon-columns",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59684,
"tempChar": ""
},
@@ -307,7 +307,7 @@
"order": 153,
"id": 134,
"name": "icon-rows",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59685,
"tempChar": ""
},
@@ -315,7 +315,7 @@
"order": 162,
"id": 140,
"name": "icon-filter",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59686,
"tempChar": ""
},
@@ -323,7 +323,7 @@
"order": 161,
"id": 139,
"name": "icon-filter-outline",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59687,
"tempChar": ""
},
@@ -331,7 +331,7 @@
"order": 155,
"id": 142,
"name": "icon-suitcase",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59688,
"tempChar": ""
},
@@ -339,7 +339,7 @@
"order": 169,
"id": 145,
"name": "icon-cursor-locked",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59689,
"tempChar": ""
},
@@ -347,7 +347,7 @@
"order": 176,
"id": 150,
"name": "icon-flag",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59690,
"tempChar": ""
},
@@ -355,7 +355,7 @@
"order": 177,
"id": 152,
"name": "icon-eye-disabled",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59691,
"tempChar": ""
},
@@ -363,7 +363,7 @@
"order": 179,
"id": 153,
"name": "icon-notebook-page",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59692,
"tempChar": ""
},
@@ -371,7 +371,7 @@
"order": 186,
"id": 160,
"name": "icon-unlocked",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59693,
"tempChar": ""
},
@@ -379,7 +379,7 @@
"order": 197,
"id": 169,
"name": "icon-circle",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59694,
"tempChar": ""
},
@@ -387,865 +387,945 @@
"order": 201,
"id": 173,
"name": "icon-draft",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59695,
"tempChar": ""
},
{
+ "order": 212,
+ "id": 183,
+ "name": "icon-circle-slash",
+ "prevSize": 16,
+ "code": 59696,
+ "tempChar": ""
+ },
+ {
+ "order": 213,
+ "id": 182,
+ "name": "icon-question-mark",
+ "prevSize": 16,
+ "code": 59697,
+ "tempChar": ""
+ },
+ {
+ "order": 206,
+ "id": 179,
+ "name": "icon-status-poll-check",
+ "prevSize": 16,
+ "code": 59698,
+ "tempChar": ""
+ },
+ {
+ "order": 207,
+ "id": 178,
+ "name": "icon-status-poll-caution",
+ "prevSize": 16,
+ "code": 59699,
+ "tempChar": ""
+ },
+ {
+ "order": 210,
+ "id": 180,
+ "name": "icon-status-poll-circle-slash",
+ "prevSize": 16,
+ "code": 59700,
+ "tempChar": ""
+ },
+ {
+ "order": 211,
+ "id": 181,
+ "name": "icon-status-poll-question-mark",
+ "prevSize": 16,
+ "code": 59701,
+ "tempChar": ""
+ },
+ {
+ "order": 209,
+ "id": 176,
+ "name": "icon-status-poll-edit",
+ "prevSize": 16,
+ "code": 59702,
+ "tempChar": ""
+ },
+ {
"order": 27,
"id": 105,
"name": "icon-arrows-right-left",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59904,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 26,
"id": 106,
"name": "icon-arrows-up-down",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59905,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 68,
"id": 56,
"name": "icon-bullet",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59906,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 150,
"id": 133,
- "prevSize": 24,
+ "prevSize": 16,
"code": 59907,
"name": "icon-calendar",
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 45,
"id": 83,
"name": "icon-chain-links",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59908,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 73,
"id": 51,
"name": "icon-download",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59909,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 39,
"id": 91,
"name": "icon-duplicate",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59910,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 50,
"id": 78,
"name": "icon-folder-new",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59911,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 138,
"id": 124,
"name": "icon-fullscreen-collapse",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59912,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 139,
"id": 123,
"name": "icon-fullscreen-expand",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59913,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 122,
"id": 104,
"name": "icon-layers",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59914,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 151,
"id": 102,
"name": "icon-line-horz",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59915,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 100,
"id": 20,
"name": "icon-magnify",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59916,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 99,
"id": 21,
"name": "icon-magnify-in",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59917,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 101,
"id": 19,
"name": "icon-magnify-out-v2",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59918,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 103,
"id": 17,
"name": "icon-menu",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59919,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 124,
"id": 89,
"name": "icon-move",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59920,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 7,
"id": 127,
"name": "icon-new-window",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59921,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 63,
"id": 63,
"name": "icon-paint-bucket-v2",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59922,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 15,
"id": 117,
"name": "icon-pencil",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59923,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 54,
"id": 72,
"name": "icon-pencil-edit-in-place",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59924,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 40,
"id": 90,
"name": "icon-play",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59925,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 125,
"id": 88,
"name": "icon-pause",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59926,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 119,
"id": 13,
"name": "icon-plot-resource",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59927,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 48,
"id": 80,
"name": "icon-pointer-left",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59928,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 47,
"id": 81,
"name": "icon-pointer-right",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59929,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 85,
"id": 37,
"name": "icon-refresh",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59930,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 55,
"id": 71,
"name": "icon-save",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59931,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 56,
"id": 70,
"name": "icon-save-as",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59932,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 58,
"id": 68,
"name": "icon-sine",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59933,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 113,
"id": 5,
"name": "icon-font",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59934,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 41,
"id": 87,
"name": "icon-thumbs-strip",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59935,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 146,
"id": 99,
"name": "icon-two-parts-both",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59936,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 145,
"id": 98,
"name": "icon-two-parts-one-only",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59937,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 82,
"id": 40,
"name": "icon-resync",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59938,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 86,
"id": 36,
"name": "icon-reset",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59939,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 61,
"id": 65,
"name": "icon-x-in-circle",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59940,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 84,
"id": 38,
"name": "icon-brightness",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59941,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 83,
"id": 39,
"name": "icon-contrast",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59942,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 87,
"id": 35,
"name": "icon-expand",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59943,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 89,
"id": 33,
"name": "icon-list-view",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59944,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 133,
"id": 28,
"name": "icon-grid-snap-to",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59945,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 132,
"id": 29,
"name": "icon-grid-snap-no",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59946,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 94,
"id": 26,
"name": "icon-frame-show",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59947,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 95,
"id": 25,
"name": "icon-frame-hide",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59948,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 97,
"id": 23,
"name": "icon-import",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59949,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 96,
"id": 24,
"name": "icon-export",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59950,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 194,
"id": 4,
"name": "icon-font-size",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59951,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 163,
"id": 141,
"name": "icon-clear-data",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59952,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 173,
"id": 149,
"name": "icon-history",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59953,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 181,
"id": 158,
"name": "icon-arrow-up-to-parent",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59954,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 184,
"id": 159,
"name": "icon-crosshair-in-circle",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59955,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 185,
"id": 161,
"name": "icon-target",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59956,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 187,
"id": 163,
"name": "icon-items-collapse",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59957,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 188,
"id": 162,
"name": "icon-items-expand",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59958,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 190,
"id": 164,
"name": "icon-3-dots",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59959,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 193,
"id": 165,
"name": "icon-grid-on",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59960,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 192,
"id": 166,
"name": "icon-grid-off",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59961,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 191,
"id": 167,
"name": "icon-camera",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59962,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 196,
"id": 168,
"name": "icon-folders-collapse",
- "prevSize": 24,
+ "prevSize": 16,
"code": 59963,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 144,
"id": 97,
"name": "icon-activity",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60160,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 104,
"id": 16,
"name": "icon-activity-mode",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60161,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 137,
"id": 125,
"name": "icon-autoflow-tabular",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60162,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 115,
"id": 3,
"name": "icon-clock",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60163,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 2,
"id": 132,
"name": "icon-database",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60164,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 3,
"id": 131,
"name": "icon-database-query",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60165,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 67,
"id": 57,
"name": "icon-dataset",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60166,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 59,
"id": 67,
"name": "icon-datatable",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60167,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 136,
"id": 126,
"name": "icon-dictionary",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60168,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 51,
"id": 77,
"name": "icon-folder",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60169,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 147,
"id": 100,
"name": "icon-image",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60170,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 4,
"id": 130,
"name": "icon-layout",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60171,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 24,
"id": 108,
"name": "icon-object",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60172,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 52,
"id": 76,
"name": "icon-object-unknown",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60173,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 105,
"id": 15,
"name": "icon-packet",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60174,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 126,
"id": 74,
"name": "icon-page",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60175,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 130,
"id": 44,
"name": "icon-plot-overlay",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60176,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 80,
"id": 42,
"name": "icon-plot-stacked",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60177,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 134,
"id": 14,
"name": "icon-session",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60178,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 109,
"id": 9,
"name": "icon-tabular",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60179,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 107,
"id": 11,
"name": "icon-tabular-lad",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60180,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 106,
"id": 12,
"name": "icon-tabular-lad-set",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60181,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 70,
"id": 54,
"name": "icon-tabular-realtime",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60182,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 60,
"id": 66,
"name": "icon-tabular-scrolling",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60183,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 131,
"id": 43,
"name": "icon-telemetry",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60184,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 202,
"id": 10,
"name": "icon-timeline",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60185,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 81,
"id": 41,
"name": "icon-timer",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60186,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 69,
"id": 55,
"name": "icon-topic",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60187,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 79,
"id": 45,
"name": "icon-box-with-dashed-lines-v2",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60188,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 90,
"id": 32,
"name": "icon-summary-widget",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60189,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 92,
"id": 30,
"name": "icon-notebook",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60190,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 168,
"id": 0,
"name": "icon-tabs-view",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60191,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 117,
"id": 1,
"name": "icon-flexible-layout",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60192,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 166,
"id": 144,
"name": "icon-generator-sine",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60193,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 167,
"id": 143,
"name": "icon-generator-event",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60194,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 165,
"id": 138,
"name": "icon-gauge-v2",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60195,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 170,
"id": 148,
"name": "icon-spectra",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60196,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 171,
"id": 147,
"name": "icon-telemetry-spectra",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60197,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 172,
"id": 146,
"name": "icon-pushbutton",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60198,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 174,
"id": 151,
"name": "icon-conditional",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60199,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 178,
"id": 154,
"name": "icon-condition-widget",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60200,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 180,
"id": 155,
"name": "icon-alphanumeric",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60201,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 183,
"id": 156,
"name": "icon-image-telemetry",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60202,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 198,
"id": 170,
"name": "icon-telemetry-aggregate",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60203,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 199,
"id": 172,
"name": "icon-bar-graph",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60204,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 200,
"id": 171,
"name": "icon-map",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60205,
- "tempChar": ""
+ "tempChar": ""
},
{
"order": 203,
"id": 174,
"name": "icon-plan",
- "prevSize": 24,
+ "prevSize": 16,
"code": 60206,
- "tempChar": ""
+ "tempChar": ""
+ },
+ {
+ "order": 204,
+ "id": 175,
+ "name": "icon-timelist",
+ "prevSize": 16,
+ "code": 60207,
+ "tempChar": ""
+ },
+ {
+ "order": 214,
+ "id": 184,
+ "name": "icon-notebook-restricted",
+ "prevSize": 16,
+ "code": 60209,
+ "tempChar": ""
+ },
+ {
+ "order": 205,
+ "id": 176,
+ "name": "icon-plot-scatter",
+ "prevSize": 16,
+ "code": 60208,
+ "tempChar": ""
}
],
"id": 0,
@@ -1258,7 +1338,7 @@
"designer": "Charles Hacskaylo"
},
"height": 1024,
- "prevSize": 24,
+ "prevSize": 16,
"icons": [
{
"id": 47,
@@ -2092,6 +2172,162 @@
}
},
{
+ "id": 183,
+ "paths": [
+ "M512 0c-282.78 0-512 229.22-512 512s229.22 512 512 512 512-229.22 512-512-229.22-512-512-512zM263.1 263.1c66.48-66.48 154.88-103.1 248.9-103.1 66.74 0 130.64 18.48 185.9 52.96l-484.94 484.94c-34.5-55.24-52.96-119.16-52.96-185.9 0-94.020 36.62-182.42 103.1-248.9zM760.9 760.9c-66.48 66.48-154.88 103.1-248.9 103.1-66.74 0-130.64-18.48-185.9-52.96l484.94-484.94c34.5 55.24 52.96 119.16 52.96 185.9 0 94.020-36.62 182.42-103.1 248.9z"
+ ],
+ "attrs": [
+ {}
+ ],
+ "isMulticolor": false,
+ "isMulticolor2": false,
+ "grid": 16,
+ "tags": [
+ "icon-circle-slash"
+ ],
+ "colorPermutations": {
+ "12552552551": [
+ {}
+ ]
+ }
+ },
+ {
+ "id": 182,
+ "paths": [
+ "M136.86 52.26c54.080-34.82 120.58-52.26 199.44-52.26 103.6 0 189.7 24.76 258.24 74.28s102.82 122.88 102.82 220.060c0 59.6-14.86 109.8-44.58 150.6-17.38 24.76-50.76 56.4-100.14 94.9l-48.68 37.82c-26.54 20.64-44.14 44.7-52.82 72.2-5.5 17.44-8.46 44.48-8.92 81.14h-186.4c2.74-77.48 10.060-131 21.94-160.58s42.5-63.62 91.88-102.12l50.060-39.2c16.46-12.38 29.72-25.9 39.78-40.58 18.28-25.2 27.42-52.96 27.42-83.22 0-34.84-10.18-66.6-30.52-95.24-20.36-28.64-57.52-42.98-111.48-42.98s-90.68 17.66-112.88 52.96c-22.18 35.32-33.26 71.98-33.26 110.040h-198.76c5.5-130.64 51.12-223.24 136.86-277.82zM251.020 825.24h205.62v198.74h-205.62v-198.74z"
+ ],
+ "attrs": [
+ {}
+ ],
+ "width": 697,
+ "isMulticolor": false,
+ "isMulticolor2": false,
+ "grid": 16,
+ "tags": [
+ "icon-question-mark"
+ ],
+ "colorPermutations": {
+ "12552552551": [
+ {}
+ ]
+ }
+ },
+ {
+ "id": 179,
+ "paths": [
+ "M512 0c-282.76 0-512 214.9-512 480 0 92.26 27.8 178.44 75.92 251.6l-75.92 292.4 313.5-101.42c61.040 24.1 128.12 37.42 198.5 37.42 282.76 0 512-214.9 512-480s-229.24-480-512-480zM768 448l-320 320-192-192v-192l192 192 320-320v192z"
+ ],
+ "attrs": [
+ {}
+ ],
+ "isMulticolor": false,
+ "isMulticolor2": false,
+ "grid": 16,
+ "tags": [
+ "icon-status-poll-check"
+ ],
+ "colorPermutations": {
+ "12552552551": [
+ {}
+ ]
+ }
+ },
+ {
+ "id": 178,
+ "paths": [
+ "M512 0c-282.76 0-512 214.9-512 480 0 92.26 27.8 178.44 75.92 251.6l-75.92 292.4 313.5-101.42c61.040 24.1 128.12 37.42 198.5 37.42 282.76 0 512-214.9 512-480s-229.24-480-512-480zM781.36 704h-538.72c-44.96 0-63.5-31.94-41.2-70.98l270-472.48c22.3-39.040 58.82-39.040 81.12 0l269.98 472.48c22.3 39.040 3.78 70.98-41.2 70.98z",
+ "M457.14 417.86l24.2 122.64h61.32l24.2-122.64v-163.5h-109.72v163.5z",
+ "M471.12 581.36h81.76v81.76h-81.76v-81.76z"
+ ],
+ "attrs": [
+ {},
+ {},
+ {}
+ ],
+ "isMulticolor": false,
+ "isMulticolor2": false,
+ "grid": 16,
+ "tags": [
+ "icon-status-poll-caution"
+ ],
+ "colorPermutations": {
+ "12552552551": [
+ {},
+ {},
+ {}
+ ]
+ }
+ },
+ {
+ "id": 180,
+ "paths": [
+ "M391.18 668.7c35.72 22.98 77.32 35.3 120.82 35.3 59.84 0 116.080-23.3 158.4-65.6 42.3-42.3 65.6-98.56 65.6-158.4 0-43.5-12.32-85.080-35.3-120.82l-309.52 309.52z",
+ "M512 256c-59.84 0-116.080 23.3-158.4 65.6-42.3 42.3-65.6 98.56-65.6 158.4 0 43.5 12.32 85.080 35.3 120.82l309.52-309.52c-35.72-22.98-77.32-35.3-120.82-35.3z",
+ "M512 0c-282.76 0-512 214.9-512 480 0 92.26 27.8 178.44 75.92 251.6l-75.92 292.4 313.5-101.42c61.040 24.1 128.12 37.42 198.5 37.42 282.76 0 512-214.9 512-480s-229.24-480-512-480zM512 800c-176.74 0-320-143.26-320-320s143.26-320 320-320 320 143.26 320 320-143.26 320-320 320z"
+ ],
+ "attrs": [
+ {},
+ {},
+ {}
+ ],
+ "isMulticolor": false,
+ "isMulticolor2": false,
+ "grid": 16,
+ "tags": [
+ "icon-status-poll-circle-slash"
+ ],
+ "colorPermutations": {
+ "12552552551": [
+ {},
+ {},
+ {}
+ ]
+ }
+ },
+ {
+ "id": 181,
+ "paths": [
+ "M512 0c-282.76 0-512 214.9-512 480 0 92.26 27.8 178.44 75.92 251.6l-75.92 292.4 313.5-101.42c61.040 24.1 128.12 37.42 198.5 37.42 282.76 0 512-214.9 512-480s-229.24-480-512-480zM579.020 832h-141.36v-136.64h141.36v136.64zM713.84 433.9c-11.94 17.020-34.9 38.78-68.84 65.24l-33.48 26c-18.24 14.18-30.34 30.74-36.32 49.64-3.78 11.98-5.82 30.58-6.14 55.8h-128.12c1.88-53.26 6.92-90.060 15.080-110.4 8.18-20.34 29.22-43.74 63.16-70.22l34.42-26.94c11.3-8.52 20.42-17.8 27.34-27.9 12.56-17.34 18.86-36.4 18.86-57.2 0-23.94-7-45.78-20.98-65.48-14-19.7-39.54-29.54-76.64-29.54s-62.34 12.14-77.6 36.4c-15.24 24.28-22.88 49.48-22.88 75.64h-136.64c3.78-89.84 35.14-153.5 94.080-191.020 37.18-23.94 82.9-35.94 137.12-35.94 71.22 0 130.42 17.020 177.54 51.060s70.68 84.48 70.68 151.3c0 40.98-10.22 75.5-30.66 103.54z"
+ ],
+ "attrs": [
+ {}
+ ],
+ "isMulticolor": false,
+ "isMulticolor2": false,
+ "grid": 16,
+ "tags": [
+ "icon-status-poll-question-mark"
+ ],
+ "colorPermutations": {
+ "12552552551": [
+ {}
+ ]
+ }
+ },
+ {
+ "id": 176,
+ "paths": [
+ "M1000.080 334.64l-336.6 336.76-20.52 6.88-450.96 153.72 160.68-471.52 332.34-332.34c-54.040-18.2-112.28-28.14-173.020-28.14-282.76 0-512 214.9-512 480 0 92.26 27.8 178.44 75.92 251.6l-75.92 292.4 313.5-101.42c61.040 24.1 128.12 37.42 198.5 37.42 282.76 0 512-214.9 512-480 0-50.68-8.4-99.5-23.92-145.36z",
+ "M408.42 395.24l-2.16 6.3-111.7 327.9 334.12-113.86 4.62-4.68 350.28-350.28c6.8-6.78 14.96-19.1 14.96-38.9 0-34.86-26.82-83.28-69.88-126.38-26.54-26.54-55.9-47.6-82.7-59.34-47.34-20.8-72.020-6.24-82.64 4.36l-354.9 354.88zM470.56 421.42h44v88h88v44l-4.7 12.72-139.68 47.54-47.94-47.94 47.6-139.72 12.72-4.6z"
+ ],
+ "attrs": [
+ {},
+ {}
+ ],
+ "isMulticolor": false,
+ "isMulticolor2": false,
+ "grid": 16,
+ "tags": [
+ "icon-status-poll-edit"
+ ],
+ "colorPermutations": {
+ "12552552551": [
+ {},
+ {}
+ ]
+ }
+ },
+ {
"id": 105,
"paths": [
"M1024 512l-448 512v-1024z",
@@ -3310,15 +3546,21 @@
{
"id": 76,
"paths": [
- "M510-2l-512 320v384l512 320 512-320v-384l-512-320zM585.4 859.2c-21.2 20.8-46 30.8-76 30.8-31.2 0-56.2-9.8-76.2-29.6-20-20-29.6-44.8-29.6-76.2 0-30.4 10.2-55.2 31-76.2s45.2-31.2 74.8-31.2c29.6 0 54.2 10.4 75.6 32s31.8 46.4 31.8 76c-0.2 29-10.8 54-31.4 74.4zM638.2 546.6c-23.6 11.8-37.4 22-43.4 32.4-3.6 6.2-6 14.8-7.4 26.8v41h-161.4v-44.2c0-40.2 4.4-69.8 13-88 8-17.2 22.6-30.2 44.8-40l34.8-15.4c32-14.2 48.2-35.2 48.2-62.8 0-16-6-30.4-17.2-41.8-11.2-11.2-25.6-17.2-41.6-17.2-24 0-54.4 10-62.8 57.4l-2.2 12.2h-147l1.4-16.2c4-44.6 17-82.4 38.8-112.2 19.6-27 45.6-48.6 77-64.6s64.6-24 98.2-24c60.6 0 110.2 19.4 151.4 59.6 41.2 40 61.2 88 61.2 147.2 0 70.8-28.8 121.4-85.8 149.8z"
+ "M511.98 0l-511.98 320v384l512 320 512-320v-384l-512.020-320zM586.22 896h-141.36v-136.64h141.36v136.64zM721.040 497.9c-11.94 17.020-34.9 38.78-68.84 65.24l-33.48 26c-18.24 14.18-30.34 30.74-36.32 49.64-3.78 11.98-5.82 30.58-6.14 55.8h-128.12c1.88-53.26 6.92-90.060 15.080-110.4 8.18-20.34 29.22-43.74 63.16-70.22l34.42-26.94c11.3-8.52 20.42-17.8 27.34-27.9 12.56-17.34 18.86-36.4 18.86-57.2 0-23.94-7-45.78-20.98-65.48-14-19.7-39.54-29.54-76.64-29.54s-62.34 12.14-77.6 36.4c-15.24 24.28-22.88 49.48-22.88 75.64h-136.64c3.78-89.84 35.14-153.5 94.080-191.020 37.18-23.94 82.9-35.94 137.12-35.94 71.22 0 130.42 17.020 177.54 51.060s70.68 84.48 70.68 151.3c0 40.98-10.22 75.5-30.66 103.54z"
+ ],
+ "attrs": [
+ {}
],
- "attrs": [],
"grid": 16,
"tags": [
"icon-object-unknown"
],
+ "isMulticolor": false,
+ "isMulticolor2": false,
"colorPermutations": {
- "12552552551": []
+ "12552552551": [
+ {}
+ ]
}
},
{
@@ -3972,6 +4214,69 @@
{}
]
}
+ },
+ {
+ "id": 175,
+ "paths": [
+ "M896 0h-768c-70.606 0.215-127.785 57.394-128 127.979l-0 0.021v768c0.215 70.606 57.394 127.785 127.979 128l0.021 0h768c70.606-0.215 127.785-57.394 128-127.979l0-0.021v-768c-0.215-70.606-57.394-127.785-127.979-128l-0.021-0zM426.94 533.46c-8.054 15.864-24.249 26.545-42.938 26.545-7.823 0-15.209-1.871-21.734-5.191l0.273 0.126-154.54-77.28v-221.66c0-26.51 21.49-48 48-48s48 21.49 48 48v0 162.34l101.46 50.72c15.864 8.054 26.545 24.249 26.545 42.938 0 7.823-1.871 15.209-5.191 21.734l0.126-0.273zM896 896h-320v-128h320zM896 704h-320v-128h320zM896 512h-320v-128h320zM896 320h-320v-128h320z"
+ ],
+ "attrs": [
+ {}
+ ],
+ "isMulticolor": false,
+ "isMulticolor2": false,
+ "grid": 16,
+ "tags": [
+ "icon-timelist"
+ ],
+ "colorPermutations": {
+ "12552552551": [
+ {}
+ ]
+ }
+ },
+ {
+ "id": 184,
+ "paths": [
+ "M896 110.72c0-79.9-55.38-127.32-123.080-105.36l-772.92 250.64h896v-145.28z",
+ "M896 320h-896v576c0 70.4 57.6 128 128 128h768c70.4 0 128-57.6 128-128v-448c0-70.4-57.6-128-128-128zM256 832h-128v-128h128v128zM256 640h-128v-128h128v128zM896 832h-512v-128h512v128zM896 640h-512v-128h512v128z"
+ ],
+ "attrs": [
+ {},
+ {}
+ ],
+ "isMulticolor": false,
+ "isMulticolor2": false,
+ "grid": 16,
+ "tags": [
+ "icon-notebook-restricted"
+ ],
+ "colorPermutations": {
+ "12552552551": [
+ {},
+ {}
+ ]
+ }
+ },
+ {
+ "id": 176,
+ "paths": [
+ "M192 0c-105.6 0-192 86.4-192 192v640c0 105.6 86.4 192 192 192h640c105.6 0 192-86.4 192-192v-640c0-105.6-86.4-192-192-192zM128 352c0-53.019 42.981-96 96-96s96 42.981 96 96c0 53.019-42.981 96-96 96v0c-53.019 0-96-42.981-96-96v0zM288 832c-53.019 0-96-42.981-96-96s42.981-96 96-96c53.019 0 96 42.981 96 96v0c0 53.019-42.981 96-96 96v0zM544 640c-53.019 0-96-42.981-96-96s42.981-96 96-96c53.019 0 96 42.981 96 96v0c0 53.019-42.981 96-96 96v0zM544 320c-53.019 0-96-42.981-96-96s42.981-96 96-96c53.019 0 96 42.981 96 96v0c0 53.019-42.981 96-96 96v0zM800 832c-53.019 0-96-42.981-96-96s42.981-96 96-96c53.019 0 96 42.981 96 96v0c0 53.019-42.981 96-96 96v0z"
+ ],
+ "attrs": [
+ {}
+ ],
+ "isMulticolor": false,
+ "isMulticolor2": false,
+ "grid": 16,
+ "tags": [
+ "icon-plot-scatter"
+ ],
+ "colorPermutations": {
+ "12552552551": [
+ {}
+ ]
+ }
}
],
"invisible": false,
@@ -4006,11 +4311,12 @@
"fontFamily": "Open-MCT-Symbols-16px",
"majorVersion": 5,
"minorVersion": 1,
- "designer": "Charles Hacskaylo"
+ "designer": "Charles Hacskaylo",
+ "description": "Change to 5% baseline height"
},
"metrics": {
"emSize": 1024,
- "baseline": 20,
+ "baseline": 10,
"whitespace": 0
},
"embed": false,
diff --git a/src/styles/fonts/Open-MCT-Symbols-16px.svg b/src/styles/fonts/Open-MCT-Symbols-16px.svg
index 4f3ced4f5..38ce5985a 100644
--- a/src/styles/fonts/Open-MCT-Symbols-16px.svg
+++ b/src/styles/fonts/Open-MCT-Symbols-16px.svg
@@ -3,163 +3,173 @@
<svg xmlns="http://www.w3.org/2000/svg">
<metadata>Generated by IcoMoon</metadata>
<defs>
-<font id="Open-MCT-Symbols-16px" horiz-adv-x="1024">
-<font-face units-per-em="1024" ascent="819.2" descent="-204.8" />
+<font id="icomoon" horiz-adv-x="1024">
+<font-face units-per-em="1024" ascent="960" descent="-64" />
<missing-glyph horiz-adv-x="1024" />
-<glyph unicode="&#x20;" horiz-adv-x="0" d="" />
-<glyph unicode="&#xe900;" glyph-name="icon-alert-rect-v2" d="M896 832h-768c-70.6-0.2-127.8-57.4-128-128v-768c0.2-70.6 57.4-127.8 128-128h768c70.6 0.2 127.8 57.4 128 128v768c-0.2 70.6-57.4 127.8-128 128zM576-64h-128v128h128v-128zM597.8 320l-37.8-192h-96l-37.8 192v384h171.8v-384z" />
-<glyph unicode="&#xe901;" glyph-name="icon-alert-triangle-v2" d="M998.2-16.8l-422.6 739.6c-35 61.2-92 61.2-127 0l-422.8-739.6c-35-61.2-6-111.2 64.4-111.2h843.4c70.6 0 99.6 50 64.6 111.2zM576-64h-128v128h128v-128zM597.8 320l-37.8-192h-96l-37.8 192v256h171.8v-256z" />
-<glyph unicode="&#xe902;" glyph-name="icon-arrow-up" d="M512 576l-512-512h1024z" />
-<glyph unicode="&#xe903;" glyph-name="icon-arrow-double-up" d="M510 322l512-512h-1024zM510 834l512-512h-1024z" />
-<glyph unicode="&#xe904;" glyph-name="icon-arrow-tall-up" d="M512 832l512-1024h-1024z" />
-<glyph unicode="&#xe905;" glyph-name="icon-arrow-right" d="M768 320l-512 512v-1024z" />
-<glyph unicode="&#xe906;" glyph-name="icon-arrow-right-equilateral" d="M962 320l-896-512v1024z" />
-<glyph unicode="&#xe907;" glyph-name="icon-arrow-down" d="M512 64l512 512h-1024z" />
-<glyph unicode="&#xe908;" glyph-name="icon-arrow-double-down" d="M510 322l-512 512h1024zM510-190l-512 512h1024z" />
-<glyph unicode="&#xe909;" glyph-name="icon-arrow-tall-down" d="M512-192l-512 1024h1024z" />
-<glyph unicode="&#xe90a;" glyph-name="icon-arrow-left" d="M256 320l512-512v1024z" />
-<glyph unicode="&#xe90b;" glyph-name="icon-asterisk" d="M1004.166 491.542l-97.522 168.916-330.534-229.414 33.414 400.956h-195.048l33.414-400.956-330.534 229.414-97.522-168.916 363.944-171.542-363.944-171.542 97.522-168.916 330.534 229.414-33.414-400.956h195.048l-33.414 400.956 330.534-229.414 97.522 168.916-363.944 171.542z" />
-<glyph unicode="&#xe90c;" glyph-name="icon-bell" d="M512-192c106 0 192 86 192 192h-384c0-106 86-192 192-192zM896 384v64c0 212-172 384-384 384s-384-172-384-384v-64c0-70.6-57.4-128-128-128v-128h1024v128c-70.6 0-128 57.4-128 128z" />
-<glyph unicode="&#xe90d;" glyph-name="icon-box-round-corners" d="M1024 0c0-105.6-86.4-192-192-192h-640c-105.6 0-192 86.4-192 192v640c0 105.6 86.4 192 192 192h640c105.6 0 192-86.4 192-192v-640z" />
-<glyph unicode="&#xe90e;" glyph-name="icon-box-with-arrow-cursor" d="M894 834h-768c-70.4 0-128-57.6-128-128v-768c0-70.4 57.6-128 128-128h400c-2.2 3.8-4 7.6-5.8 11.4l-255.2 576.8c-21.4 48.4-10.8 105 26.6 142.4 24.4 24.4 57.2 37.4 90.4 37.4 17.4 0 35.2-3.6 51.8-11l576.6-255.4c4-1.8 7.8-3.8 11.4-5.8v400.2c0.2 70.4-57.4 128-127.8 128zM958.6 194.6l-576.6 255.4 255.4-576.6 64.6 128.6 192-192 128 128-192 192z" />
-<glyph unicode="&#xe90f;" glyph-name="icon-check" d="M1024 832l-640-640-384 384v-384l384-384 640 640z" />
-<glyph unicode="&#xe910;" glyph-name="icon-connectivity" d="M704 256c0-70.4-57.6-128-128-128h-128c-70.4 0-128 57.6-128 128v128c0 70.4 57.6 128 128 128h128c70.4 0 128-57.6 128-128v-128zM1024 320l-192 320v-640zM0 320l192 320v-640z" />
-<glyph unicode="&#xe911;" glyph-name="icon-database-in-brackets" d="M768 480c0-53.019-114.615-96-256-96s-256 42.981-256 96c0 53.019 114.615 96 256 96s256-42.981 256-96zM768 160v256c0-53-114.6-96-256-96s-256 43-256 96v-256c0-53 114.6-96 256-96s256 43 256 96zM832 832h-128v-192h127.6c0.2 0 0.2-0.2 0.4-0.4v-639.4c0-0.2-0.2-0.2-0.4-0.4h-127.6v-192h128c105.6 0 192 86.4 192 192v640.2c0 105.6-86.4 192-192 192zM192 0.4v639.4c0 0.2 0.2 0.2 0.4 0.4h127.6v191.8h-128c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h128v192h-127.6c-0.2 0-0.4 0.2-0.4 0.4z" />
-<glyph unicode="&#xe912;" glyph-name="icon-eye-open" d="M512 715.6c-245.8 0-452.2-168-510.8-395.6 58.6-227.4 265-395.6 510.8-395.6s452.2 168 510.8 395.6c-58.6 227.4-265 395.6-510.8 395.6zM829.2 243.6c-22.6-34.4-50.6-64.8-83-90.4-32.8-25.8-69-45.6-108-59.4-40.4-14.2-82.8-21.4-126-21.4s-85.8 7.2-126 21.4c-39 13.8-75.4 33.8-108 59.4-32.4 25.6-60.4 55.8-83 90.4-15.8 24-28.8 49.6-38.6 76.4 10 26.8 23 52.4 38.6 76.4 22.6 34.4 50.6 64.8 83 90.4 32.8 25.8 69 45.6 108 59.4 40.4 14.2 82.8 21.4 126 21.4s85.8-7.2 126-21.4c39-13.8 75.4-33.8 108-59.4 32.4-25.6 60.4-55.8 83-90.4 15.8-24 28.8-49.6 38.6-76.4-9.8-26.8-22.8-52.4-38.6-76.4zM704 320c0-106.039-85.961-192-192-192s-192 85.961-192 192c0 106.039 85.961 192 192 192s192-85.961 192-192z" />
-<glyph unicode="&#xe913;" glyph-name="icon-gear" d="M1024 256v128l-140.976 35.244c-8.784 32.922-21.818 64.106-38.504 92.918l74.774 124.622-90.51 90.51-124.622-74.774c-28.812 16.686-59.996 29.72-92.918 38.504l-35.244 140.976h-128l-35.244-140.976c-32.922-8.784-64.106-21.818-92.918-38.504l-124.622 74.774-90.51-90.51 74.774-124.622c-16.686-28.812-29.72-59.996-38.504-92.918l-140.976-35.244v-128l140.976-35.244c8.784-32.922 21.818-64.106 38.504-92.918l-74.774-124.622 90.51-90.51 124.622 74.774c28.812-16.686 59.996-29.72 92.918-38.504l35.244-140.976h128l35.244 140.976c32.922 8.784 64.106 21.818 92.918 38.504l124.622-74.774 90.51 90.51-74.774 124.622c16.686 28.812 29.72 59.996 38.504 92.918l140.976 35.244zM704 320c0-106.038-85.962-192-192-192s-192 85.962-192 192 85.962 192 192 192 192-85.962 192-192z" />
-<glyph unicode="&#xe914;" glyph-name="icon-hourglass" d="M1024 832h-1024c0-282.8 229.2-512 512-512s512 229.2 512 512zM512 448c-102.6 0-199 40-271.6 112.4-41.2 41.2-72 90.2-90.8 143.6h724.6c-18.8-53.4-49.6-102.4-90.8-143.6-72.4-72.4-168.8-112.4-271.4-112.4zM512 320c-282.8 0-512-229.2-512-512h1024c0 282.8-229.2 512-512 512z" />
-<glyph unicode="&#xe915;" glyph-name="icon-info" d="M512 832c-282.8 0-512-229.2-512-512s229.2-512 512-512 512 229.2 512 512-229.2 512-512 512zM512 704c70.6 0 128-57.4 128-128s-57.4-128-128-128c-70.6 0-128 57.4-128 128s57.4 128 128 128zM704 0h-384v128h64v256h256v-256h64v-128z" />
-<glyph unicode="&#xe916;" glyph-name="icon-link" d="M1024 320l-512 512v-307.2l-512-204.8v-256h512v-256z" />
-<glyph unicode="&#xe917;" glyph-name="icon-lock" horiz-adv-x="768" d="M702 448h-62v128c0 141.385-114.615 256-256 256s-256-114.615-256-256v0-128h-64c-35.301-0.113-63.887-28.699-64-63.989v-512.011c0.113-35.301 28.699-63.887 63.989-64h638.011c35.301 0.113 63.887 28.699 64 63.989v512.011c-0.113 35.301-28.699 63.887-63.989 64h-0.011zM256 448v128c0 70.692 57.308 128 128 128s128-57.308 128-128v0-128z" />
-<glyph unicode="&#xe918;" glyph-name="icon-minus" d="M960 192c35.2 0 64 28.8 64 64v128c0 35.2-28.8 64-64 64h-896c-35.2 0-64-28.8-64-64v-128c0-35.2 28.8-64 64-64h896z" />
-<glyph unicode="&#xe919;" glyph-name="icon-people" d="M704 512h64c70.4 0 128 57.6 128 128v64c0 70.4-57.6 128-128 128h-64c-70.4 0-128-57.6-128-128v-64c0-70.4 57.6-128 128-128zM256 512h64c70.4 0 128 57.6 128 128v64c0 70.4-57.6 128-128 128h-64c-70.4 0-128-57.6-128-128v-64c0-70.4 57.6-128 128-128zM832 448h-192c-34.908 0-67.716-9.448-96-25.904 57.278-33.324 96-95.404 96-166.096v-448h384v448c0 105.6-86.4 192-192 192zM384 448h-192c-105.6 0-192-86.4-192-192v-448h576v448c0 105.6-86.4 192-192 192z" />
-<glyph unicode="&#xe91a;" glyph-name="icon-person" d="M768 576c0-105.6-86.4-192-192-192h-128c-105.6 0-192 86.4-192 192v64c0 105.6 86.4 192 192 192h128c105.6 0 192-86.4 192-192v-64zM64-192v192c0 140.8 115.2 256 256 256h384c140.8 0 256-115.2 256-256v-192z" />
-<glyph unicode="&#xe91b;" glyph-name="icon-plus" d="M960 448h-330v320c0 35.2-28.8 64-64 64h-108c-35.2 0-64-28.8-64-64v-320h-330c-35.2 0-64-28.8-64-64v-128c0-35.2 28.8-64 64-64h330v-320c0-35.2 28.8-64 64-64h108c35.2 0 64 28.8 64 64v320h330c35.2 0 64 28.8 64 64v128c0 35.2-28.8 64-64 64z" />
-<glyph unicode="&#xe91c;" glyph-name="icon-plus-in-rect" d="M830 832h-636c-106.6 0-194-87.2-194-194v-636c0-106.8 87.4-194 194-194h636c106.6 0 194 87.2 194 194v636c0 106.8-87.4 194-194 194zM896 224c0-17.673-14.327-32-32-32v0h-224v-224c0-17.673-14.327-32-32-32v0h-192c-17.673 0-32 14.327-32 32v0 224h-224c-17.673 0-32 14.327-32 32v0 192c0 17.673 14.327 32 32 32v0h224v224c0 17.673 14.327 32 32 32v0h192c17.673 0 32-14.327 32-32v0-224h224c17.673 0 32-14.327 32-32v0z" />
-<glyph unicode="&#xe91d;" glyph-name="icon-trash" d="M832 704h-192.36v64c0 35.2-28.8 64-64 64h-128c-35.2 0-64-28.8-64-64v-64h-191.64c-105.6 0-192-72-192-160s0-160 0-160h64v-384c0-105.6 86.4-192 192-192h512c105.6 0 192 86.4 192 192v384h64c0 0 0 72 0 160s-86.4 160-192 160zM320 0h-128v384h128v-384zM576 0h-128v384h128v-384zM832 0h-128v384h128v-384z" />
-<glyph unicode="&#xe91e;" glyph-name="icon-x-heavy" d="M704 320l301.332-301.332c24.89-24.89 24.89-65.62 0-90.51l-101.49-101.49c-24.89-24.89-65.62-24.89-90.51 0l-301.332 301.332c0 0-301.332-301.332-301.332-301.332-24.89-24.89-65.62-24.89-90.51 0l-101.49 101.49c-24.89 24.89-24.89 65.62 0 90.51l301.332 301.332c0 0-301.332 301.332-301.332 301.332-24.89 24.89-24.89 65.62 0 90.51l101.49 101.49c24.89 24.89 65.62 24.89 90.51 0l301.332-301.332c0 0 301.332 301.332 301.332 301.332 24.89 24.89 65.62 24.89 90.51 0l101.49-101.49c24.89-24.89 24.89-65.62 0-90.51 0 0-301.332-301.332-301.332-301.332z" />
-<glyph unicode="&#xe91f;" glyph-name="icon-brackets" d="M832 832h-192v-192h191.66l0.34-0.34v-639.32l-0.34-0.34h-191.66v-192h192c105.6 0 192 86.4 192 192v640c0 105.6-86.4 192-192 192zM384 0h-191.66l-0.34 0.34v639.32l0.34 0.34h191.66v192h-192c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h192v192z" />
-<glyph unicode="&#xe920;" glyph-name="icon-crosshair" d="M574 834h-128v-320h128v320zM1022 386h-320v-128h320v128zM574 130h-128v-320h128v320zM318 386h-320v-128h320v128z" />
-<glyph unicode="&#xe921;" glyph-name="icon-grippy" d="M365.4 649.2c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM365.4 429.8c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM365.4 210.2c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM365.4-9.2c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM584.8 758.8c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM584.8 539.4c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM584.8 320c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM584.8 100.6c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM584.8-118.8c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM804.2 649.2c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM804.2 429.8c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM804.2 210.2c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM804.2-9.2c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2z" />
-<glyph unicode="&#xe922;" glyph-name="icon-grid" d="M0 256v-256c0-105.6 86.4-192 192-192h256v448h-448zM448 832h-256c-105.6 0-192-86.4-192-192v-256h448v448zM832 832h-256v-448h448v256c0 105.6-86.4 192-192 192zM576-192h256c105.6 0 192 86.4 192 192v256h-448v-448z" />
-<glyph unicode="&#xe923;" glyph-name="icon-grippy-ew" d="M704 832h128v-1024h-128v1024zM448 832h128v-1024h-128v1024zM192 832h128v-1024h-128v1024z" />
-<glyph unicode="&#xe924;" glyph-name="icon-columns" d="M0 832h256v-1024h-256v1024zM384 832h256v-1024h-256v1024zM768 832h256v-1024h-256v1024z" />
-<glyph unicode="&#xe925;" glyph-name="icon-rows" d="M0 832h1024v-256h-1024v256zM0 448h1024v-256h-1024v256zM0 64h1024v-256h-1024v256z" />
-<glyph unicode="&#xe926;" glyph-name="icon-filter" d="M896 832h-768c-70.601-0.227-127.773-57.399-128-127.978v-768.022c0.227-70.601 57.399-127.773 127.978-128h256.022v512l-192 192h640l-192-192v-512h256c70.601 0.227 127.773 57.399 128 127.978v768.022c-0.227 70.601-57.399 127.773-127.978 128h-0.022z" />
-<glyph unicode="&#xe927;" glyph-name="icon-filter-outline" d="M896 832h-768c-70.601-0.227-127.773-57.399-128-127.978v-768.022c0.227-70.601 57.399-127.773 127.978-128h768.022c70.601 0.227 127.773 57.399 128 127.978v768.022c-0.227 70.601-57.399 127.773-127.978 128h-0.022zM896-63.8h-256v383.8l192 192h-640l192-192v-384h-256v767.8h768z" />
-<glyph unicode="&#xe928;" glyph-name="icon-suitcase" d="M768 704c-0.080 70.66-57.34 127.92-127.993 128h-256.007c-70.66-0.080-127.92-57.34-128-127.993v-128.007h-64v-768h640v768h-64zM384 703.88l0.12 0.12 255.88-0.12v-127.88h-256zM0 512v-640c0.102-35.305 28.695-63.898 63.99-64h64.010v768h-64c-35.305-0.102-63.898-28.695-64-63.99v-0.010zM960 576h-64v-768h64c35.305 0.102 63.898 28.695 64 63.99v640.010c-0.102 35.305-28.695 63.898-63.99 64h-0.010z" />
-<glyph unicode="&#xe929;" glyph-name="icon-cursor-locked" horiz-adv-x="768" d="M704 512h-64v64c0 141.385-114.615 256-256 256s-256-114.615-256-256v0-64h-64c-35.301-0.113-63.887-28.699-64-63.989v-576.011c0.113-35.301 28.699-63.887 63.989-64h640.011c35.301 0.113 63.887 28.699 64 63.989v576.011c-0.113 35.301-28.699 63.887-63.989 64h-0.011zM256 576c0 70.692 57.308 128 128 128s128-57.308 128-128v0-64h-256zM533.4-64l-128 128-43-85-170.4 383.6 383.6-170.2-85-43 128-128z" />
-<glyph unicode="&#xe92a;" glyph-name="icon-flag" d="M192 192h832l-192 320 192 320h-896c-70.606-0.215-127.785-57.394-128-127.979v-896.021h192z" />
-<glyph unicode="&#xe92b;" glyph-name="icon-eye-disabled" d="M209.46 223.32q-7.46 9.86-14.26 20.28c-14.737 21.984-27.741 47.184-37.759 73.847l-0.841 2.553c11.078 29.259 24.068 54.443 39.51 77.869l-0.91-1.469c23.221 34.963 50.705 64.8 82.207 89.793l0.793 0.607c57.663 45.719 130.179 75.053 209.311 79.947l1.069 0.053 114.48 140.88c-27.366 5.017-58.869 7.898-91.041 7.92h-0.019c-245.8 0-452.2-168-510.8-395.6 21.856-82.93 60.906-154.847 113.325-214.773l-0.525 0.613zM814.76 416.92q7.52-10 14.44-20.52c14.737-21.984 27.741-47.184 37.759-73.847l0.841-2.553c-10.859-29.216-23.863-54.416-39.447-77.748l0.847 1.348c-23.221-34.963-50.705-64.8-82.207-89.793l-0.793-0.607c-57.762-45.834-130.437-75.216-209.743-80.049l-1.057-0.051-114.46-140.86c27.346-4.988 58.817-7.84 90.955-7.84 0.037 0 0.074 0 0.111 0h-0.005c245.8 0 452.2 168 510.8 395.6-21.856 82.93-60.906 154.847-113.325 214.773l0.525-0.613zM832 832l-832-1024h192l832 1024h-192z" />
-<glyph unicode="&#xe92c;" glyph-name="icon-notebook-page" d="M830 770h-830l-4-702c0-106.6 87.4-194 194-194h640c106.6 0 194 87.4 194 194v508c0 106.8-87.4 194-194 194zM832 386l-384-384-192 192v256l192-192 384 384v-256z" />
-<glyph unicode="&#xe92d;" glyph-name="icon-unlocked" d="M768 832c-141.339-0.114-255.886-114.661-256-255.989v-128.011h-448c-35.301-0.113-63.887-28.699-64-63.989v-512.011c0.113-35.301 28.699-63.887 63.989-64h638.011c35.301 0.113 63.887 28.699 64 63.989v512.011c-0.113 35.301-28.699 63.887-63.989 64h-62.011v128c0 70.692 57.308 128 128 128s128-57.308 128-128v0-128h128v128c-0.114 141.339-114.661 255.886-255.989 256h-0.011z" />
-<glyph unicode="&#xe92e;" glyph-name="icon-circle" d="M1024 320c0-282.77-229.23-512-512-512s-512 229.23-512 512c0 282.77 229.23 512 512 512s512-229.23 512-512z" />
-<glyph unicode="&#xe92f;" glyph-name="icon-draft" d="M876.34 196.42l-49.9-49.88-19.26-19.5-26-8.7-423.040-144.2 144.2 423.28 8.84 25.78 150 149.88-85.6 149.78c-34.92 61.12-92 61.12-127 0l-422.78-739.72c-34.94-61.14-5.92-111.14 64.48-111.14h843.44c70.4 0 99.42 50 64.48 111.14zM973.18 589.16c-19.32 19.3-40.66 34.62-60.16 43.16-34.42 15.12-52.38 4.54-60.1-3.16l-258.12-258.12-82.8-243.040 243 82.8 3.36 3.4 254.76 254.76c4.94 4.94 10.88 13.88 10.88 28.3 0 25.34-19.5 60.56-50.82 91.9zM631 212.18l-34.88 34.86 34.64 101.6 9.24 3.36h32v-64h64v-32l-3.42-9.26z" />
-<glyph unicode="&#xea00;" glyph-name="icon-arrows-right-left" d="M1024 320l-448-512v1024zM448 832l-448-512 448-512z" />
-<glyph unicode="&#xea01;" glyph-name="icon-arrows-up-down" d="M512 832l512-448h-1024zM0 256l512-448 512 448z" />
-<glyph unicode="&#xea02;" glyph-name="icon-bullet" d="M832 80c0-44-36-80-80-80h-480c-44 0-80 36-80 80v480c0 44 36 80 80 80h480c44 0 80-36 80-80v-480z" />
-<glyph unicode="&#xea03;" glyph-name="icon-calendar" d="M896 832h-768c-70.4 0-128-57.6-128-128v-768c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v768c0 70.4-57.6 128-128 128zM640 384h-256v192h256v-192zM384 320h256v-192h-256v192zM320 128h-256v192h256v-192zM320 576v-192h-256v192h256zM128-128c-17 0-33 6.6-45.2 18.8s-18.8 28.2-18.8 45.2v128h256v-192h-192zM384-128v192h256v-192h-256zM960-64c0-17-6.6-33-18.8-45.2s-28.2-18.8-45.2-18.8h-192v192h256v-128zM960 128h-256v192h256v-192zM960 384h-256v192h256v-192z" />
-<glyph unicode="&#xea04;" glyph-name="icon-chain-links" d="M958.4 766.4c-43.8 43.8-101 65.6-158.4 65.6s-114.6-21.8-158.4-65.6l-128-128c-74-74-85.4-187-34-273l-12.8-12.8c-35.4 20.8-75 31.4-114.8 31.4-57.4 0-114.6-21.8-158.4-65.6l-128-128c-87.4-87.4-87.4-229.4 0-316.8 43.8-43.8 101-65.6 158.4-65.6s114.6 21.8 158.4 65.6l128 128c74 74 85.4 187 34 273l12.8 12.8c35.2-21 75-31.6 114.6-31.6 57.4 0 114.6 21.8 158.4 65.6l128 128c87.6 87.6 87.6 229.6 0.2 317zM419.8 92.2l-128-128c-18-18.2-42.2-28.2-67.8-28.2s-49.8 10-67.8 28.2c-37.4 37.4-37.4 98.4 0 135.8l128 128c18.2 18.2 42.2 28.2 67.8 28.2 5.6 0 11.2-0.6 16.8-1.4l-55.6-55.6c-10.4-10.4-16.2-24.2-16.2-38.8s5.8-28.6 16.2-38.8c10.4-10.4 24.2-16.2 38.8-16.2s28.6 5.8 38.8 16.2l55.6 55.6c5.4-30.4-3.6-62.2-26.6-85zM867.8 540.2l-128-128c-18-18.2-42.2-28.2-67.8-28.2-5.6 0-11.2 0.6-16.8 1.4l55.6 55.6c10.4 10.4 16.2 24.2 16.2 38.8s-5.8 28.6-16.2 38.8c-10.4 10.4-24.2 16.2-38.8 16.2s-28.6-5.8-38.8-16.2l-55.6-55.6c-5.2 29.8 3.6 61.6 26.6 84.6l128 128c18 18.4 42.2 28.4 67.8 28.4s49.8-10 67.8-28.2c37.6-37.4 37.6-98.2 0-135.6z" />
-<glyph unicode="&#xea05;" glyph-name="icon-download" d="M832 256v-255.66l-0.34-0.34-639.66 0.34v255.66h-192v-256c0-105.6 86.4-192 192-192h640c105.6 0 192 86.4 192 192v256h-192zM512 192l448 448h-256v192h-384v-192h-256l448-448z" />
-<glyph unicode="&#xea06;" glyph-name="icon-duplicate" d="M640 576v128c0 70.4-57.6 128-128 128h-384c-70.4 0-128-57.6-128-128v-384c0-70.4 57.6-128 128-128h128v139.6c0 134.8 109.6 244.4 244.4 244.4h139.6zM896 448h-384c-70.4 0-128-57.6-128-128v-384c0-70.4 57.6-128 128-128h384c70.4 0 128 57.6 128 128v384c0 70.4-57.6 128-128 128z" />
-<glyph unicode="&#xea07;" glyph-name="icon-folder-new" d="M896 640h-320c-16.4 16.4-96.8 96.8-109.2 109.2l-37.4 37.4c-25 25-74.2 45.4-109.4 45.4h-256c-35.2 0-64-28.8-64-64v-384c0 70.4 57.6 128 128 128h768c70.4 0 128-57.6 128-128v128c0 70.4-57.6 128-128 128zM896 384h-768c-70.4 0-128-57.6-128-128v-320c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v320c0 70.4-57.6 128-128 128zM704 32h-128v-128h-128v128h-128v128h128v128h128v-128h128v-128z" />
-<glyph unicode="&#xea08;" glyph-name="icon-fullscreen-collapse" d="M191.656 0c0.118-0.1 0.244-0.224 0.344-0.344v-191.656h192v192c0 105.6-86.4 192-192 192h-192v-192h191.656zM192 640.344c-0.1-0.118-0.224-0.244-0.344-0.344h-191.656v-192h192c105.6 0 192 86.4 192 192v192h-192v-191.656zM832 448h192v192h-191.656c-0.118 0.1-0.244 0.226-0.344 0.344v191.656h-192v-192c0-105.6 86.4-192 192-192zM832-0.344c0.1 0.118 0.224 0.244 0.344 0.344h191.656v192h-192c-105.6 0-192-86.4-192-192v-192h192v191.656z" />
-<glyph unicode="&#xea09;" glyph-name="icon-fullscreen-expand" d="M192.344 0c-0.118 0.1-0.244 0.224-0.344 0.344v191.656h-192v-192c0-105.6 86.4-192 192-192h192v192h-191.656zM192 639.656c0.1 0.118 0.224 0.244 0.344 0.344h191.656v192h-192c-105.6 0-192-86.4-192-192v-192h192v191.656zM832 832h-192v-192h191.656c0.118-0.1 0.244-0.226 0.344-0.344v-191.656h192v192c0 105.6-86.4 192-192 192zM832 0.344c-0.1-0.118-0.224-0.244-0.344-0.344h-191.656v-192h192c105.6 0 192 86.4 192 192v192h-192v-191.656z" />
-<glyph unicode="&#xea0a;" glyph-name="icon-layers" d="M1024 448l-512 384-512-384 512-384zM512-64l-426.666 320-85.334-64 512-384 512 384-85.334 64z" />
-<glyph unicode="&#xea0b;" glyph-name="icon-line-horz" d="M64 256c-35.346 0-64 28.654-64 64s28.654 64 64 64h896c35.346 0 64-28.654 64-64s-28.654-64-64-64h-896z" />
-<glyph unicode="&#xea0c;" glyph-name="icon-magnify" d="M1024-64l-256.8 256.8c42.4 66.6 65 144 64.8 223.2 0 229.8-186.2 416-416 416s-416-186.2-416-416 186.2-416 416-416c79-0.2 156.4 22.4 223.2 64.8l256.8-256.8 128 128zM212.4 212.4c-112.4 112.4-112.4 294.8 0 407.2s294.8 112.4 407.2 0 112.4-294.8 0-407.2c-54-54-127.2-84.4-203.6-84.4-76.4-0.2-149.8 30.2-203.6 84.4z" />
-<glyph unicode="&#xea0d;" glyph-name="icon-magnify-in" d="M1024-64l-256.86 256.86c40.681 62.963 64.861 139.898 64.861 222.481 0 0.232 0 0.464-0.001 0.696v-0.036c0 229.76-186.24 416-416 416s-416-186.24-416-416 186.24-416 416-416c0.196 0 0.427-0.001 0.659-0.001 82.583 0 159.518 24.18 224.112 65.846l-1.631-0.985 256.86-256.86zM212.36 212.36c-52.114 52.117-84.346 124.114-84.346 203.64 0 159.058 128.942 288 288 288s288-128.942 288-288c0-159.058-128.942-288-288-288-0.005 0-0.010 0-0.014 0h0.001c-0.242-0.001-0.529-0.001-0.815-0.001-79.271 0-151.010 32.251-202.811 84.348l-0.013 0.014zM224 480h384v-128h-384v128zM352 608h128v-384h-128v384z" />
-<glyph unicode="&#xea0e;" glyph-name="icon-magnify-out-v2" d="M767.2 192.8c42.4 66.6 65 144 64.8 223.2 0 229.8-186.2 416-416 416s-416-186.2-416-416 186.2-416 416-416c79-0.2 156.4 22.4 223.2 64.8l256.8-256.8 128 128-256.8 256.8zM619.6 212.4c-54-54-127.2-84.4-203.6-84.4-76.4-0.2-149.8 30.2-203.6 84.4-112.4 112.4-112.4 294.8 0 407.2s294.8 112.4 407.2 0c112.4-112.4 112.4-294.8 0-407.2zM224 480h384v-128h-384v128z" />
-<glyph unicode="&#xea0f;" glyph-name="icon-menu" d="M0 704h1024v-128h-1024v128zM0 384h1024v-128h-1024v128zM0 64h1024v-128h-1024v128z" />
-<glyph unicode="&#xea10;" glyph-name="icon-move" d="M293.4 320l218.6 218.6 256-256v421.4c0 70.4-57.6 128-128 128h-512c-70.4 0-128-57.6-128-128v-512c0-70.4 57.6-128 128-128h421.4l-256 256zM1024 384h-128v-320l-384 384-128-128 384-384h-320v-128h576z" />
-<glyph unicode="&#xea11;" glyph-name="icon-new-window" d="M448 832v-128h320l-384-384 128-128 384 384v-320h128v576zM576 157.726v-157.382c-0.1-0.118-0.226-0.244-0.344-0.344h-383.312c-0.118 0.1-0.244 0.226-0.344 0.344v383.312c0.1 0.118 0.226 0.244 0.344 0.344h157.382l192 192h-349.726c-105.6 0-192-86.4-192-192v-384c0-105.6 86.4-192 192-192h384c105.6 0 192 86.4 192 192v349.726l-192-192z" />
-<glyph unicode="&#xea12;" glyph-name="icon-paint-bucket-v2" d="M544 608v-224c0-88.4-71.6-160-160-160s-160 71.6-160 160v97.2l-197.4-196.4c-50-50-12.4-215.2 112.4-340s290-162.4 340-112.4l417 423.6-352 352zM896-192c70.6 0 128 57.4 128 128 0 108.6-128 192-128 192s-128-83.4-128-192c0-70.6 57.4-128 128-128zM384 320c-35.4 0-64 28.6-64 64v384c0 35.4 28.6 64 64 64s64-28.6 64-64v-384c0-35.4-28.6-64-64-64z" />
-<glyph unicode="&#xea13;" glyph-name="icon-pencil" d="M922.344 730.32c-38.612 38.596-81.306 69.232-120.304 86.324-68.848 30.25-104.77 9.078-120.194-6.344l-516.228-516.216-3.136-9.152-162.482-476.932 485.998 165.612 6.73 6.806 509.502 509.506c9.882 9.866 21.768 27.77 21.768 56.578 0.002 50.71-38.996 121.148-101.654 183.818zM237.982-23.66l-69.73 69.728 69.25 203.228 18.498 6.704h64v-128h128v-64l-6.846-18.506-203.172-69.154z" />
-<glyph unicode="&#xea14;" glyph-name="icon-pencil-edit-in-place" d="M922.4 730.4c-38.6 38.6-81.4 69.2-120.4 86.2-68.8 30.2-104.8 9-120.2-6.4l-516.2-516.2-3.2-9.2-162.4-476.8 486 165.6 516.2 516.4c9.8 9.8 21.8 27.8 21.8 56.6 0 50.6-39 121-101.6 183.8zM238-23.6l-69.8 69.6 69.2 203.2 18.4 6.8h64v-128h128v-64l-6.8-18.6-203-69zM0 832v-512l128 128v256h256l128 128zM1024-192v512l-128-128v-256h-256l-128-128z" />
-<glyph unicode="&#xea15;" glyph-name="icon-play" d="M1024 320l-1024-512v1024z" />
-<glyph unicode="&#xea16;" glyph-name="icon-pause" d="M126 834h256v-1024h-256v1024zM638 834h256v-1024h-256v1024z" />
-<glyph unicode="&#xea17;" glyph-name="icon-plot-resource" d="M255.8 128c0.2 0 0.2 0 0 0l0.2 128c0 70.6 57.4 128 128 128h255.8c0 0 0 0 0.2 0.2v127.8c0 70.6 57.4 128 128 128h143.6c-93.8 117-238 192-399.6 192-282.8 0-512-229.2-512-512 0-68 13.2-132.8 37.2-192h218.6zM768.2 512c-0.2 0-0.2 0 0 0l-0.2-128c0-70.6-57.4-128-128-128h-255.8c0 0 0 0-0.2-0.2v-127.8c0-70.6-57.4-128-128-128h-143.6c93.8-117 238-192 399.6-192 282.8 0 512 229.2 512 512 0 68-13.2 132.8-37.2 192h-218.6z" />
-<glyph unicode="&#xea18;" glyph-name="icon-pointer-left" d="M766-192l-256 512 256 512h-256l-256-512 256-512z" />
-<glyph unicode="&#xea19;" glyph-name="icon-pointer-right" d="M254 832l256-512-256-512h256l256 512-256 512z" />
-<glyph unicode="&#xea1a;" glyph-name="icon-refresh" d="M1024 371.2v460.8l-175.8-175.8c-85.2 69.6-190.8 107.6-302 107.6-127.6 0-247.6-49.8-338-140s-140-210.4-140-338 49.8-247.6 140-338 210.4-140 338-140 247.6 49.8 338 140c74 74 120.8 167.8 135 269.6h-138.6c-32-155.4-169.8-272.8-334.6-272.8-188.2 0-341.4 153.2-341.4 341.4s153.4 341.2 341.6 341.2c76.8 0 147.6-25.4 204.8-68.2l-187.8-187.8h460.8z" />
-<glyph unicode="&#xea1b;" glyph-name="icon-save" d="M192.2 256c-0.2 0-0.2 0 0 0l-0.2-448h640v447.8c0 0 0 0-0.2 0.2h-639.6zM978.8 621.2l-165.4 165.4c-25 25-74.2 45.4-109.4 45.4h-576c-70.4 0-128-57.6-128-128v-768c0-70.4 57.6-128 128-128v448c0 35.2 28.8 64 64 64h640c35.2 0 64-28.8 64-64v-448c70.4 0 128 57.6 128 128v576c0 35.2-20.4 84.4-45.2 109.2zM704 576c0-35.2-28.8-64-64-64h-448c-35.2 0-64 28.8-64 64v192h320v-192h128v192h128v-192z" />
-<glyph unicode="&#xea1c;" glyph-name="icon-save-as" d="M978.8 493.2l-64 64c24.8-24.8 45.2-74 45.2-109.2v-448c0-70.4-57.6-128-128-128h-640c-18.8 0-36.6 4.2-52.6 11.4 20.2-44.4 65-75.4 116.6-75.4h640c70.4 0 128 57.6 128 128v448c0 35.2-20.4 84.4-45.2 109.2zM704-64v319.8c0 0 0 0-0.2 0.2h-511.6l-0.2-320h512zM192 320h512c35.2 0 64-28.8 64-64v-320c70.4 0 128 57.6 128 128v448c0 35.2-20.4 84.4-45.2 109.2l-165.4 165.4c-25 25-74.2 45.4-109.4 45.4h-448c-70.4 0-128-57.6-128-128v-640c0-70.4 57.6-128 128-128v320c0 35.2 28.8 64 64 64zM128 768h192v-192h128v192h128v-192c0-35.2-28.8-64-64-64h-320c-35.2 0-64 28.8-64 64v192z" />
-<glyph unicode="&#xea1d;" glyph-name="icon-sine" d="M1024 320c-1.8 7.2-3.4 14.4-5.2 21.8-20.2 86.2-53.4 209.4-98.4 307.2-22.4 49-45.4 86.6-70.2 115.2-48.6 56-98.4 67.8-131.8 67.8-33.2 0-83.2-11.8-131.8-67.8-24.6-28.6-47.6-66.2-70-115.2-44.8-97.8-78.2-221-98.4-307.2-21.8-93-46.6-175.4-72-238.4-16.4-40.6-30.4-66.4-40.8-82.8-10.4 16.2-24.4 42.2-40.8 82.8-23.2 58-46.2 132.4-66.6 216.6h-198c1.8-7.2 3.4-14.4 5.2-21.8 20.2-86.2 53.4-209.4 98.4-307.2 22.4-49 45.4-86.6 70.2-115.2 48.6-56 98.6-67.8 131.8-67.8s83.2 11.8 131.8 67.8c24.8 28.6 47.6 66.2 70.2 115.2 44.8 97.8 78.2 221 98.4 307.2 21.8 93 46.6 175.4 72 238.4 16.4 40.6 30.4 66.4 40.8 82.8 10.4-16.2 24.4-42.2 40.8-82.8 23.4-57.8 46.4-132.4 66.8-216.4h197.6z" />
-<glyph unicode="&#xea1e;" glyph-name="icon-font" d="M800-192h224l-384 1024h-256l-384-1024h224l84 224h408zM380 224l132 352 132-352z" />
-<glyph unicode="&#xea1f;" glyph-name="icon-thumbs-strip" d="M448 450c0-35.2-28.8-64-64-64h-320c-35.2 0-64 28.8-64 64v320c0 35.2 28.8 64 64 64h320c35.2 0 64-28.8 64-64v-320zM1024 450c0-35.2-28.8-64-64-64h-320c-35.2 0-64 28.8-64 64v320c0 35.2 28.8 64 64 64h320c35.2 0 64-28.8 64-64v-320zM448-126c0-35.2-28.8-64-64-64h-320c-35.2 0-64 28.8-64 64v320c0 35.2 28.8 64 64 64h320c35.2 0 64-28.8 64-64v-320zM1024-126c0-35.2-28.8-64-64-64h-320c-35.2 0-64 28.8-64 64v320c0 35.2 28.8 64 64 64h320c35.2 0 64-28.8 64-64v-320z" />
-<glyph unicode="&#xea20;" glyph-name="icon-two-parts-both" d="M896 832h-768c-70.4 0-128-57.6-128-128v-768c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v768c0 70.4-57.6 128-128 128zM128 704h320v-768h-320v768zM896-64h-320v768h320v-768z" />
-<glyph unicode="&#xea21;" glyph-name="icon-two-parts-one-only" d="M896 832h-768c-70.4 0-128-57.6-128-128v-768c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v768c0 70.4-57.6 128-128 128zM896-64h-320v768h320v-768z" />
-<glyph unicode="&#xea22;" glyph-name="icon-resync" d="M795.2 667.2c-79.8 65.2-178.8 100.8-283.2 100.8-119.6 0-232.2-46.6-316.8-131.2-69.4-69.4-113.2-157.4-126.6-252.8h130c29.6 145.8 158.8 256 313.4 256 72 0 138.4-23.8 192-64l-176-176h432v432l-164.8-164.8zM512 0c-72 0-138.4 23.8-192 64l176 176h-432v-432l164.8 164.8c79.8-65.2 178.8-100.8 283.2-100.8 119.6 0 232.2 46.6 316.8 131.2 69.4 69.4 113.2 157.4 126.6 252.8h-130c-29.6-145.8-158.8-256-313.4-256z" />
-<glyph unicode="&#xea23;" glyph-name="icon-reset" d="M460.8 371.2l-187.8 187.8c57.2 42.8 128 68.2 204.8 68.2 188.2 0 341.6-153.2 341.6-341.4s-153.2-341.2-341.4-341.2c-165 0-302.8 117.6-334.6 273h-138.4c14.2-101.8 61-195.6 135-269.6 90.2-90.2 210.4-140 338-140s247.6 49.8 338 140 140 210.4 140 338-49.8 247.6-140 338-210.4 140-338 140c-111.4 0-217-38-302-107.6l-176 175.6v-460.8h460.8z" />
-<glyph unicode="&#xea24;" glyph-name="icon-x-in-circle" d="M512 832c-282.8 0-512-229.2-512-512s229.2-512 512-512 512 229.2 512 512-229.2 512-512 512zM832 128l-128-128-192 192-192-192-128 128 192 192-192 192 128 128 192-192 192 192 128-128-192-192 192-192z" />
-<glyph unicode="&#xea25;" glyph-name="icon-brightness" d="M253.414 513.939l-155.172 116.384c-50.233-66.209-85.127-146.713-97.91-234.39l-0.333-2.781 191.919-27.434c8.145 56.552 29.998 106.879 62.068 149.006l-0.573-0.784zM191.98 274.283l-191.919-27.434c13.115-90.459 48.009-170.963 99.174-238.453l-0.931 1.281 155.111 116.384c-31.476 41.347-53.309 91.675-61.231 146.504l-0.204 1.719zM466.283 640.020l-27.434 191.919c-90.459-13.115-170.963-48.009-238.453-99.174l1.281 0.931 116.384-155.111c41.347 31.476 91.675 53.309 146.504 61.231l1.719 0.204zM822.323 733.758c-66.209 50.233-146.713 85.127-234.39 97.91l-2.781 0.333-27.434-191.919c56.552-8.145 106.879-29.998 149.006-62.068l-0.784 0.573zM832.020 365.717l191.919 27.434c-13.115 90.459-48.009 170.963-99.174 238.453l0.931-1.281-155.111-116.384c31.476-41.347 53.309-91.675 61.231-146.504l0.204-1.719zM201.677-93.758c66.209-50.233 146.713-85.127 234.39-97.91l2.781-0.333 27.434 191.919c-56.552 8.145-106.879 29.998-149.006 62.068l0.784-0.573zM770.586 126.061l155.131-116.343c50.233 66.209 85.127 146.713 97.91 234.39l0.333 2.781-191.919 27.434c-8.125-56.564-29.966-106.906-62.028-149.049l0.574 0.786zM557.717-0.020l27.434-191.919c90.459 13.115 170.963 48.009 238.453 99.174l-1.281-0.931-116.384 155.111c-41.347-31.476-91.675-53.309-146.504-61.231l-1.719-0.204zM770.586 320c0-142.813-115.773-258.586-258.586-258.586s-258.586 115.773-258.586 258.586c0 142.813 115.773 258.586 258.586 258.586s258.586-115.773 258.586-258.586z" />
-<glyph unicode="&#xea26;" glyph-name="icon-contrast" d="M512 832c-282.78 0-512-229.24-512-512s229.22-512 512-512 512 229.24 512 512-229.22 512-512 512zM783.52 48.48c-69.111-69.481-164.785-112.481-270.502-112.481-0.358 0-0.716 0-1.074 0.001h0.055v768c212.070-0.010 383.982-171.929 383.982-384 0-106.034-42.977-202.031-112.462-271.52v0z" />
-<glyph unicode="&#xea27;" glyph-name="icon-expand" d="M960 832c0 0 0 0 0 0h-320v-128h165.4l-210.6-210.8c-25-25-25-65.6 0-90.6 12.4-12.4 28.8-18.8 45.2-18.8s32.8 6.2 45.2 18.8l210.8 210.8v-165.4h128v384h-64zM896 26.6l-210.8 210.6c-25 25-65.6 25-90.6 0s-25-65.6 0-90.6l210.8-210.6h-165.4v-128h384v384h-128v-165.4zM218.6 704h165.4v128h-320c0 0 0 0 0 0h-64v-384h128v165.4l210.8-210.8c12.4-12.4 28.8-18.8 45.2-18.8s32.8 6.2 45.2 18.8c25 25 25 65.6 0 90.6l-210.6 210.8zM338.8 237.2l-210.8-210.6v165.4h-128v-384h384v128h-165.4l210.8 210.8c25 25 25 65.6 0 90.6-25.2 24.8-65.6 24.8-90.6-0.2z" />
-<glyph unicode="&#xea28;" glyph-name="icon-list-view" d="M0 768h1024v-128h-1024v128zM0 512h1024v-128h-1024v128zM0 256h1024v-128h-1024v128zM0 0h1024v-128h-1024v128z" />
-<glyph unicode="&#xea29;" glyph-name="icon-grid-snap-to" d="M382 2h448v448h-448v-448zM510 322h192v-192h-192v192zM-2 258h320v-64h-320v64zM894 258h128v-64h-128v64zM574 834h64v-320h-64v320zM574-62h64v-128h-64v128zM574 258h64v-64h-64v64z" />
-<glyph unicode="&#xea2a;" glyph-name="icon-grid-snap-no" d="M768 256h192v-64h-192v64zM256 256h192v-64h-192v64zM0 256h192v-64h-192v64zM640 320h-64v-64h-64v-64h64v-64h64v64h64v64h-64zM576 576h64v-192h-64v192zM576 832h64v-192h-64v192zM576 64h64v-192h-64v192z" />
-<glyph unicode="&#xea2b;" glyph-name="icon-frame-show" d="M0 768v-896h1024v896h-1024zM896 0h-768v640h768v-640zM192 576h384v-128h-384v128z" />
-<glyph unicode="&#xea2c;" glyph-name="icon-frame-hide" d="M128 642h420l104 128h-652v-802.4l128 157.4zM896 2h-420l-104-128h652v802.4l-128-157.4zM832 834l-832-1024h192l832 1024zM392 450l104 128h-304v-128z" />
-<glyph unicode="&#xea2d;" glyph-name="icon-import" d="M832 639.6v-639.4c0-0.2-0.2-0.2-0.4-0.4h-319.6v-192h320c105.6 0 192 86.4 192 192v640.2c0 105.6-86.4 192-192 192h-320v-192h319.6c0.2 0 0.4-0.2 0.4-0.4zM192 128v-192l384 384-384 384v-192h-192v-384z" />
-<glyph unicode="&#xea2e;" glyph-name="icon-export" d="M192 0.34v639.32l0.34 0.34h319.66v192h-320c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h320v192h-319.66zM1024 320l-384 384v-192h-192v-384h192v-192l384 384z" />
-<glyph unicode="&#xea2f;" glyph-name="icon-font-size" horiz-adv-x="1504" d="M1226.4 512h-176l-76.22-203.24 77-205.34 87.22 232.58 90.74-242h-174.44l49.5-132h174.44l57.76-154h154l-264 704zM384 832l-384-1024h224l84 224h408l84-224h224l-384 1024zM380 224l132 352 132-352z" />
-<glyph unicode="&#xea30;" glyph-name="icon-clear-data" d="M632 520l-120-120-120 120-80-80 120-120-120-120 80-80 120 120 120-120 80 80-120 120 120 120-80 80zM512 832c-282.76 0-512-86-512-192v-640c0-106 229.24-192 512-192s512 86 512 192v640c0 106-229.24 192-512 192zM512 0c-176.731 0-320 143.269-320 320s143.269 320 320 320c176.731 0 320-143.269 320-320v0c0-176.731-143.269-320-320-320v0z" />
-<glyph unicode="&#xea31;" glyph-name="icon-history" d="M576 768c-247.4 0-448-200.6-448-448h-128l192-192 192 192h-128c0 85.4 33.2 165.8 93.8 226.2 60.4 60.6 140.8 93.8 226.2 93.8s165.8-33.2 226.2-93.8c60.6-60.4 93.8-140.8 93.8-226.2s-33.2-165.8-93.8-226.2c-60.4-60.6-140.8-93.8-226.2-93.8s-165.8 33.2-226.2 93.8l-90.6-90.6c81-81 193-131.2 316.8-131.2 247.4 0 448 200.6 448 448s-200.6 448-448 448zM576 560c-26.6 0-48-21.4-48-48v-211.8l142-142c9.4-9.4 21.6-14 34-14s24.6 4.6 34 14c18.8 18.8 18.8 49.2 0 67.8l-114 114v172c0 26.6-21.4 48-48 48z" />
-<glyph unicode="&#xea32;" glyph-name="icon-arrow-up-to-parent" horiz-adv-x="1056" d="M643.427 6.739c-81.955 0.697-148.179 67.065-148.642 149.010v395.872l296.871-247.393v197.914l-395.828 329.857-395.828-328.62v-197.502l296.871 246.156v-396.241c0-190.905 155.239-346.556 346.144-346.968l412.321-0.825 0.412 197.914z" />
-<glyph unicode="&#xea33;" glyph-name="icon-crosshair-in-circle" d="M512 832c-282.8 0-512-229.2-512-512s229.2-512 512-512 512 229.2 512 512-229.2 512-512 512zM783.6 48.4c-54.634-54.8-125.77-93.12-205.322-106.874l-2.278-0.326v250.8h-128v-250.8c-161.302 28.062-286.738 153.497-314.468 312.5l-0.332 2.3h250.8v128h-250.8c28.062 161.302 153.497 286.738 312.5 314.468l2.3 0.332v-250.8h128v250.8c161.302-28.062 286.738-153.497 314.468-312.5l0.332-2.3h-250.8v-128h250.8c-14.080-81.83-52.4-152.966-107.191-207.591l-0.009-0.009z" />
-<glyph unicode="&#xea34;" glyph-name="icon-target" d="M512 448c70.692 0 128-57.308 128-128s-57.308-128-128-128c-70.692 0-128 57.308-128 128v0c0.114 70.647 57.353 127.886 127.989 128h0.011zM512 576c-141.385 0-256-114.615-256-256s114.615-256 256-256c141.385 0 256 114.615 256 256v0c-0.114 141.339-114.661 255.886-255.989 256h-0.011zM512 704c211.87-0.128 383.575-171.912 383.575-383.8 0-211.967-171.833-383.8-383.8-383.8s-383.8 171.833-383.8 383.8c0 105.99 42.963 201.945 112.425 271.4v0c69.21 69.437 164.944 112.401 270.713 112.401 0.312 0 0.624 0 0.936-0.001h-0.048zM512 832c-282.8 0-512-229.2-512-512s229.2-512 512-512 512 229.2 512 512-229.2 512-512 512z" />
-<glyph unicode="&#xea35;" glyph-name="icon-items-collapse" d="M45.2 173.2h229.6l-274.8-274.6 90.6-90.6 274.6 274.8v-229.6h128v448h-448v-128zM1024 741.4l-90.6 90.6-274.6-274.8v229.6h-128v-448h448v128h-229.6l274.8 274.6z" />
-<glyph unicode="&#xea36;" glyph-name="icon-items-expand" d="M448-64h-229.4l274.6 274.8-90.4 90.4-274.8-274.6v229.4h-128v-448h448v128zM530.8 429.2l90.4-90.4 274.8 274.6v-229.4h128v448h-448v-128h229.4l-274.6-274.8z" />
-<glyph unicode="&#xea37;" glyph-name="icon-3-dots" d="M256 320c0-70.692-57.308-128-128-128s-128 57.308-128 128c0 70.692 57.308 128 128 128s128-57.308 128-128zM640 320c0-70.692-57.308-128-128-128s-128 57.308-128 128c0 70.692 57.308 128 128 128s128-57.308 128-128zM1024 320c0-70.692-57.308-128-128-128s-128 57.308-128 128c0 70.692 57.308 128 128 128s128-57.308 128-128z" />
-<glyph unicode="&#xea38;" glyph-name="icon-grid-on" d="M1024 448v128h-256v256h-128v-256h-256v256h-128v-256h-256v-128h256v-256h-256v-128h256v-256h128v256h256v-256h128v256h256v128h-256v256zM640 192h-256v256h256z" />
-<glyph unicode="&#xea39;" glyph-name="icon-grid-off" d="M256 280.6l128 157.6v9.8h8l104 128h-112v256h-128v-256h-256v-128h256v-167.4zM184 192h-184v-128h80l104 128zM768 359.4l-128-157.6v-9.8h-8l-104-128h112v-256h128v256h256v128h-256v167.4zM840 448h184v128h-80l-104-128zM832 832l-832-1024h192l832 1024h-192z" />
-<glyph unicode="&#xea3a;" glyph-name="icon-camera" d="M896 576h-128l-128 256h-256l-128-256h-128c-70.601-0.227-127.773-57.399-128-127.978v-512.022c0.227-70.601 57.399-127.773 127.978-128h768.022c70.601 0.227 127.773 57.399 128 127.978v512.022c-0.227 70.601-57.399 127.773-127.978 128h-0.022zM512-32c-141.385 0-256 114.615-256 256s114.615 256 256 256c141.385 0 256-114.615 256-256v0c0-141.385-114.615-256-256-256v0z" />
-<glyph unicode="&#xea3b;" glyph-name="icon-folders-collapse" d="M896 512v-448c-0.215-70.606-57.394-127.785-127.979-128h-576.021c0.215-70.606 57.394-127.785 127.979-128h576.021c70.606 0.215 127.785 57.394 128 127.979v448.021c-0.215 70.606-57.394 127.785-127.979 128h-0.021zM832 128v448c-0.215 70.606-57.394 127.785-127.979 128h-192.021l-101.5 82.74c-24.88 24.9-74.040 45.26-109.24 45.26h-237.26c-35.305-0.102-63.898-28.695-64-63.99v-640.010c0.215-70.606 57.394-127.785 127.979-128h576.021c70.606 0.215 127.785 57.394 128 127.979v0.021zM128 188v516l256-260z" />
-<glyph unicode="&#xeb00;" glyph-name="icon-activity" d="M576 768h-256l320-320h-290.256c-44.264 76.516-126.99 128-221.744 128h-128v-512h128c94.754 0 177.48 51.484 221.744 128h290.256l-320-320h256l448 448-448 448z" />
-<glyph unicode="&#xeb01;" glyph-name="icon-activity-mode" d="M512 832c-214.8 0-398.8-132.4-474.8-320h90.8c56.8 0 108-24.8 143-64h241l-192 192h256l320-320-320-320h-256l192 192h-241c-35-39.2-86.2-64-143-64h-90.8c76-187.6 259.8-320 474.8-320 282.8 0 512 229.2 512 512s-229.2 512-512 512z" />
-<glyph unicode="&#xeb02;" glyph-name="icon-autoflow-tabular" d="M192 832c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h64v1024h-64zM384 832h256v-1024h-256v1024zM832 832h-64v-704h256v512c0 105.6-86.4 192-192 192z" />
-<glyph unicode="&#xeb03;" glyph-name="icon-clock" d="M512 832c-282.8 0-512-229.2-512-512s229.2-512 512-512 512 229.2 512 512-229.2 512-512 512zM782 142c-12.8-22.2-36.6-36-62.4-36-12.6 0-25 3.4-36 9.6l-222 128.2c-0.8 0.4-1.6 1-2.4 1.4l-0.8 0.6-1.8 1.2-2.4 2-1.8 1.4-0.6 0.6c-0.8 0.6-1.4 1.2-2.2 1.8v0c-5 4.6-9.4 10-13 15.8-0.2 0.4-0.6 1-0.8 1.4s-0.6 1-0.8 1.4c-3.2 6-5.8 12.4-7.2 19.2v0.2c-0.2 1-0.4 1.8-0.6 2.8 0 0.2 0 0.6-0.2 0.8-0.2 0.6-0.2 1.4-0.2 2.2s-0.2 1-0.2 1.6 0 1-0.2 1.6-0.2 1.6-0.2 2.2c0 0.4 0 0.6 0 1 0 1 0 1.8 0 2.8 0 0 0 0.2 0 0.4v363.8c0 39.8 32.2 72 72 72s72-32.2 72-72v-322.4l185.8-107.2c34.2-20 45.8-64 26-98.4z" />
-<glyph unicode="&#xeb04;" glyph-name="icon-database" d="M1024 640c0-106.039-229.23-192-512-192s-512 85.961-512 192c0 106.039 229.23 192 512 192s512-85.961 512-192zM512 320c-282.77 0-512 85.962-512 192v-512c0-106.038 229.23-192 512-192s512 85.962 512 192v512c0-106.038-229.23-192-512-192z" />
-<glyph unicode="&#xeb05;" glyph-name="icon-database-query" d="M683.52 12.714c-50.782-28.456-109.284-44.714-171.52-44.714-194.094 0-352 157.906-352 352s157.906 352 352 352 352-157.906 352-352c0-62.236-16.258-120.738-44.714-171.52l191.692-191.692c8.516 13.89 13.022 28.354 13.022 43.212v640c0 106.038-229.23 192-512 192s-512-85.962-512-192v-640c0-106.038 229.23-192 512-192 126.11 0 241.548 17.108 330.776 45.46l-159.256 159.254zM352 320c0-88.224 71.776-160 160-160s160 71.776 160 160-71.776 160-160 160-160-71.776-160-160z" />
-<glyph unicode="&#xeb06;" glyph-name="icon-dataset" d="M896 640h-320c-16.4 16.4-96.8 96.8-109.2 109.2l-37.4 37.4c-25 25-74.2 45.4-109.4 45.4h-256c-35.2 0-64-28.8-64-64v-384c0 70.4 57.6 128 128 128h768c70.4 0 128-57.6 128-128v128c0 70.4-57.6 128-128 128zM896 384h-768c-70.4 0-128-57.6-128-128v-320c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v320c0 70.4-57.6 128-128 128zM320-64h-128v320h128v-320zM576-64h-128v320h128v-320zM832-64h-128v320h128v-320z" />
-<glyph unicode="&#xeb07;" glyph-name="icon-datatable" d="M1024 640c0-106.039-229.23-192-512-192s-512 85.961-512 192c0 106.039 229.23 192 512 192s512-85.961 512-192zM512 320c-282.8 0-512 86-512 192v-512c0-106 229.2-192 512-192s512 86 512 192v512c0-106-229.2-192-512-192zM896 257v-256c-36.6-15.6-79.8-28.8-128-39.4v256c48.2 10.6 91.4 23.8 128 39.4zM256 217.6v-256c-48.2 10.4-91.4 23.8-128 39.4v256c36.6-15.6 79.8-28.8 128-39.4zM384-58v256c41-4 83.8-6 128-6s87 2.2 128 6v-256c-41-4-83.8-6-128-6s-87 2.2-128 6z" />
-<glyph unicode="&#xeb08;" glyph-name="icon-dictionary" d="M832 192c105.6 0 192 86.4 192 192v256c0 105.6-86.4 192-192 192v-320l-128 64-128-64v320h-384c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h640c105.6 0 192 86.4 192 192v192c0-105.6-86.4-192-192-192h-640v192h640z" />
-<glyph unicode="&#xeb09;" glyph-name="icon-folder" d="M896 640h-320c-16.4 16.4-96.8 96.8-109.2 109.2l-37.4 37.4c-25 25-74.2 45.4-109.4 45.4h-256c-35.2 0-64-28.8-64-64v-384c0 70.4 57.6 128 128 128h768c70.4 0 128-57.6 128-128v128c0 70.4-57.6 128-128 128zM896 384h-768c-70.4 0-128-57.6-128-128v-320c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v320c0 70.4-57.6 128-128 128z" />
-<glyph unicode="&#xeb0a;" glyph-name="icon-image" d="M896 832h-768c-70.4 0-128-57.6-128-128v-768c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v768c0 70.4-57.6 128-128 128zM896-64h-768v768h768v-768zM320 576l-128-128v-448h640v320l-128 128-128-128z" />
-<glyph unicode="&#xeb0b;" glyph-name="icon-layout" d="M448 832h-256c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h256v1024zM832 832h-256v-577.664h448v385.664c0 105.6-86.4 192-192 192zM576-192h256c105.6 0 192 86.4 192 192v129.664h-448v-321.664z" />
-<glyph unicode="&#xeb0c;" glyph-name="icon-object" d="M512-192l512 320v384l-512.020 320-511.98-320v-384l512-320zM512 640l358.4-224-358.4-224-358.4 224 358.4 224z" />
-<glyph unicode="&#xeb0d;" glyph-name="icon-object-unknown" d="M510 834l-512-320v-384l512-320 512 320v384l-512 320zM585.4-27.2c-21.2-20.8-46-30.8-76-30.8-31.2 0-56.2 9.8-76.2 29.6-20 20-29.6 44.8-29.6 76.2 0 30.4 10.2 55.2 31 76.2s45.2 31.2 74.8 31.2c29.6 0 54.2-10.4 75.6-32s31.8-46.4 31.8-76c-0.2-29-10.8-54-31.4-74.4zM638.2 285.4c-23.6-11.8-37.4-22-43.4-32.4-3.6-6.2-6-14.8-7.4-26.8v-41h-161.4v44.2c0 40.2 4.4 69.8 13 88 8 17.2 22.6 30.2 44.8 40l34.8 15.4c32 14.2 48.2 35.2 48.2 62.8 0 16-6 30.4-17.2 41.8-11.2 11.2-25.6 17.2-41.6 17.2-24 0-54.4-10-62.8-57.4l-2.2-12.2h-147l1.4 16.2c4 44.6 17 82.4 38.8 112.2 19.6 27 45.6 48.6 77 64.6s64.6 24 98.2 24c60.6 0 110.2-19.4 151.4-59.6 41.2-40 61.2-88 61.2-147.2 0-70.8-28.8-121.4-85.8-149.8z" />
-<glyph unicode="&#xeb0e;" glyph-name="icon-packet" d="M512 832l-512-320v-512c0-105.6 86.4-192 192-192h640c105.6 0 192 86.4 192 192v512l-512 320zM512 640l358.4-224-358.4-224-358.4 224 358.4 224z" />
-<glyph unicode="&#xeb0f;" glyph-name="icon-page" d="M704 320c-105.6 0-192 86.4-192 192v320h-320c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h640c105.6 0 192 86.4 192 192v320h-320zM768 448h256l-384 384v-256c0-70.4 57.6-128 128-128z" />
-<glyph unicode="&#xeb10;" glyph-name="icon-plot-overlay" d="M830 832h-636c-106.7 0-194-87.3-194-194v-406.82c14.18-18.64 25.66-28.34 32-30.84 14.28 5.62 54.44 47.54 92.96 146 42.46 108.38 116.32 237.66 227.040 237.66 52.4 0 101.42-29.16 145.7-86.68 37.34-48.5 64.84-108.92 81.34-151.080 38.52-98.38 78.68-140.3 92.96-146 14.28 5.62 54.44 47.54 92.96 146 42.46 108.48 116.32 237.76 227.040 237.76 11.355-0.003 22.389-1.366 32.952-3.936l-0.952 0.196v57.74c0 106.7-87.3 194-194 194zM992 439.66c-14.28-5.62-54.44-47.52-92.96-146-42.46-108.38-116.32-237.66-227.040-237.66-52.4 0-101.42 29.16-145.7 86.68-37.34 48.5-64.84 108.92-81.34 151.080-38.52 98.38-78.68 140.3-92.96 146-14.28-5.62-54.44-47.52-92.96-146-42.46-108.48-116.32-237.76-227.040-237.76-11.355 0.003-22.389 1.367-32.952 3.936l0.952-0.196v-57.74c0-106.7 87.3-194 194-194h636c106.7 0 194 87.3 194 194v406.82c-14.18 18.64-25.66 28.34-32 30.84z" />
-<glyph unicode="&#xeb11;" glyph-name="icon-plot-stacked" d="M89.6 520c24.98 0 48.96 26.52 85.52 70.18 45.42 54.28 102 121.82 196 121.82 44.64 0 86.62-15.46 124.8-46 28.68-22.9 51.16-50.42 72.92-77.060 38.42-46.94 59.16-68.94 83.96-68.94h371.2v118c0 106.7-87.3 194-194 194h-636c-106.7 0-194-87.3-194-194v-118h89.6zM529.5 421.6c-28.24 22.64-50.52 50-72 76.28-35.5 43.48-58.76 70.12-86.3 70.12-25.060 0-49.080-26.54-85.66-70.24-45.4-54.24-102-121.76-196-121.76h-89.54v-112h371.2c44 0 85.54-15.34 123.3-45.6 28.24-22.64 50.52-50 72-76.28 35.5-43.48 58.76-70.12 86.3-70.12 25.060 0 49.080 26.54 85.66 70.24 45.4 54.24 102 121.76 196 121.76h89.54v112h-371.2c-44.060 0-85.54 15.34-123.3 45.6zM934.4 120c-24.98 0-48.96-26.52-85.52-70.18-45.42-54.28-102-121.82-196-121.82-44.64 0-86.62 15.46-124.8 46-28.68 22.9-51.16 50.42-72.92 77.060-38.42 46.94-59.16 68.94-83.96 68.94h-371.2v-118c0-106.7 87.3-194 194-194h636c106.7 0 194 87.3 194 194v118h-89.6z" />
-<glyph unicode="&#xeb12;" glyph-name="icon-session" d="M635.6 307.6c6.6-4.2 13.2-8.6 19.2-13.6l120.4-96.4c29.6-23.8 83.8-23.8 113.4 0l135.2 108c0.2 4.8 0.2 9.4 0.2 14.2 0 52.2-7.8 102.4-22.2 149.8l-154.8-123.6c-58.2-46.6-140.2-59.2-211.4-38.4zM248.6 197.8l120.4 96.4c58 46.4 140 59.2 211.2 38.4-6.6 4.2-13.2 8.6-19.2 13.6l-120.4 96.4c-29.6 23.8-83.8 23.8-113.4 0l-120.2-96.6c-40-32-91.4-48-143-48-21.6 0-43 2.8-63.8 8.4 0-0.6 0-1.2 0-1.6 5-3.4 10-6.8 14.6-10.6l120.4-96.4c29.8-23.8 83.8-23.8 113.4 0zM120.6 453.8l120.4 96.4c80.2 64.2 205.6 64.2 285.8 0l120.4-96.4c29.6-23.8 83.8-23.8 113.4 0l181 144.8c-91.2 140.4-249.6 233.4-429.6 233.4-238.6 0-439.2-163.2-496-384.2 30.8-17.6 77.8-15.6 104.6 6zM689 90l-120.4 96.4c-29.6 23.8-83.8 23.8-113.4 0l-120.2-96.4c-40-32-91.4-48-143-48-47.8 0-95.4 13.8-134.2 41.4 85.6-163.6 256.8-275.4 454.2-275.4s368.6 111.8 454.2 275.4c-80.4-57.4-199.8-55.2-277.2 6.6z" />
-<glyph unicode="&#xeb13;" glyph-name="icon-tabular" d="M896 832h-768c-70.4 0-128-57.6-128-128v-768c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v768c0 70.4-57.6 128-128 128zM640 384h-256v192h256v-192zM384 320h256v-192h-256v192zM320 128h-256v192h256v-192zM320 576v-192h-256v192h256zM128-128c-17 0-33 6.6-45.2 18.8s-18.8 28.2-18.8 45.2v128h256v-192h-192zM384-128v192h256v-192h-256zM960-64c0-17-6.6-33-18.8-45.2s-28.2-18.8-45.2-18.8h-192v192h256v-128zM960 128h-256v192h256v-192zM960 384h-256v192h256v-192z" />
-<glyph unicode="&#xeb14;" glyph-name="icon-tabular-lad" d="M896 832h-768c-70.6-0.2-127.8-57.4-128-128v-768c0.2-70.6 57.4-127.8 128-128h768c70.6 0.2 127.8 57.4 128 128v768c-0.2 70.6-57.4 127.8-128 128zM64 576h256v-192h-256v192zM64 320h256v-192h-256v192zM128-128c-35.2 0.2-63.8 28.8-64 64v128h256v-192h-192zM384-128v192h256v-192h-256zM960-64c-0.2-35.2-28.8-63.8-64-64h-192v192h256v-128zM960 320v-192h-576v192h64v64h-64v192h576v-192h-64v-64h64zM782.4 284.6l-110.4 55.2v172.2c0 17.6-14.4 32-32 32s-32-14.4-32-32v-211.8l145.6-72.8c15.8-8 35-1.6 43 14.4 8 15.6 1.6 35-14.2 42.8v0z" />
-<glyph unicode="&#xeb15;" glyph-name="icon-tabular-lad-set" d="M128 64v576c-70.6-0.2-127.8-57.4-128-128v-576c0.2-70.6 57.4-127.8 128-128h576c70.6 0.2 127.8 57.4 128 128h-576c-70.6 0.2-127.8 57.4-128 128zM896 832h-576c-70.6-0.2-127.8-57.4-128-128v-576c0.2-70.6 57.4-127.8 128-128h576c70.6 0.2 127.8 57.4 128 128v576c-0.2 70.6-57.4 127.8-128 128zM256 640h192v-128h-192v128zM256 448h192v-192h-192v192zM320 64c-35.2 0.2-63.8 28.8-64 64v64h192v-128h-128zM512 64v128h192v-128h-192zM960 128c-0.2-35.2-28.8-63.8-64-64h-128v128h192v-64zM960 256h-448v384h448v-384zM832 352c17.6 0 32 14.4 32 32 0 13.8-8.8 26-21.8 30.4l-74.2 24.6v105c0 17.6-14.4 32-32 32s-32-14.4-32-32v-151l117.8-39.2c3.4-1.2 6.8-1.8 10.2-1.8z" />
-<glyph unicode="&#xeb16;" glyph-name="icon-tabular-realtime" d="M896 832h-768c-70.606-0.215-127.785-57.394-128-127.979v-768.021c0.215-70.606 57.394-127.785 127.979-128h768.021c70.606 0.215 127.785 57.394 128 127.979v768.021c-0.215 70.606-57.394 127.785-127.979 128h-0.021zM448 540l25.060-25.32c7.916-7.922 18.856-12.822 30.94-12.822s23.023 4.9 30.94 12.822v0l75.5 76.3c29.97 30.338 71.571 49.128 117.56 49.128s87.59-18.79 117.544-49.112l0.016-0.016 50.44-50.98v-152.2c-24.111 8.83-44.678 22.255-61.542 39.342l-0.018 0.018-75.5 76.3c-7.916 7.922-18.856 12.822-30.94 12.822s-23.023-4.9-30.94-12.822v0l-75.5-76.3c-29.971-30.343-71.575-49.137-117.568-49.137-20.084 0-39.331 3.584-57.137 10.146l1.145-0.369v152.2zM320-128h-192c-35.26 0.214-63.786 28.74-64 63.98v128.020h256v-192zM320 128h-256v192h256v-192zM320 384h-256v192h256v-192zM640-128h-256v192h256v-192zM448 195.38v174.5c1.88-1.74 3.74-3.5 5.56-5.34l75.5-76.3c7.916-7.922 18.856-12.822 30.94-12.822s23.023 4.9 30.94 12.822v0l75.5 76.3c29.966 30.333 71.56 49.119 117.542 49.119 43.28 0 82.673-16.643 112.128-43.879l-0.11 0.1v-174.5c-1.88 1.74-3.74 3.5-5.56 5.34l-75.5 76.3c-7.916 7.922-18.856 12.822-30.94 12.822s-23.023-4.9-30.94-12.822v0l-75.5-76.3c-29.966-30.333-71.56-49.119-117.542-49.119-43.28 0-82.673 16.643-112.128 43.879l0.11-0.1zM960-64c-0.214-35.26-28.74-63.786-63.98-64h-192.020v192h256v-128z" />
-<glyph unicode="&#xeb17;" glyph-name="icon-tabular-scrolling" d="M64 832c-35.2 0-64-28.8-64-64v-192h448v256h-384zM1024 576v192c0 35.2-28.8 64-64 64h-384v-256h448zM0 448v-192c0-35.2 28.8-64 64-64h384v256h-448zM960 192c35.2 0 64 28.8 64 64v192h-448v-256h384zM512-192l-256 256h512z" />
-<glyph unicode="&#xeb18;" glyph-name="icon-telemetry" d="M32 200.34c14.28 5.62 54.44 47.54 92.96 146 42.46 108.38 116.32 237.66 227.040 237.66 52.4 0 101.42-29.16 145.7-86.68 37.34-48.5 64.84-108.92 81.34-151.080 38.52-98.38 78.68-140.3 92.96-146 14.28 5.62 54.44 47.54 92.96 146 37.4 95.5 99.14 207.14 188.94 232.46-90.462 152.598-254.314 253.3-441.686 253.3-0.075 0-0.15 0-0.225 0h0.011c-282.76 0-512-229.24-512-512 0-0.032 0-0.070 0-0.108 0-35.719 3.641-70.587 10.572-104.254l-0.572 3.323c9.54-10.78 17.22-16.74 22-18.62zM992 439.66c-14.28-5.62-54.44-47.52-92.96-146-42.46-108.38-116.32-237.66-227.040-237.66-52.4 0-101.42 29.16-145.7 86.68-37.34 48.5-64.84 108.92-81.34 151.080-38.52 98.38-78.68 140.3-92.96 146-14.28-5.62-54.44-47.52-92.96-146-37.4-95.5-99.14-207.14-188.94-232.46 90.462-152.598 254.314-253.3 441.686-253.3 0.075 0 0.15 0 0.225 0h-0.011c282.76 0 512 229.24 512 512 0 0.032 0 0.070 0 0.108 0 35.719-3.641 70.587-10.572 104.254l0.572-3.323c-9.54 10.78-17.22 16.74-22 18.62z" />
-<glyph unicode="&#xeb19;" glyph-name="icon-timeline" d="M832 832h-640c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h640c105.6 0 192 86.4 192 192v640c0 105.6-86.4 192-192 192zM128 512v128h256v-128zM256 384h384v-128h-384zM896 0h-448v128h448zM896 256h-128v128h128zM896 512h-384v128h384z" />
-<glyph unicode="&#xeb1a;" glyph-name="icon-timer" d="M640 685.4v82.58c0 35.346-28.654 64-64 64v0h-128c-35.346 0-64-28.654-64-64v0-82.58c-185.040-55.080-320-226.48-320-429.42 0-247.42 200.58-448 448-448s448 200.58 448 448c0 202.96-135 374.4-320 429.42zM532 235.98l-263.76-211c-57.105 59.935-92.24 141.25-92.24 230.772 0 0.080 0 0.16 0 0.24v-0.012c0 185.28 150.72 336 336 336 6.72 0 13.38-0.22 20-0.62v-355.38z" />
-<glyph unicode="&#xeb1b;" glyph-name="icon-topic" d="M454.36 355.36l86.3 86.3c9.088 8.965 21.577 14.502 35.36 14.502s26.272-5.537 35.366-14.507l86.294-86.294c19.328-19.358 42.832-34.541 69.047-44.082l1.313-0.418v172.14l-57.64 57.64c-34.408 34.33-81.9 55.558-134.35 55.558s-99.943-21.228-134.354-55.562l-86.296-86.296c-9.088-8.965-21.577-14.502-35.36-14.502s-26.272 5.537-35.366 14.507l-28.674 28.654v-172.14c19.045-7.022 41.040-11.084 63.984-11.084 52.463 0 99.966 21.239 134.379 55.587l-0.003-0.003zM505.64 284.64l-86.3-86.3c-9.088-8.965-21.577-14.502-35.36-14.502s-26.272 5.537-35.366 14.507l-86.294 86.294c-2 2-4.2 4-6.36 6v-197.36c33.664-30.721 78.65-49.537 128.031-49.537 52.44 0 99.923 21.22 134.333 55.541l86.296 86.296c9.088 8.965 21.577 14.502 35.36 14.502s26.272-5.537 35.366-14.507l86.294-86.294c2-2 4.2-4 6.36-6v197.36c-33.664 30.721-78.65 49.537-128.031 49.537-52.44 0-99.923-21.22-134.333-55.541l0.004 0.004zM832 832h-128v-192h127.66l0.34-0.34v-639.32l-0.34-0.34h-127.66v-192h128c105.6 0 192 86.4 192 192v640c0 105.6-86.4 192-192 192zM320 0h-127.66l-0.34 0.34v639.32l0.34 0.34h127.66v192h-128c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h128v192z" />
-<glyph unicode="&#xeb1c;" glyph-name="icon-box-with-dashed-lines-v2" d="M0 448h128v-256h-128v256zM128 703.78l0.22 0.22h191.78v128h-192c-70.606-0.215-127.785-57.394-128-127.979v-192.021h128v191.78zM128-63.78v191.78h-128v-192c0.215-70.606 57.394-127.785 127.979-128h192.021v128h-191.78zM384 832h256v-128h-256v128zM896-63.78l-0.22-0.22h-191.78v-128h192c70.606 0.215 127.785 57.394 128 127.979v192.021h-128v-191.78zM896 832h-192v-128h191.78l0.22-0.22v-191.78h128v192c-0.215 70.606-57.394 127.785-127.979 128h-0.021zM896 448h128v-256h-128v256zM384-64h256v-128h-256v128zM256 576h512v-512h-512v512z" />
-<glyph unicode="&#xeb1d;" glyph-name="icon-summary-widget" d="M896 832h-768c-70.4 0-128-57.6-128-128v-768c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v768c0 70.4-57.6 128-128 128zM847.8 221.6l-82.6-143.2-189.6 131.6 19.2-230h-165.4l19.2 230-189.6-131.6-82.6 143.2 208.6 98.4-208.8 98.4 82.6 143.2 189.6-131.6-19.2 230h165.4l-19.2-230 189.6 131.6 82.6-143.2-208.6-98.4 208.8-98.4z" />
-<glyph unicode="&#xeb1e;" glyph-name="icon-notebook" d="M896 721.2c0 79.8-55.4 127.4-123 105.4l-773-250.6h896v145.2zM896 512h-896v-576c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v448c0 70.4-57.6 128-128 128zM832 0h-384v320h384v-320z" />
-<glyph unicode="&#xeb1f;" glyph-name="icon-tabs-view" d="M0-64c0.227-70.601 57.399-127.773 127.978-128h768.022c70.601 0.227 127.773 57.399 128 127.978v608.022h-512l-50.2 225.6c-7.6 34.2-42.6 62.4-77.8 62.4h-256c-70.601-0.227-127.773-57.399-128-127.978v-0.022zM832 64h-640v256h640zM480 832c35.2 0 70.2-28.2 77.8-62.4l36-161.6h430.2v96c-0.227 70.601-57.399 127.773-127.978 128h-0.022z" />
-<glyph unicode="&#xeb20;" glyph-name="icon-flexible-layout" d="M0 0c0-105.6 86.4-192 192-192h64v576h-256zM0 640v-128h256v320h-64c-105.6 0-192-86.4-192-192zM768-192h64c105.6 0 192 86.4 192 192v128h-256zM384 832h256v-1024h-256v1024zM832 832h-64v-576h256v384c0 105.6-86.4 192-192 192z" />
-<glyph unicode="&#xeb21;" glyph-name="icon-generator-sine" d="M152 358.2c10.8 4.2 40.8 35.6 69.8 109.4 31.8 81.4 87.2 178.4 170.2 178.4 39.4 0 76-21.8 109.2-65 28-36.4 48.8-81.6 61-113.4 29-73.8 59-105.2 69.8-109.4 10.8 4.2 40.8 35.6 69.8 109.4s74.2 155.4 141.6 174.4c-67.89 114.467-190.82 190-331.391 190-0.003 0-0.007 0-0.010 0h0.001c-212 0-384-172-384-384 0.017-26.829 2.71-53.018 7.827-78.329l-0.427 2.529c7.2-8 13-12.6 16.6-14zM884.6 355c7.235 27.919 11.392 59.972 11.4 92.995v0.005c-0.017 26.829-2.71 53.018-7.827 78.329l0.427-2.529c-7.2 8-13 12.6-16.6 14-10.8-4.2-40.8-35.6-69.8-109.4-21.8-55.8-54.6-119-100-153.2zM512 192l135 59c-4.485-0.614-9.689-0.977-14.972-1h-0.028c-39.4 0-76 21.8-109.2 65-28 36.4-48.8 81.6-61 113.4-29 73.8-59 105.2-69.8 109.4-10.8-4.2-40.8-35.6-69.8-109.4-16.4-42.2-39.2-88.4-68.8-123.2zM1024 352l-512-224-512 224v-320l512-224 512 224v320z" />
-<glyph unicode="&#xeb22;" glyph-name="icon-generator-event" d="M320 640h384v-64h-384v64zM320 384h384v-64h-384v64zM320 512h320v-64h-320v64zM256 703.8h512v-399.8l128 56v344c-0.227 70.601-57.399 127.773-127.978 128h-512.022c-70.601-0.227-127.773-57.399-128-127.978v-344.022l128-56zM658.2 256h-292.4l146.2-64 146.2 64zM512 128l-512 224v-320l512-224 512 224v320l-512-224z" />
-<glyph unicode="&#xeb23;" glyph-name="icon-gauge-v2" d="M512 832c-282.8 0-512-229.2-512-512 0-226.4 147-418.4 350.6-486l257.4 486v-503c236.8 45 416 253 416 503 0 282.8-229.2 512-512 512zM754.8 304.2c-58.967 68.597-145.842 111.772-242.8 111.772s-183.833-43.176-242.445-111.35l-0.355-0.422-146 125c8.6 10 17.4 19.6 26.8 28.8 92.628 92.679 220.619 150.006 362 150.006s269.372-57.326 361.997-150.003l0.003-0.003c9.4-9.2 18.2-18.8 26.8-28.8z" />
-<glyph unicode="&#xeb24;" glyph-name="icon-spectra" d="M768 128h-512l102.4 179.2-358.4-51.2v-254c0-106.6 87.4-194 194-194h636c106.8 0 194 87.4 194 194v62l-325.8 186.2zM830 832h-636c-106.6 0-194-87.2-194-194v-318l400 60.2 112 195.8 109.8-192h402.2v254c-0.227 107.052-86.948 193.773-193.978 194h-0.022zM1024 192v64l-384 64 384-128z" />
-<glyph unicode="&#xeb25;" glyph-name="icon-telemetry-spectra" d="M512 576l109.8-192h398.2c-31.4 252.6-247 448-508 448-282.8 0-512-229.2-512-512l400 60.2zM768 128h-512l102.4 179.2-354.4-50.6c31.2-252.8 246.8-448.6 508-448.6 201.6 0 376 116.6 459.6 286l-273.4 156.2zM640 320l384-128v64l-384 64z" />
-<glyph unicode="&#xeb26;" glyph-name="icon-pushbutton" d="M370.2 372.6c9.326-8.53 19.666-16.261 30.729-22.914l0.871-0.486c-11.077 19.209-17.664 42.221-17.8 66.76v0.040c0 39.6 17.8 77.6 50.2 107.4 37 34 87.4 52.6 141.8 52.6 40.2 0 78.2-10.2 110.2-29.2-8.918 15.653-19.693 29.040-32.268 40.482l-0.132 0.118c-37 34-87.4 52.6-141.8 52.6s-104.8-18.6-141.8-52.6c-32.4-29.8-50.2-67.8-50.2-107.4s17.8-77.6 50.2-107.4zM885.4 562.4c-40.6 154.6-192.4 269.6-373.4 269.6s-332.8-115-373.4-269.6c-86-80-138.6-187.8-138.6-306.4 0-247.4 229.2-448 512-448s512 200.6 512 448c0 118.6-52.6 226.4-138.6 306.4zM512 704c141.2 0 256-100.4 256-224s-114.8-224-256-224-256 100.4-256 224 114.8 224 256 224zM512 0c-175.4 0-318.4 127.8-320 285.4 68.8-94.8 186.4-157.4 320-157.4s251.2 62.6 320 157.4c-1.6-157.6-144.6-285.4-320-285.4z" />
-<glyph unicode="&#xeb27;" glyph-name="icon-conditional" d="M512 832c-282.76 0-512-229.24-512-512s229.24-512 512-512 512 229.24 512 512-229.24 512-512 512zM512 64l-384 256 384 256 384-256z" />
-<glyph unicode="&#xeb28;" glyph-name="icon-condition-widget" d="M832 832h-640c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h640c105.6 0 192 86.4 192 192v640c0 105.6-86.4 192-192 192zM512 64l-384 256 384 256 384-256z" />
-<glyph unicode="&#xeb29;" glyph-name="icon-alphanumeric" d="M535.6 301.4c-8.4-1.6-17.2-3-26.2-4s-18.2-2.4-27.2-4c-10.196-1.861-18.808-4.010-27.21-6.633l1.61 0.433c-8.609-2.674-16.105-6.348-22.89-10.987l0.29 0.187c-6.693-4.517-12.283-10.107-16.663-16.585l-0.137-0.215c-4.6-6.8-7.4-15.6-8.8-26s-0.4-18.4 2.4-25.2c2.746-6.688 7.224-12.195 12.881-16.122l0.119-0.078c5.967-4.053 13.057-6.94 20.704-8.161l0.296-0.039c7.592-1.527 16.319-2.4 25.25-2.4 0.123 0 0.246 0 0.369 0h-0.019c22.2 0 39.6 3.6 52.6 11s23.2 16.2 30.2 26.4c6.273 8.873 11.271 19.191 14.426 30.285l0.174 0.715c1.853 6.809 3.601 15.41 4.855 24.169l0.145 1.231 5.2 41.6c-5.4-4.217-11.723-7.564-18.583-9.689l-0.417-0.111c-6.489-2.241-14.362-4.255-22.444-5.662l-0.956-0.138zM1024 448v192h-152l24 192h-192l-24-192h-256l24 192h-192l-24-192h-232v-192h208l-32-256h-176v-192h152l-24-192h192l24 192h256l-24-192h192l24 192h232v192h-208l32 256zM702.8 420.2l-26.4-211.8c-2.231-15.809-3.537-34.122-3.6-52.727v-0.073c0-16.8 2.2-29.4 6.4-37.8h-113.4c-1.342 5.556-2.338 12.122-2.781 18.84l-0.019 0.36c-0.261 3.524-0.409 7.634-0.409 11.778 0 2.962 0.076 5.907 0.226 8.832l-0.017-0.41c-18.663-17.401-41.395-30.694-66.597-38.289l-1.203-0.311c-22.627-6.956-48.639-10.974-75.586-11h-0.014c-0.764-0.011-1.666-0.018-2.569-0.018-18.098 0-35.598 2.563-52.156 7.345l1.325-0.328c-15.991 4.512-29.851 12.090-41.545 22.122l0.145-0.122c-11.233 9.982-19.792 22.733-24.624 37.192l-0.176 0.608c-5.2 15.2-6.4 33.4-3.8 54.4s9.4 42.2 19.4 57.2c9.524 14.399 21.535 26.346 35.532 35.512l0.468 0.288c13.387 8.662 28.922 15.533 45.512 19.765l1.088 0.235c13.436 3.792 30.801 7.554 48.47 10.41l2.93 0.39c17 2.6 33.8 4.6 50.4 6.2 16.628 1.527 31.69 4.070 46.349 7.643l-2.149-0.443c13 3 23.6 7.6 31.6 13.6s12.6 15 13.6 26.4 0.8 21.8-2.4 28.8c-2.849 6.902-7.542 12.56-13.468 16.517l-0.132 0.083c-6.217 4.011-13.604 6.78-21.543 7.774l-0.257 0.026c-7.897 1.277-17 2.007-26.274 2.007-0.537 0-1.073-0.002-1.609-0.007l0.082 0.001c-22 0-40-4.6-53.8-14.2s-23-25.2-28-47.2h-111.8c4.8 26.2 14.2 48 27.8 65.4 13.475 16.978 29.89 30.968 48.574 41.377l0.826 0.423c18.192 10.038 39.297 17.806 61.619 22.175l1.381 0.225c20.488 4.162 44.053 6.563 68.171 6.6h0.029c21.8-0.005 43.239-1.532 64.222-4.479l-2.422 0.279c20.641-2.809 39.324-8.783 56.401-17.461l-1.001 0.461c15.909-8.108 28.858-20.031 37.967-34.601l0.233-0.399c9-15 12.2-34.8 9-59.6z" />
-<glyph unicode="&#xeb2a;" glyph-name="icon-image-telemetry" d="M512 832c-282.8 0-512-229.2-512-512s229.2-512 512-512 512 229.2 512 512-229.2 512-512 512zM783.6 48.4c-69.581-69.675-165.757-112.776-272-112.776-212.298 0-384.4 172.102-384.4 384.4s172.102 384.4 384.4 384.4c212.298 0 384.4-172.102 384.4-384.4 0-0.008 0-0.017 0-0.025v0.001c0.001-0.264 0.001-0.575 0.001-0.887 0-105.769-42.964-201.503-112.391-270.703l-0.010-0.010zM704 448l-128-128-192 192-192-192c0-176.731 143.269-320 320-320s320 143.269 320 320v0z" />
-<glyph unicode="&#xeb2b;" glyph-name="icon-telemetry-aggregate" d="M78 436.56c14 41.44 37.48 100.8 69.2 148.36 38.62 57.78 82.38 87.080 130.14 87.080s91.5-29.3 130-87.080c31.72-47.56 55.14-106.92 69.2-148.36 30.88-90.96 63.12-134.98 78-146.54 14.94 11.56 47.2 55.58 78 146.54 14 41.44 37.48 100.8 69.22 148.36q27.8 41.7 59.12 63.5c-75.7 111.377-201.81 183.58-344.783 183.58-0.034 0-0.068 0-0.103 0h0.006c-229.76 0-416-186.24-416-416 0-0.071 0-0.156 0-0.24 0-39.119 5.396-76.977 15.484-112.871l-0.704 2.931c16.78 21.74 40.4 63.34 63.22 130.74zM754 395.44c-14-41.44-37.48-100.8-69.2-148.36-38.56-57.78-82.32-87.080-130-87.080s-91.5 29.3-130 87.080c-31.72 47.56-55.14 106.92-69.2 148.36-30.88 90.96-63.14 134.98-78 146.54-14.94-11.56-47.2-55.58-78-146.54-14.38-41.44-37.8-100.8-69.6-148.36q-27.8-41.7-59.12-63.5c75.7-111.378 201.81-183.58 344.783-183.58 0.119 0 0.237 0 0.356 0h-0.019c229.76 0 416 186.24 416 416 0 0.071 0 0.156 0 0.24 0 39.119-5.396 76.977-15.484 112.871l0.704-2.931c-16.78-21.74-40.4-63.34-63.22-130.74zM921.56 497.38c4.098-24.449 6.44-52.617 6.44-81.332 0-0.017 0-0.034 0-0.051v0.003c0-0.095 0-0.208 0-0.32 0-282.593-229.087-511.68-511.68-511.68-0.113 0-0.225 0-0.338 0h0.018c-0.014 0-0.031 0-0.048 0-28.716 0-56.884 2.342-84.325 6.845l2.993-0.405c72.483-63.623 168.109-102.44 272.802-102.44 0.203 0 0.406 0 0.61 0h-0.031c229.76 0 416 186.24 416 416 0 0.172 0 0.375 0 0.578 0 104.692-38.817 200.319-102.844 273.271l0.404-0.47z" />
-<glyph unicode="&#xeb2c;" glyph-name="icon-bar-graph" d="M832 832h-640c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h640c105.6 0 192 86.4 192 192v640c0 105.6-86.4 192-192 192zM267.64-64h-139.64v448h139.64zM477.1-64h-139.64v768h139.64zM686.54-64h-139.64v320h139.64zM896-64h-139.64v640h139.64z" />
-<glyph unicode="&#xeb2d;" glyph-name="icon-map" d="M896 766.6l-128-62.6v-896l128 62.6c70.4 34.42 128 120.2 128 190.6v640c0 70.4-57.6 99.82-128 65.4zM320-80l387.2-96.8v896l-387.2 96.8v-896zM259.2 831.2l-3.2 0.8-128-62.6c-70.4-34.42-128-120.2-128-190.6v-640c0-70.4 57.6-99.82 128-65.4l128 62.6 3.2-0.8z" />
-<glyph unicode="&#xeb2e;" glyph-name="icon-plan" d="M256 640v64c0.215 70.606 57.394 127.785 127.979 128h256.021c70.606-0.215 127.785-57.394 128-127.979v-64.021zM832 704v-128h-640v128c-105.6 0-192-86.4-192-192v-512c0-105.6 86.4-192 192-192h640c105.6 0 192 86.4 192 192v512c0 105.6-86.4 192-192 192zM128 256v128h256v-128zM640 0h-384v128h384zM896 0h-128v128h128zM896 256h-384v128h384z" />
+<glyph unicode="&#x20;" horiz-adv-x="512" d="" />
+<glyph unicode="&#xe900;" glyph-name="icon-alert-rect-v2" d="M896 960h-768c-70.6-0.2-127.8-57.4-128-128v-768c0.2-70.6 57.4-127.8 128-128h768c70.6 0.2 127.8 57.4 128 128v768c-0.2 70.6-57.4 127.8-128 128zM576 64h-128v128h128v-128zM597.8 448l-37.8-192h-96l-37.8 192v384h171.8v-384z" />
+<glyph unicode="&#xe901;" glyph-name="icon-alert-triangle-v2" d="M998.2 111.2l-422.6 739.6c-35 61.2-92 61.2-127 0l-422.8-739.6c-35-61.2-6-111.2 64.4-111.2h843.4c70.6 0 99.6 50 64.6 111.2zM576 64h-128v128h128v-128zM597.8 448l-37.8-192h-96l-37.8 192v256h171.8v-256z" />
+<glyph unicode="&#xe902;" glyph-name="icon-arrow-up" d="M512 704l-512-512h1024z" />
+<glyph unicode="&#xe903;" glyph-name="icon-arrow-double-up" d="M510 450l512-512h-1024zM510 962l512-512h-1024z" />
+<glyph unicode="&#xe904;" glyph-name="icon-arrow-tall-up" d="M512 960l512-1024h-1024z" />
+<glyph unicode="&#xe905;" glyph-name="icon-arrow-right" d="M768 448l-512 512v-1024z" />
+<glyph unicode="&#xe906;" glyph-name="icon-arrow-right-equilateral" d="M962 448l-896-512v1024z" />
+<glyph unicode="&#xe907;" glyph-name="icon-arrow-down" d="M512 192l512 512h-1024z" />
+<glyph unicode="&#xe908;" glyph-name="icon-arrow-double-down" d="M510 450l-512 512h1024zM510-62l-512 512h1024z" />
+<glyph unicode="&#xe909;" glyph-name="icon-arrow-tall-down" d="M512-64l-512 1024h1024z" />
+<glyph unicode="&#xe90a;" glyph-name="icon-arrow-left" d="M256 448l512-512v1024z" />
+<glyph unicode="&#xe90b;" glyph-name="icon-asterisk" d="M1004.166 619.542l-97.522 168.916-330.534-229.414 33.414 400.956h-195.048l33.414-400.956-330.534 229.414-97.522-168.916 363.944-171.542-363.944-171.542 97.522-168.916 330.534 229.414-33.414-400.956h195.048l-33.414 400.956 330.534-229.414 97.522 168.916-363.944 171.542z" />
+<glyph unicode="&#xe90c;" glyph-name="icon-bell" d="M512-64c106 0 192 86 192 192h-384c0-106 86-192 192-192zM896 512v64c0 212-172 384-384 384s-384-172-384-384v-64c0-70.6-57.4-128-128-128v-128h1024v128c-70.6 0-128 57.4-128 128z" />
+<glyph unicode="&#xe90d;" glyph-name="icon-box-round-corners" d="M1024 128c0-105.6-86.4-192-192-192h-640c-105.6 0-192 86.4-192 192v640c0 105.6 86.4 192 192 192h640c105.6 0 192-86.4 192-192v-640z" />
+<glyph unicode="&#xe90e;" glyph-name="icon-box-with-arrow-cursor" d="M894 962h-768c-70.4 0-128-57.6-128-128v-768c0-70.4 57.6-128 128-128h400c-2.2 3.8-4 7.6-5.8 11.4l-255.2 576.8c-21.4 48.4-10.8 105 26.6 142.4 24.4 24.4 57.2 37.4 90.4 37.4 17.4 0 35.2-3.6 51.8-11l576.6-255.4c4-1.8 7.8-3.8 11.4-5.8v400.2c0.2 70.4-57.4 128-127.8 128zM958.6 322.6l-576.6 255.4 255.4-576.6 64.6 128.6 192-192 128 128-192 192z" />
+<glyph unicode="&#xe90f;" glyph-name="icon-check" d="M1024 960l-640-640-384 384v-384l384-384 640 640z" />
+<glyph unicode="&#xe910;" glyph-name="icon-connectivity" d="M704 384c0-70.4-57.6-128-128-128h-128c-70.4 0-128 57.6-128 128v128c0 70.4 57.6 128 128 128h128c70.4 0 128-57.6 128-128v-128zM1024 448l-192 320v-640zM0 448l192 320v-640z" />
+<glyph unicode="&#xe911;" glyph-name="icon-database-in-brackets" d="M768 608c0-53.019-114.615-96-256-96s-256 42.981-256 96c0 53.019 114.615 96 256 96s256-42.981 256-96zM768 288v256c0-53-114.6-96-256-96s-256 43-256 96v-256c0-53 114.6-96 256-96s256 43 256 96zM832 960h-128v-192h127.6c0.2 0 0.2-0.2 0.4-0.4v-639.4c0-0.2-0.2-0.2-0.4-0.4h-127.6v-192h128c105.6 0 192 86.4 192 192v640.2c0 105.6-86.4 192-192 192zM192 128.4v639.4c0 0.2 0.2 0.2 0.4 0.4h127.6v191.8h-128c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h128v192h-127.6c-0.2 0-0.4 0.2-0.4 0.4z" />
+<glyph unicode="&#xe912;" glyph-name="icon-eye-open" d="M512 843.6c-245.8 0-452.2-168-510.8-395.6 58.6-227.4 265-395.6 510.8-395.6s452.2 168 510.8 395.6c-58.6 227.4-265 395.6-510.8 395.6zM829.2 371.6c-22.6-34.4-50.6-64.8-83-90.4-32.8-25.8-69-45.6-108-59.4-40.4-14.2-82.8-21.4-126-21.4s-85.8 7.2-126 21.4c-39 13.8-75.4 33.8-108 59.4-32.4 25.6-60.4 55.8-83 90.4-15.8 24-28.8 49.6-38.6 76.4 10 26.8 23 52.4 38.6 76.4 22.6 34.4 50.6 64.8 83 90.4 32.8 25.8 69 45.6 108 59.4 40.4 14.2 82.8 21.4 126 21.4s85.8-7.2 126-21.4c39-13.8 75.4-33.8 108-59.4 32.4-25.6 60.4-55.8 83-90.4 15.8-24 28.8-49.6 38.6-76.4-9.8-26.8-22.8-52.4-38.6-76.4zM704 448c0-106.039-85.961-192-192-192s-192 85.961-192 192c0 106.039 85.961 192 192 192s192-85.961 192-192z" />
+<glyph unicode="&#xe913;" glyph-name="icon-gear" d="M1024 384v128l-140.976 35.244c-8.784 32.922-21.818 64.106-38.504 92.918l74.774 124.622-90.51 90.51-124.622-74.774c-28.812 16.686-59.996 29.72-92.918 38.504l-35.244 140.976h-128l-35.244-140.976c-32.922-8.784-64.106-21.818-92.918-38.504l-124.622 74.774-90.51-90.51 74.774-124.622c-16.686-28.812-29.72-59.996-38.504-92.918l-140.976-35.244v-128l140.976-35.244c8.784-32.922 21.818-64.106 38.504-92.918l-74.774-124.622 90.51-90.51 124.622 74.774c28.812-16.686 59.996-29.72 92.918-38.504l35.244-140.976h128l35.244 140.976c32.922 8.784 64.106 21.818 92.918 38.504l124.622-74.774 90.51 90.51-74.774 124.622c16.686 28.812 29.72 59.996 38.504 92.918l140.976 35.244zM704 448c0-106.038-85.962-192-192-192s-192 85.962-192 192 85.962 192 192 192 192-85.962 192-192z" />
+<glyph unicode="&#xe914;" glyph-name="icon-hourglass" d="M1024 960h-1024c0-282.8 229.2-512 512-512s512 229.2 512 512zM512 576c-102.6 0-199 40-271.6 112.4-41.2 41.2-72 90.2-90.8 143.6h724.6c-18.8-53.4-49.6-102.4-90.8-143.6-72.4-72.4-168.8-112.4-271.4-112.4zM512 448c-282.8 0-512-229.2-512-512h1024c0 282.8-229.2 512-512 512z" />
+<glyph unicode="&#xe915;" glyph-name="icon-info" d="M512 960c-282.8 0-512-229.2-512-512s229.2-512 512-512 512 229.2 512 512-229.2 512-512 512zM512 832c70.6 0 128-57.4 128-128s-57.4-128-128-128c-70.6 0-128 57.4-128 128s57.4 128 128 128zM704 128h-384v128h64v256h256v-256h64v-128z" />
+<glyph unicode="&#xe916;" glyph-name="icon-link" d="M1024 448l-512 512v-307.2l-512-204.8v-256h512v-256z" />
+<glyph unicode="&#xe917;" glyph-name="icon-lock" horiz-adv-x="768" d="M702 576h-62v128c0 141.385-114.615 256-256 256s-256-114.615-256-256v0-128h-64c-35.301-0.113-63.887-28.699-64-63.989v-512.011c0.113-35.301 28.699-63.887 63.989-64h638.011c35.301 0.113 63.887 28.699 64 63.989v512.011c-0.113 35.301-28.699 63.887-63.989 64h-0.011zM256 576v128c0 70.692 57.308 128 128 128s128-57.308 128-128v0-128z" />
+<glyph unicode="&#xe918;" glyph-name="icon-minus" d="M960 320c35.2 0 64 28.8 64 64v128c0 35.2-28.8 64-64 64h-896c-35.2 0-64-28.8-64-64v-128c0-35.2 28.8-64 64-64h896z" />
+<glyph unicode="&#xe919;" glyph-name="icon-people" d="M704 640h64c70.4 0 128 57.6 128 128v64c0 70.4-57.6 128-128 128h-64c-70.4 0-128-57.6-128-128v-64c0-70.4 57.6-128 128-128zM256 640h64c70.4 0 128 57.6 128 128v64c0 70.4-57.6 128-128 128h-64c-70.4 0-128-57.6-128-128v-64c0-70.4 57.6-128 128-128zM832 576h-192c-34.908 0-67.716-9.448-96-25.904 57.278-33.324 96-95.404 96-166.096v-448h384v448c0 105.6-86.4 192-192 192zM384 576h-192c-105.6 0-192-86.4-192-192v-448h576v448c0 105.6-86.4 192-192 192z" />
+<glyph unicode="&#xe91a;" glyph-name="icon-person" d="M768 704c0-105.6-86.4-192-192-192h-128c-105.6 0-192 86.4-192 192v64c0 105.6 86.4 192 192 192h128c105.6 0 192-86.4 192-192v-64zM64-64v192c0 140.8 115.2 256 256 256h384c140.8 0 256-115.2 256-256v-192z" />
+<glyph unicode="&#xe91b;" glyph-name="icon-plus" d="M960 576h-330v320c0 35.2-28.8 64-64 64h-108c-35.2 0-64-28.8-64-64v-320h-330c-35.2 0-64-28.8-64-64v-128c0-35.2 28.8-64 64-64h330v-320c0-35.2 28.8-64 64-64h108c35.2 0 64 28.8 64 64v320h330c35.2 0 64 28.8 64 64v128c0 35.2-28.8 64-64 64z" />
+<glyph unicode="&#xe91c;" glyph-name="icon-plus-in-rect" d="M830 960h-636c-106.6 0-194-87.2-194-194v-636c0-106.8 87.4-194 194-194h636c106.6 0 194 87.2 194 194v636c0 106.8-87.4 194-194 194zM896 352c0-17.673-14.327-32-32-32v0h-224v-224c0-17.673-14.327-32-32-32v0h-192c-17.673 0-32 14.327-32 32v0 224h-224c-17.673 0-32 14.327-32 32v0 192c0 17.673 14.327 32 32 32v0h224v224c0 17.673 14.327 32 32 32v0h192c17.673 0 32-14.327 32-32v0-224h224c17.673 0 32-14.327 32-32v0z" />
+<glyph unicode="&#xe91d;" glyph-name="icon-trash" d="M832 832h-192.36v64c0 35.2-28.8 64-64 64h-128c-35.2 0-64-28.8-64-64v-64h-191.64c-105.6 0-192-72-192-160s0-160 0-160h64v-384c0-105.6 86.4-192 192-192h512c105.6 0 192 86.4 192 192v384h64c0 0 0 72 0 160s-86.4 160-192 160zM320 128h-128v384h128v-384zM576 128h-128v384h128v-384zM832 128h-128v384h128v-384z" />
+<glyph unicode="&#xe91e;" glyph-name="icon-x-heavy" d="M704 448l301.332-301.332c24.89-24.89 24.89-65.62 0-90.51l-101.49-101.49c-24.89-24.89-65.62-24.89-90.51 0l-301.332 301.332c0 0-301.332-301.332-301.332-301.332-24.89-24.89-65.62-24.89-90.51 0l-101.49 101.49c-24.89 24.89-24.89 65.62 0 90.51l301.332 301.332c0 0-301.332 301.332-301.332 301.332-24.89 24.89-24.89 65.62 0 90.51l101.49 101.49c24.89 24.89 65.62 24.89 90.51 0l301.332-301.332c0 0 301.332 301.332 301.332 301.332 24.89 24.89 65.62 24.89 90.51 0l101.49-101.49c24.89-24.89 24.89-65.62 0-90.51 0 0-301.332-301.332-301.332-301.332z" />
+<glyph unicode="&#xe91f;" glyph-name="icon-brackets" d="M832 960h-192v-192h191.66l0.34-0.34v-639.32l-0.34-0.34h-191.66v-192h192c105.6 0 192 86.4 192 192v640c0 105.6-86.4 192-192 192zM384 128h-191.66l-0.34 0.34v639.32l0.34 0.34h191.66v192h-192c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h192v192z" />
+<glyph unicode="&#xe920;" glyph-name="icon-crosshair" d="M574 962h-128v-320h128v320zM1022 514h-320v-128h320v128zM574 258h-128v-320h128v320zM318 514h-320v-128h320v128z" />
+<glyph unicode="&#xe921;" glyph-name="icon-grippy" d="M365.4 777.2c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM365.4 557.8c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM365.4 338.2c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM365.4 118.8c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM584.8 886.8c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM584.8 667.4c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM584.8 448c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM584.8 228.6c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM584.8 9.2c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM804.2 777.2c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM804.2 557.8c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM804.2 338.2c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2zM804.2 118.8c0-40.427-32.773-73.2-73.2-73.2s-73.2 32.773-73.2 73.2c0 40.427 32.773 73.2 73.2 73.2s73.2-32.773 73.2-73.2z" />
+<glyph unicode="&#xe922;" glyph-name="icon-grid" d="M0 384v-256c0-105.6 86.4-192 192-192h256v448h-448zM448 960h-256c-105.6 0-192-86.4-192-192v-256h448v448zM832 960h-256v-448h448v256c0 105.6-86.4 192-192 192zM576-64h256c105.6 0 192 86.4 192 192v256h-448v-448z" />
+<glyph unicode="&#xe923;" glyph-name="icon-grippy-ew" d="M704 960h128v-1024h-128v1024zM448 960h128v-1024h-128v1024zM192 960h128v-1024h-128v1024z" />
+<glyph unicode="&#xe924;" glyph-name="icon-columns" d="M0 960h256v-1024h-256v1024zM384 960h256v-1024h-256v1024zM768 960h256v-1024h-256v1024z" />
+<glyph unicode="&#xe925;" glyph-name="icon-rows" d="M0 960h1024v-256h-1024v256zM0 576h1024v-256h-1024v256zM0 192h1024v-256h-1024v256z" />
+<glyph unicode="&#xe926;" glyph-name="icon-filter" d="M896 960h-768c-70.601-0.227-127.773-57.399-128-127.978v-768.022c0.227-70.601 57.399-127.773 127.978-128h256.022v512l-192 192h640l-192-192v-512h256c70.601 0.227 127.773 57.399 128 127.978v768.022c-0.227 70.601-57.399 127.773-127.978 128h-0.022z" />
+<glyph unicode="&#xe927;" glyph-name="icon-filter-outline" d="M896 960h-768c-70.601-0.227-127.773-57.399-128-127.978v-768.022c0.227-70.601 57.399-127.773 127.978-128h768.022c70.601 0.227 127.773 57.399 128 127.978v768.022c-0.227 70.601-57.399 127.773-127.978 128h-0.022zM896 64.2h-256v383.8l192 192h-640l192-192v-384h-256v767.8h768z" />
+<glyph unicode="&#xe928;" glyph-name="icon-suitcase" d="M768 832c-0.080 70.66-57.34 127.92-127.993 128h-256.007c-70.66-0.080-127.92-57.34-128-127.993v-128.007h-64v-768h640v768h-64zM384 831.88l0.12 0.12 255.88-0.12v-127.88h-256zM0 640v-640c0.102-35.305 28.695-63.898 63.99-64h64.010v768h-64c-35.305-0.102-63.898-28.695-64-63.99v-0.010zM960 704h-64v-768h64c35.305 0.102 63.898 28.695 64 63.99v640.010c-0.102 35.305-28.695 63.898-63.99 64h-0.010z" />
+<glyph unicode="&#xe929;" glyph-name="icon-cursor-locked" horiz-adv-x="768" d="M704 640h-64v64c0 141.385-114.615 256-256 256s-256-114.615-256-256v0-64h-64c-35.301-0.113-63.887-28.699-64-63.989v-576.011c0.113-35.301 28.699-63.887 63.989-64h640.011c35.301 0.113 63.887 28.699 64 63.989v576.011c-0.113 35.301-28.699 63.887-63.989 64h-0.011zM256 704c0 70.692 57.308 128 128 128s128-57.308 128-128v0-64h-256zM533.4 64l-128 128-43-85-170.4 383.6 383.6-170.2-85-43 128-128z" />
+<glyph unicode="&#xe92a;" glyph-name="icon-flag" d="M192 320h832l-192 320 192 320h-896c-70.606-0.215-127.785-57.394-128-127.979v-896.021h192z" />
+<glyph unicode="&#xe92b;" glyph-name="icon-eye-disabled" d="M209.46 351.32q-7.46 9.86-14.26 20.28c-14.737 21.984-27.741 47.184-37.759 73.847l-0.841 2.553c11.078 29.259 24.068 54.443 39.51 77.869l-0.91-1.469c23.221 34.963 50.705 64.8 82.207 89.793l0.793 0.607c57.663 45.719 130.179 75.053 209.311 79.947l1.069 0.053 114.48 140.88c-27.366 5.017-58.869 7.898-91.041 7.92h-0.019c-245.8 0-452.2-168-510.8-395.6 21.856-82.93 60.906-154.847 113.325-214.773l-0.525 0.613zM814.76 544.92q7.52-10 14.44-20.52c14.737-21.984 27.741-47.184 37.759-73.847l0.841-2.553c-10.859-29.216-23.863-54.416-39.447-77.748l0.847 1.348c-23.221-34.963-50.705-64.8-82.207-89.793l-0.793-0.607c-57.762-45.834-130.437-75.216-209.743-80.049l-1.057-0.051-114.46-140.86c27.346-4.988 58.817-7.84 90.955-7.84 0.037 0 0.074 0 0.111 0h-0.005c245.8 0 452.2 168 510.8 395.6-21.856 82.93-60.906 154.847-113.325 214.773l0.525-0.613zM832 960l-832-1024h192l832 1024h-192z" />
+<glyph unicode="&#xe92c;" glyph-name="icon-notebook-page" d="M830 898h-830l-4-702c0-106.6 87.4-194 194-194h640c106.6 0 194 87.4 194 194v508c0 106.8-87.4 194-194 194zM832 514l-384-384-192 192v256l192-192 384 384v-256z" />
+<glyph unicode="&#xe92d;" glyph-name="icon-unlocked" d="M768 960c-141.339-0.114-255.886-114.661-256-255.989v-128.011h-448c-35.301-0.113-63.887-28.699-64-63.989v-512.011c0.113-35.301 28.699-63.887 63.989-64h638.011c35.301 0.113 63.887 28.699 64 63.989v512.011c-0.113 35.301-28.699 63.887-63.989 64h-62.011v128c0 70.692 57.308 128 128 128s128-57.308 128-128v0-128h128v128c-0.114 141.339-114.661 255.886-255.989 256h-0.011z" />
+<glyph unicode="&#xe92e;" glyph-name="icon-circle" d="M1024 448c0-282.77-229.23-512-512-512s-512 229.23-512 512c0 282.77 229.23 512 512 512s512-229.23 512-512z" />
+<glyph unicode="&#xe92f;" glyph-name="icon-draft" d="M876.34 324.42l-49.9-49.88-19.26-19.5-26-8.7-423.040-144.2 144.2 423.28 8.84 25.78 150 149.88-85.6 149.78c-34.92 61.12-92 61.12-127 0l-422.78-739.72c-34.94-61.14-5.92-111.14 64.48-111.14h843.44c70.4 0 99.42 50 64.48 111.14zM973.18 717.16c-19.32 19.3-40.66 34.62-60.16 43.16-34.42 15.12-52.38 4.54-60.1-3.16l-258.12-258.12-82.8-243.040 243 82.8 3.36 3.4 254.76 254.76c4.94 4.94 10.88 13.88 10.88 28.3 0 25.34-19.5 60.56-50.82 91.9zM631 340.18l-34.88 34.86 34.64 101.6 9.24 3.36h32v-64h64v-32l-3.42-9.26z" />
+<glyph unicode="&#xe930;" glyph-name="icon-circle-slash" d="M512 960c-282.78 0-512-229.22-512-512s229.22-512 512-512 512 229.22 512 512-229.22 512-512 512zM263.1 696.9c66.48 66.48 154.88 103.1 248.9 103.1 66.74 0 130.64-18.48 185.9-52.96l-484.94-484.94c-34.5 55.24-52.96 119.16-52.96 185.9 0 94.020 36.62 182.42 103.1 248.9zM760.9 199.1c-66.48-66.48-154.88-103.1-248.9-103.1-66.74 0-130.64 18.48-185.9 52.96l484.94 484.94c34.5-55.24 52.96-119.16 52.96-185.9 0-94.020-36.62-182.42-103.1-248.9z" />
+<glyph unicode="&#xe931;" glyph-name="icon-question-mark" horiz-adv-x="697" d="M136.86 907.74c54.080 34.82 120.58 52.26 199.44 52.26 103.6 0 189.7-24.76 258.24-74.28s102.82-122.88 102.82-220.060c0-59.6-14.86-109.8-44.58-150.6-17.38-24.76-50.76-56.4-100.14-94.9l-48.68-37.82c-26.54-20.64-44.14-44.7-52.82-72.2-5.5-17.44-8.46-44.48-8.92-81.14h-186.4c2.74 77.48 10.060 131 21.94 160.58s42.5 63.62 91.88 102.12l50.060 39.2c16.46 12.38 29.72 25.9 39.78 40.58 18.28 25.2 27.42 52.96 27.42 83.22 0 34.84-10.18 66.6-30.52 95.24-20.36 28.64-57.52 42.98-111.48 42.98s-90.68-17.66-112.88-52.96c-22.18-35.32-33.26-71.98-33.26-110.040h-198.76c5.5 130.64 51.12 223.24 136.86 277.82zM251.020 134.76h205.62v-198.74h-205.62v198.74z" />
+<glyph unicode="&#xe932;" glyph-name="icon-status-poll-check" d="M512 960c-282.76 0-512-214.9-512-480 0-92.26 27.8-178.44 75.92-251.6l-75.92-292.4 313.5 101.42c61.040-24.1 128.12-37.42 198.5-37.42 282.76 0 512 214.9 512 480s-229.24 480-512 480zM768 512l-320-320-192 192v192l192-192 320 320v-192z" />
+<glyph unicode="&#xe933;" glyph-name="icon-status-poll-caution" d="M512 960c-282.76 0-512-214.9-512-480 0-92.26 27.8-178.44 75.92-251.6l-75.92-292.4 313.5 101.42c61.040-24.1 128.12-37.42 198.5-37.42 282.76 0 512 214.9 512 480s-229.24 480-512 480zM781.36 256h-538.72c-44.96 0-63.5 31.94-41.2 70.98l270 472.48c22.3 39.040 58.82 39.040 81.12 0l269.98-472.48c22.3-39.040 3.78-70.98-41.2-70.98zM457.14 542.14l24.2-122.64h61.32l24.2 122.64v163.5h-109.72v-163.5zM471.12 378.64h81.76v-81.76h-81.76v81.76z" />
+<glyph unicode="&#xe934;" glyph-name="icon-status-poll-circle-slash" d="M391.18 291.3c35.72-22.98 77.32-35.3 120.82-35.3 59.84 0 116.080 23.3 158.4 65.6 42.3 42.3 65.6 98.56 65.6 158.4 0 43.5-12.32 85.080-35.3 120.82l-309.52-309.52zM512 704c-59.84 0-116.080-23.3-158.4-65.6-42.3-42.3-65.6-98.56-65.6-158.4 0-43.5 12.32-85.080 35.3-120.82l309.52 309.52c-35.72 22.98-77.32 35.3-120.82 35.3zM512 960c-282.76 0-512-214.9-512-480 0-92.26 27.8-178.44 75.92-251.6l-75.92-292.4 313.5 101.42c61.040-24.1 128.12-37.42 198.5-37.42 282.76 0 512 214.9 512 480s-229.24 480-512 480zM512 160c-176.74 0-320 143.26-320 320s143.26 320 320 320 320-143.26 320-320-143.26-320-320-320z" />
+<glyph unicode="&#xe935;" glyph-name="icon-status-poll-question-mark" d="M512 960c-282.76 0-512-214.9-512-480 0-92.26 27.8-178.44 75.92-251.6l-75.92-292.4 313.5 101.42c61.040-24.1 128.12-37.42 198.5-37.42 282.76 0 512 214.9 512 480s-229.24 480-512 480zM579.020 128h-141.36v136.64h141.36v-136.64zM713.84 526.1c-11.94-17.020-34.9-38.78-68.84-65.24l-33.48-26c-18.24-14.18-30.34-30.74-36.32-49.64-3.78-11.98-5.82-30.58-6.14-55.8h-128.12c1.88 53.26 6.92 90.060 15.080 110.4 8.18 20.34 29.22 43.74 63.16 70.22l34.42 26.94c11.3 8.52 20.42 17.8 27.34 27.9 12.56 17.34 18.86 36.4 18.86 57.2 0 23.94-7 45.78-20.98 65.48-14 19.7-39.54 29.54-76.64 29.54s-62.34-12.14-77.6-36.4c-15.24-24.28-22.88-49.48-22.88-75.64h-136.64c3.78 89.84 35.14 153.5 94.080 191.020 37.18 23.94 82.9 35.94 137.12 35.94 71.22 0 130.42-17.020 177.54-51.060s70.68-84.48 70.68-151.3c0-40.98-10.22-75.5-30.66-103.54z" />
+<glyph unicode="&#xe936;" glyph-name="icon-status-poll-edit" d="M1000.080 625.36l-336.6-336.76-20.52-6.88-450.96-153.72 160.68 471.52 332.34 332.34c-54.040 18.2-112.28 28.14-173.020 28.14-282.76 0-512-214.9-512-480 0-92.26 27.8-178.44 75.92-251.6l-75.92-292.4 313.5 101.42c61.040-24.1 128.12-37.42 198.5-37.42 282.76 0 512 214.9 512 480 0 50.68-8.4 99.5-23.92 145.36zM408.42 564.76l-2.16-6.3-111.7-327.9 334.12 113.86 4.62 4.68 350.28 350.28c6.8 6.78 14.96 19.1 14.96 38.9 0 34.86-26.82 83.28-69.88 126.38-26.54 26.54-55.9 47.6-82.7 59.34-47.34 20.8-72.020 6.24-82.64-4.36l-354.9-354.88zM470.56 538.58h44v-88h88v-44l-4.7-12.72-139.68-47.54-47.94 47.94 47.6 139.72 12.72 4.6z" />
+<glyph unicode="&#xea00;" glyph-name="icon-arrows-right-left" d="M1024 448l-448-512v1024zM448 960l-448-512 448-512z" />
+<glyph unicode="&#xea01;" glyph-name="icon-arrows-up-down" d="M512 960l512-448h-1024zM0 384l512-448 512 448z" />
+<glyph unicode="&#xea02;" glyph-name="icon-bullet" d="M832 208c0-44-36-80-80-80h-480c-44 0-80 36-80 80v480c0 44 36 80 80 80h480c44 0 80-36 80-80v-480z" />
+<glyph unicode="&#xea03;" glyph-name="icon-calendar" d="M896 960h-768c-70.4 0-128-57.6-128-128v-768c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v768c0 70.4-57.6 128-128 128zM640 512h-256v192h256v-192zM384 448h256v-192h-256v192zM320 256h-256v192h256v-192zM320 704v-192h-256v192h256zM128 0c-17 0-33 6.6-45.2 18.8s-18.8 28.2-18.8 45.2v128h256v-192h-192zM384 0v192h256v-192h-256zM960 64c0-17-6.6-33-18.8-45.2s-28.2-18.8-45.2-18.8h-192v192h256v-128zM960 256h-256v192h256v-192zM960 512h-256v192h256v-192z" />
+<glyph unicode="&#xea04;" glyph-name="icon-chain-links" d="M958.4 894.4c-43.8 43.8-101 65.6-158.4 65.6s-114.6-21.8-158.4-65.6l-128-128c-74-74-85.4-187-34-273l-12.8-12.8c-35.4 20.8-75 31.4-114.8 31.4-57.4 0-114.6-21.8-158.4-65.6l-128-128c-87.4-87.4-87.4-229.4 0-316.8 43.8-43.8 101-65.6 158.4-65.6s114.6 21.8 158.4 65.6l128 128c74 74 85.4 187 34 273l12.8 12.8c35.2-21 75-31.6 114.6-31.6 57.4 0 114.6 21.8 158.4 65.6l128 128c87.6 87.6 87.6 229.6 0.2 317zM419.8 220.2l-128-128c-18-18.2-42.2-28.2-67.8-28.2s-49.8 10-67.8 28.2c-37.4 37.4-37.4 98.4 0 135.8l128 128c18.2 18.2 42.2 28.2 67.8 28.2 5.6 0 11.2-0.6 16.8-1.4l-55.6-55.6c-10.4-10.4-16.2-24.2-16.2-38.8s5.8-28.6 16.2-38.8c10.4-10.4 24.2-16.2 38.8-16.2s28.6 5.8 38.8 16.2l55.6 55.6c5.4-30.4-3.6-62.2-26.6-85zM867.8 668.2l-128-128c-18-18.2-42.2-28.2-67.8-28.2-5.6 0-11.2 0.6-16.8 1.4l55.6 55.6c10.4 10.4 16.2 24.2 16.2 38.8s-5.8 28.6-16.2 38.8c-10.4 10.4-24.2 16.2-38.8 16.2s-28.6-5.8-38.8-16.2l-55.6-55.6c-5.2 29.8 3.6 61.6 26.6 84.6l128 128c18 18.4 42.2 28.4 67.8 28.4s49.8-10 67.8-28.2c37.6-37.4 37.6-98.2 0-135.6z" />
+<glyph unicode="&#xea05;" glyph-name="icon-download" d="M832 384v-255.66l-0.34-0.34-639.66 0.34v255.66h-192v-256c0-105.6 86.4-192 192-192h640c105.6 0 192 86.4 192 192v256h-192zM512 320l448 448h-256v192h-384v-192h-256l448-448z" />
+<glyph unicode="&#xea06;" glyph-name="icon-duplicate" d="M640 704v128c0 70.4-57.6 128-128 128h-384c-70.4 0-128-57.6-128-128v-384c0-70.4 57.6-128 128-128h128v139.6c0 134.8 109.6 244.4 244.4 244.4h139.6zM896 576h-384c-70.4 0-128-57.6-128-128v-384c0-70.4 57.6-128 128-128h384c70.4 0 128 57.6 128 128v384c0 70.4-57.6 128-128 128z" />
+<glyph unicode="&#xea07;" glyph-name="icon-folder-new" d="M896 768h-320c-16.4 16.4-96.8 96.8-109.2 109.2l-37.4 37.4c-25 25-74.2 45.4-109.4 45.4h-256c-35.2 0-64-28.8-64-64v-384c0 70.4 57.6 128 128 128h768c70.4 0 128-57.6 128-128v128c0 70.4-57.6 128-128 128zM896 512h-768c-70.4 0-128-57.6-128-128v-320c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v320c0 70.4-57.6 128-128 128zM704 160h-128v-128h-128v128h-128v128h128v128h128v-128h128v-128z" />
+<glyph unicode="&#xea08;" glyph-name="icon-fullscreen-collapse" d="M191.656 128c0.118-0.1 0.244-0.224 0.344-0.344v-191.656h192v192c0 105.6-86.4 192-192 192h-192v-192h191.656zM192 768.344c-0.1-0.118-0.224-0.244-0.344-0.344h-191.656v-192h192c105.6 0 192 86.4 192 192v192h-192v-191.656zM832 576h192v192h-191.656c-0.118 0.1-0.244 0.226-0.344 0.344v191.656h-192v-192c0-105.6 86.4-192 192-192zM832 127.656c0.1 0.118 0.224 0.244 0.344 0.344h191.656v192h-192c-105.6 0-192-86.4-192-192v-192h192v191.656z" />
+<glyph unicode="&#xea09;" glyph-name="icon-fullscreen-expand" d="M192.344 128c-0.118 0.1-0.244 0.224-0.344 0.344v191.656h-192v-192c0-105.6 86.4-192 192-192h192v192h-191.656zM192 767.656c0.1 0.118 0.224 0.244 0.344 0.344h191.656v192h-192c-105.6 0-192-86.4-192-192v-192h192v191.656zM832 960h-192v-192h191.656c0.118-0.1 0.244-0.226 0.344-0.344v-191.656h192v192c0 105.6-86.4 192-192 192zM832 128.344c-0.1-0.118-0.224-0.244-0.344-0.344h-191.656v-192h192c105.6 0 192 86.4 192 192v192h-192v-191.656z" />
+<glyph unicode="&#xea0a;" glyph-name="icon-layers" d="M1024 576l-512 384-512-384 512-384zM512 64l-426.666 320-85.334-64 512-384 512 384-85.334 64z" />
+<glyph unicode="&#xea0b;" glyph-name="icon-line-horz" d="M64 384c-35.346 0-64 28.654-64 64s28.654 64 64 64h896c35.346 0 64-28.654 64-64s-28.654-64-64-64h-896z" />
+<glyph unicode="&#xea0c;" glyph-name="icon-magnify" d="M1024 64l-256.8 256.8c42.4 66.6 65 144 64.8 223.2 0 229.8-186.2 416-416 416s-416-186.2-416-416 186.2-416 416-416c79-0.2 156.4 22.4 223.2 64.8l256.8-256.8 128 128zM212.4 340.4c-112.4 112.4-112.4 294.8 0 407.2s294.8 112.4 407.2 0 112.4-294.8 0-407.2c-54-54-127.2-84.4-203.6-84.4-76.4-0.2-149.8 30.2-203.6 84.4z" />
+<glyph unicode="&#xea0d;" glyph-name="icon-magnify-in" d="M1024 64l-256.86 256.86c40.681 62.963 64.861 139.898 64.861 222.481 0 0.232 0 0.464-0.001 0.696v-0.036c0 229.76-186.24 416-416 416s-416-186.24-416-416 186.24-416 416-416c0.196 0 0.427-0.001 0.659-0.001 82.583 0 159.518 24.18 224.112 65.846l-1.631-0.985 256.86-256.86zM212.36 340.36c-52.114 52.117-84.346 124.114-84.346 203.64 0 159.058 128.942 288 288 288s288-128.942 288-288c0-159.058-128.942-288-288-288-0.005 0-0.010 0-0.014 0h0.001c-0.242-0.001-0.529-0.001-0.815-0.001-79.271 0-151.010 32.251-202.811 84.348l-0.013 0.014zM224 608h384v-128h-384v128zM352 736h128v-384h-128v384z" />
+<glyph unicode="&#xea0e;" glyph-name="icon-magnify-out-v2" d="M767.2 320.8c42.4 66.6 65 144 64.8 223.2 0 229.8-186.2 416-416 416s-416-186.2-416-416 186.2-416 416-416c79-0.2 156.4 22.4 223.2 64.8l256.8-256.8 128 128-256.8 256.8zM619.6 340.4c-54-54-127.2-84.4-203.6-84.4-76.4-0.2-149.8 30.2-203.6 84.4-112.4 112.4-112.4 294.8 0 407.2s294.8 112.4 407.2 0c112.4-112.4 112.4-294.8 0-407.2zM224 608h384v-128h-384v128z" />
+<glyph unicode="&#xea0f;" glyph-name="icon-menu" d="M0 832h1024v-128h-1024v128zM0 512h1024v-128h-1024v128zM0 192h1024v-128h-1024v128z" />
+<glyph unicode="&#xea10;" glyph-name="icon-move" d="M293.4 448l218.6 218.6 256-256v421.4c0 70.4-57.6 128-128 128h-512c-70.4 0-128-57.6-128-128v-512c0-70.4 57.6-128 128-128h421.4l-256 256zM1024 512h-128v-320l-384 384-128-128 384-384h-320v-128h576z" />
+<glyph unicode="&#xea11;" glyph-name="icon-new-window" d="M448 960v-128h320l-384-384 128-128 384 384v-320h128v576zM576 285.726v-157.382c-0.1-0.118-0.226-0.244-0.344-0.344h-383.312c-0.118 0.1-0.244 0.226-0.344 0.344v383.312c0.1 0.118 0.226 0.244 0.344 0.344h157.382l192 192h-349.726c-105.6 0-192-86.4-192-192v-384c0-105.6 86.4-192 192-192h384c105.6 0 192 86.4 192 192v349.726l-192-192z" />
+<glyph unicode="&#xea12;" glyph-name="icon-paint-bucket-v2" d="M544 736v-224c0-88.4-71.6-160-160-160s-160 71.6-160 160v97.2l-197.4-196.4c-50-50-12.4-215.2 112.4-340s290-162.4 340-112.4l417 423.6-352 352zM896-64c70.6 0 128 57.4 128 128 0 108.6-128 192-128 192s-128-83.4-128-192c0-70.6 57.4-128 128-128zM384 448c-35.4 0-64 28.6-64 64v384c0 35.4 28.6 64 64 64s64-28.6 64-64v-384c0-35.4-28.6-64-64-64z" />
+<glyph unicode="&#xea13;" glyph-name="icon-pencil" d="M922.344 858.32c-38.612 38.596-81.306 69.232-120.304 86.324-68.848 30.25-104.77 9.078-120.194-6.344l-516.228-516.216-3.136-9.152-162.482-476.932 485.998 165.612 6.73 6.806 509.502 509.506c9.882 9.866 21.768 27.77 21.768 56.578 0.002 50.71-38.996 121.148-101.654 183.818zM237.982 104.34l-69.73 69.728 69.25 203.228 18.498 6.704h64v-128h128v-64l-6.846-18.506-203.172-69.154z" />
+<glyph unicode="&#xea14;" glyph-name="icon-pencil-edit-in-place" d="M922.4 858.4c-38.6 38.6-81.4 69.2-120.4 86.2-68.8 30.2-104.8 9-120.2-6.4l-516.2-516.2-3.2-9.2-162.4-476.8 486 165.6 516.2 516.4c9.8 9.8 21.8 27.8 21.8 56.6 0 50.6-39 121-101.6 183.8zM238 104.4l-69.8 69.6 69.2 203.2 18.4 6.8h64v-128h128v-64l-6.8-18.6-203-69zM0 960v-512l128 128v256h256l128 128zM1024-64v512l-128-128v-256h-256l-128-128z" />
+<glyph unicode="&#xea15;" glyph-name="icon-play" d="M1024 448l-1024-512v1024z" />
+<glyph unicode="&#xea16;" glyph-name="icon-pause" d="M126 962h256v-1024h-256v1024zM638 962h256v-1024h-256v1024z" />
+<glyph unicode="&#xea17;" glyph-name="icon-plot-resource" d="M255.8 256c0.2 0 0.2 0 0 0l0.2 128c0 70.6 57.4 128 128 128h255.8c0 0 0 0 0.2 0.2v127.8c0 70.6 57.4 128 128 128h143.6c-93.8 117-238 192-399.6 192-282.8 0-512-229.2-512-512 0-68 13.2-132.8 37.2-192h218.6zM768.2 640c-0.2 0-0.2 0 0 0l-0.2-128c0-70.6-57.4-128-128-128h-255.8c0 0 0 0-0.2-0.2v-127.8c0-70.6-57.4-128-128-128h-143.6c93.8-117 238-192 399.6-192 282.8 0 512 229.2 512 512 0 68-13.2 132.8-37.2 192h-218.6z" />
+<glyph unicode="&#xea18;" glyph-name="icon-pointer-left" d="M766-64l-256 512 256 512h-256l-256-512 256-512z" />
+<glyph unicode="&#xea19;" glyph-name="icon-pointer-right" d="M254 960l256-512-256-512h256l256 512-256 512z" />
+<glyph unicode="&#xea1a;" glyph-name="icon-refresh" d="M1024 499.2v460.8l-175.8-175.8c-85.2 69.6-190.8 107.6-302 107.6-127.6 0-247.6-49.8-338-140s-140-210.4-140-338 49.8-247.6 140-338 210.4-140 338-140 247.6 49.8 338 140c74 74 120.8 167.8 135 269.6h-138.6c-32-155.4-169.8-272.8-334.6-272.8-188.2 0-341.4 153.2-341.4 341.4s153.4 341.2 341.6 341.2c76.8 0 147.6-25.4 204.8-68.2l-187.8-187.8h460.8z" />
+<glyph unicode="&#xea1b;" glyph-name="icon-save" d="M192.2 384c-0.2 0-0.2 0 0 0l-0.2-448h640v447.8c0 0 0 0-0.2 0.2h-639.6zM978.8 749.2l-165.4 165.4c-25 25-74.2 45.4-109.4 45.4h-576c-70.4 0-128-57.6-128-128v-768c0-70.4 57.6-128 128-128v448c0 35.2 28.8 64 64 64h640c35.2 0 64-28.8 64-64v-448c70.4 0 128 57.6 128 128v576c0 35.2-20.4 84.4-45.2 109.2zM704 704c0-35.2-28.8-64-64-64h-448c-35.2 0-64 28.8-64 64v192h320v-192h128v192h128v-192z" />
+<glyph unicode="&#xea1c;" glyph-name="icon-save-as" d="M978.8 621.2l-64 64c24.8-24.8 45.2-74 45.2-109.2v-448c0-70.4-57.6-128-128-128h-640c-18.8 0-36.6 4.2-52.6 11.4 20.2-44.4 65-75.4 116.6-75.4h640c70.4 0 128 57.6 128 128v448c0 35.2-20.4 84.4-45.2 109.2zM704 64v319.8c0 0 0 0-0.2 0.2h-511.6l-0.2-320h512zM192 448h512c35.2 0 64-28.8 64-64v-320c70.4 0 128 57.6 128 128v448c0 35.2-20.4 84.4-45.2 109.2l-165.4 165.4c-25 25-74.2 45.4-109.4 45.4h-448c-70.4 0-128-57.6-128-128v-640c0-70.4 57.6-128 128-128v320c0 35.2 28.8 64 64 64zM128 896h192v-192h128v192h128v-192c0-35.2-28.8-64-64-64h-320c-35.2 0-64 28.8-64 64v192z" />
+<glyph unicode="&#xea1d;" glyph-name="icon-sine" d="M1024 448c-1.8 7.2-3.4 14.4-5.2 21.8-20.2 86.2-53.4 209.4-98.4 307.2-22.4 49-45.4 86.6-70.2 115.2-48.6 56-98.4 67.8-131.8 67.8-33.2 0-83.2-11.8-131.8-67.8-24.6-28.6-47.6-66.2-70-115.2-44.8-97.8-78.2-221-98.4-307.2-21.8-93-46.6-175.4-72-238.4-16.4-40.6-30.4-66.4-40.8-82.8-10.4 16.2-24.4 42.2-40.8 82.8-23.2 58-46.2 132.4-66.6 216.6h-198c1.8-7.2 3.4-14.4 5.2-21.8 20.2-86.2 53.4-209.4 98.4-307.2 22.4-49 45.4-86.6 70.2-115.2 48.6-56 98.6-67.8 131.8-67.8s83.2 11.8 131.8 67.8c24.8 28.6 47.6 66.2 70.2 115.2 44.8 97.8 78.2 221 98.4 307.2 21.8 93 46.6 175.4 72 238.4 16.4 40.6 30.4 66.4 40.8 82.8 10.4-16.2 24.4-42.2 40.8-82.8 23.4-57.8 46.4-132.4 66.8-216.4h197.6z" />
+<glyph unicode="&#xea1e;" glyph-name="icon-font" d="M800-64h224l-384 1024h-256l-384-1024h224l84 224h408zM380 352l132 352 132-352z" />
+<glyph unicode="&#xea1f;" glyph-name="icon-thumbs-strip" d="M448 578c0-35.2-28.8-64-64-64h-320c-35.2 0-64 28.8-64 64v320c0 35.2 28.8 64 64 64h320c35.2 0 64-28.8 64-64v-320zM1024 578c0-35.2-28.8-64-64-64h-320c-35.2 0-64 28.8-64 64v320c0 35.2 28.8 64 64 64h320c35.2 0 64-28.8 64-64v-320zM448 2c0-35.2-28.8-64-64-64h-320c-35.2 0-64 28.8-64 64v320c0 35.2 28.8 64 64 64h320c35.2 0 64-28.8 64-64v-320zM1024 2c0-35.2-28.8-64-64-64h-320c-35.2 0-64 28.8-64 64v320c0 35.2 28.8 64 64 64h320c35.2 0 64-28.8 64-64v-320z" />
+<glyph unicode="&#xea20;" glyph-name="icon-two-parts-both" d="M896 960h-768c-70.4 0-128-57.6-128-128v-768c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v768c0 70.4-57.6 128-128 128zM128 832h320v-768h-320v768zM896 64h-320v768h320v-768z" />
+<glyph unicode="&#xea21;" glyph-name="icon-two-parts-one-only" d="M896 960h-768c-70.4 0-128-57.6-128-128v-768c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v768c0 70.4-57.6 128-128 128zM896 64h-320v768h320v-768z" />
+<glyph unicode="&#xea22;" glyph-name="icon-resync" d="M795.2 795.2c-79.8 65.2-178.8 100.8-283.2 100.8-119.6 0-232.2-46.6-316.8-131.2-69.4-69.4-113.2-157.4-126.6-252.8h130c29.6 145.8 158.8 256 313.4 256 72 0 138.4-23.8 192-64l-176-176h432v432l-164.8-164.8zM512 128c-72 0-138.4 23.8-192 64l176 176h-432v-432l164.8 164.8c79.8-65.2 178.8-100.8 283.2-100.8 119.6 0 232.2 46.6 316.8 131.2 69.4 69.4 113.2 157.4 126.6 252.8h-130c-29.6-145.8-158.8-256-313.4-256z" />
+<glyph unicode="&#xea23;" glyph-name="icon-reset" d="M460.8 499.2l-187.8 187.8c57.2 42.8 128 68.2 204.8 68.2 188.2 0 341.6-153.2 341.6-341.4s-153.2-341.2-341.4-341.2c-165 0-302.8 117.6-334.6 273h-138.4c14.2-101.8 61-195.6 135-269.6 90.2-90.2 210.4-140 338-140s247.6 49.8 338 140 140 210.4 140 338-49.8 247.6-140 338-210.4 140-338 140c-111.4 0-217-38-302-107.6l-176 175.6v-460.8h460.8z" />
+<glyph unicode="&#xea24;" glyph-name="icon-x-in-circle" d="M512 960c-282.8 0-512-229.2-512-512s229.2-512 512-512 512 229.2 512 512-229.2 512-512 512zM832 256l-128-128-192 192-192-192-128 128 192 192-192 192 128 128 192-192 192 192 128-128-192-192 192-192z" />
+<glyph unicode="&#xea25;" glyph-name="icon-brightness" d="M253.414 641.939l-155.172 116.384c-50.233-66.209-85.127-146.713-97.91-234.39l-0.333-2.781 191.919-27.434c8.145 56.552 29.998 106.879 62.068 149.006l-0.573-0.784zM191.98 402.283l-191.919-27.434c13.115-90.459 48.009-170.963 99.174-238.453l-0.931 1.281 155.111 116.384c-31.476 41.347-53.309 91.675-61.231 146.504l-0.204 1.719zM466.283 768.020l-27.434 191.919c-90.459-13.115-170.963-48.009-238.453-99.174l1.281 0.931 116.384-155.111c41.347 31.476 91.675 53.309 146.504 61.231l1.719 0.204zM822.323 861.758c-66.209 50.233-146.713 85.127-234.39 97.91l-2.781 0.333-27.434-191.919c56.552-8.145 106.879-29.998 149.006-62.068l-0.784 0.573zM832.020 493.717l191.919 27.434c-13.115 90.459-48.009 170.963-99.174 238.453l0.931-1.281-155.111-116.384c31.476-41.347 53.309-91.675 61.231-146.504l0.204-1.719zM201.677 34.242c66.209-50.233 146.713-85.127 234.39-97.91l2.781-0.333 27.434 191.919c-56.552 8.145-106.879 29.998-149.006 62.068l0.784-0.573zM770.586 254.061l155.131-116.343c50.233 66.209 85.127 146.713 97.91 234.39l0.333 2.781-191.919 27.434c-8.125-56.564-29.966-106.906-62.028-149.049l0.574 0.786zM557.717 127.98l27.434-191.919c90.459 13.115 170.963 48.009 238.453 99.174l-1.281-0.931-116.384 155.111c-41.347-31.476-91.675-53.309-146.504-61.231l-1.719-0.204zM770.586 448c0-142.813-115.773-258.586-258.586-258.586s-258.586 115.773-258.586 258.586c0 142.813 115.773 258.586 258.586 258.586s258.586-115.773 258.586-258.586z" />
+<glyph unicode="&#xea26;" glyph-name="icon-contrast" d="M512 960c-282.78 0-512-229.24-512-512s229.22-512 512-512 512 229.24 512 512-229.22 512-512 512zM783.52 176.48c-69.111-69.481-164.785-112.481-270.502-112.481-0.358 0-0.716 0-1.074 0.001h0.055v768c212.070-0.010 383.982-171.929 383.982-384 0-106.034-42.977-202.031-112.462-271.52v0z" />
+<glyph unicode="&#xea27;" glyph-name="icon-expand" d="M960 960c0 0 0 0 0 0h-320v-128h165.4l-210.6-210.8c-25-25-25-65.6 0-90.6 12.4-12.4 28.8-18.8 45.2-18.8s32.8 6.2 45.2 18.8l210.8 210.8v-165.4h128v384h-64zM896 154.6l-210.8 210.6c-25 25-65.6 25-90.6 0s-25-65.6 0-90.6l210.8-210.6h-165.4v-128h384v384h-128v-165.4zM218.6 832h165.4v128h-320c0 0 0 0 0 0h-64v-384h128v165.4l210.8-210.8c12.4-12.4 28.8-18.8 45.2-18.8s32.8 6.2 45.2 18.8c25 25 25 65.6 0 90.6l-210.6 210.8zM338.8 365.2l-210.8-210.6v165.4h-128v-384h384v128h-165.4l210.8 210.8c25 25 25 65.6 0 90.6-25.2 24.8-65.6 24.8-90.6-0.2z" />
+<glyph unicode="&#xea28;" glyph-name="icon-list-view" d="M0 896h1024v-128h-1024v128zM0 640h1024v-128h-1024v128zM0 384h1024v-128h-1024v128zM0 128h1024v-128h-1024v128z" />
+<glyph unicode="&#xea29;" glyph-name="icon-grid-snap-to" d="M382 130h448v448h-448v-448zM510 450h192v-192h-192v192zM-2 386h320v-64h-320v64zM894 386h128v-64h-128v64zM574 962h64v-320h-64v320zM574 66h64v-128h-64v128zM574 386h64v-64h-64v64z" />
+<glyph unicode="&#xea2a;" glyph-name="icon-grid-snap-no" d="M768 384h192v-64h-192v64zM256 384h192v-64h-192v64zM0 384h192v-64h-192v64zM640 448h-64v-64h-64v-64h64v-64h64v64h64v64h-64zM576 704h64v-192h-64v192zM576 960h64v-192h-64v192zM576 192h64v-192h-64v192z" />
+<glyph unicode="&#xea2b;" glyph-name="icon-frame-show" d="M0 896v-896h1024v896h-1024zM896 128h-768v640h768v-640zM192 704h384v-128h-384v128z" />
+<glyph unicode="&#xea2c;" glyph-name="icon-frame-hide" d="M128 770h420l104 128h-652v-802.4l128 157.4zM896 130h-420l-104-128h652v802.4l-128-157.4zM832 962l-832-1024h192l832 1024zM392 578l104 128h-304v-128z" />
+<glyph unicode="&#xea2d;" glyph-name="icon-import" d="M832 767.6v-639.4c0-0.2-0.2-0.2-0.4-0.4h-319.6v-192h320c105.6 0 192 86.4 192 192v640.2c0 105.6-86.4 192-192 192h-320v-192h319.6c0.2 0 0.4-0.2 0.4-0.4zM192 256v-192l384 384-384 384v-192h-192v-384z" />
+<glyph unicode="&#xea2e;" glyph-name="icon-export" d="M192 128.34v639.32l0.34 0.34h319.66v192h-320c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h320v192h-319.66zM1024 448l-384 384v-192h-192v-384h192v-192l384 384z" />
+<glyph unicode="&#xea2f;" glyph-name="icon-font-size" horiz-adv-x="1504" d="M1226.4 640h-176l-76.22-203.24 77-205.34 87.22 232.58 90.74-242h-174.44l49.5-132h174.44l57.76-154h154l-264 704zM384 960l-384-1024h224l84 224h408l84-224h224l-384 1024zM380 352l132 352 132-352z" />
+<glyph unicode="&#xea30;" glyph-name="icon-clear-data" d="M632 648l-120-120-120 120-80-80 120-120-120-120 80-80 120 120 120-120 80 80-120 120 120 120-80 80zM512 960c-282.76 0-512-86-512-192v-640c0-106 229.24-192 512-192s512 86 512 192v640c0 106-229.24 192-512 192zM512 128c-176.731 0-320 143.269-320 320s143.269 320 320 320c176.731 0 320-143.269 320-320v0c0-176.731-143.269-320-320-320v0z" />
+<glyph unicode="&#xea31;" glyph-name="icon-history" d="M576 896c-247.4 0-448-200.6-448-448h-128l192-192 192 192h-128c0 85.4 33.2 165.8 93.8 226.2 60.4 60.6 140.8 93.8 226.2 93.8s165.8-33.2 226.2-93.8c60.6-60.4 93.8-140.8 93.8-226.2s-33.2-165.8-93.8-226.2c-60.4-60.6-140.8-93.8-226.2-93.8s-165.8 33.2-226.2 93.8l-90.6-90.6c81-81 193-131.2 316.8-131.2 247.4 0 448 200.6 448 448s-200.6 448-448 448zM576 688c-26.6 0-48-21.4-48-48v-211.8l142-142c9.4-9.4 21.6-14 34-14s24.6 4.6 34 14c18.8 18.8 18.8 49.2 0 67.8l-114 114v172c0 26.6-21.4 48-48 48z" />
+<glyph unicode="&#xea32;" glyph-name="icon-arrow-up-to-parent" horiz-adv-x="1056" d="M643.427 134.739c-81.955 0.697-148.179 67.065-148.642 149.010v395.872l296.871-247.393v197.914l-395.828 329.857-395.828-328.62v-197.502l296.871 246.156v-396.241c0-190.905 155.239-346.556 346.144-346.968l412.321-0.825 0.412 197.914z" />
+<glyph unicode="&#xea33;" glyph-name="icon-crosshair-in-circle" d="M512 960c-282.8 0-512-229.2-512-512s229.2-512 512-512 512 229.2 512 512-229.2 512-512 512zM783.6 176.4c-54.634-54.8-125.77-93.12-205.322-106.874l-2.278-0.326v250.8h-128v-250.8c-161.302 28.062-286.738 153.497-314.468 312.5l-0.332 2.3h250.8v128h-250.8c28.062 161.302 153.497 286.738 312.5 314.468l2.3 0.332v-250.8h128v250.8c161.302-28.062 286.738-153.497 314.468-312.5l0.332-2.3h-250.8v-128h250.8c-14.080-81.83-52.4-152.966-107.191-207.591l-0.009-0.009z" />
+<glyph unicode="&#xea34;" glyph-name="icon-target" d="M512 576c70.692 0 128-57.308 128-128s-57.308-128-128-128c-70.692 0-128 57.308-128 128v0c0.114 70.647 57.353 127.886 127.989 128h0.011zM512 704c-141.385 0-256-114.615-256-256s114.615-256 256-256c141.385 0 256 114.615 256 256v0c-0.114 141.339-114.661 255.886-255.989 256h-0.011zM512 832c211.87-0.128 383.575-171.912 383.575-383.8 0-211.967-171.833-383.8-383.8-383.8s-383.8 171.833-383.8 383.8c0 105.99 42.963 201.945 112.425 271.4v0c69.21 69.437 164.944 112.401 270.713 112.401 0.312 0 0.624 0 0.936-0.001h-0.048zM512 960c-282.8 0-512-229.2-512-512s229.2-512 512-512 512 229.2 512 512-229.2 512-512 512z" />
+<glyph unicode="&#xea35;" glyph-name="icon-items-collapse" d="M45.2 301.2h229.6l-274.8-274.6 90.6-90.6 274.6 274.8v-229.6h128v448h-448v-128zM1024 869.4l-90.6 90.6-274.6-274.8v229.6h-128v-448h448v128h-229.6l274.8 274.6z" />
+<glyph unicode="&#xea36;" glyph-name="icon-items-expand" d="M448 64h-229.4l274.6 274.8-90.4 90.4-274.8-274.6v229.4h-128v-448h448v128zM530.8 557.2l90.4-90.4 274.8 274.6v-229.4h128v448h-448v-128h229.4l-274.6-274.8z" />
+<glyph unicode="&#xea37;" glyph-name="icon-3-dots" d="M256 448c0-70.692-57.308-128-128-128s-128 57.308-128 128c0 70.692 57.308 128 128 128s128-57.308 128-128zM640 448c0-70.692-57.308-128-128-128s-128 57.308-128 128c0 70.692 57.308 128 128 128s128-57.308 128-128zM1024 448c0-70.692-57.308-128-128-128s-128 57.308-128 128c0 70.692 57.308 128 128 128s128-57.308 128-128z" />
+<glyph unicode="&#xea38;" glyph-name="icon-grid-on" d="M1024 576v128h-256v256h-128v-256h-256v256h-128v-256h-256v-128h256v-256h-256v-128h256v-256h128v256h256v-256h128v256h256v128h-256v256zM640 320h-256v256h256z" />
+<glyph unicode="&#xea39;" glyph-name="icon-grid-off" d="M256 408.6l128 157.6v9.8h8l104 128h-112v256h-128v-256h-256v-128h256v-167.4zM184 320h-184v-128h80l104 128zM768 487.4l-128-157.6v-9.8h-8l-104-128h112v-256h128v256h256v128h-256v167.4zM840 576h184v128h-80l-104-128zM832 960l-832-1024h192l832 1024h-192z" />
+<glyph unicode="&#xea3a;" glyph-name="icon-camera" d="M896 704h-128l-128 256h-256l-128-256h-128c-70.601-0.227-127.773-57.399-128-127.978v-512.022c0.227-70.601 57.399-127.773 127.978-128h768.022c70.601 0.227 127.773 57.399 128 127.978v512.022c-0.227 70.601-57.399 127.773-127.978 128h-0.022zM512 96c-141.385 0-256 114.615-256 256s114.615 256 256 256c141.385 0 256-114.615 256-256v0c0-141.385-114.615-256-256-256v0z" />
+<glyph unicode="&#xea3b;" glyph-name="icon-folders-collapse" d="M896 640v-448c-0.215-70.606-57.394-127.785-127.979-128h-576.021c0.215-70.606 57.394-127.785 127.979-128h576.021c70.606 0.215 127.785 57.394 128 127.979v448.021c-0.215 70.606-57.394 127.785-127.979 128h-0.021zM832 256v448c-0.215 70.606-57.394 127.785-127.979 128h-192.021l-101.5 82.74c-24.88 24.9-74.040 45.26-109.24 45.26h-237.26c-35.305-0.102-63.898-28.695-64-63.99v-640.010c0.215-70.606 57.394-127.785 127.979-128h576.021c70.606 0.215 127.785 57.394 128 127.979v0.021zM128 316v516l256-260z" />
+<glyph unicode="&#xeb00;" glyph-name="icon-activity" d="M576 896h-256l320-320h-290.256c-44.264 76.516-126.99 128-221.744 128h-128v-512h128c94.754 0 177.48 51.484 221.744 128h290.256l-320-320h256l448 448-448 448z" />
+<glyph unicode="&#xeb01;" glyph-name="icon-activity-mode" d="M512 960c-214.8 0-398.8-132.4-474.8-320h90.8c56.8 0 108-24.8 143-64h241l-192 192h256l320-320-320-320h-256l192 192h-241c-35-39.2-86.2-64-143-64h-90.8c76-187.6 259.8-320 474.8-320 282.8 0 512 229.2 512 512s-229.2 512-512 512z" />
+<glyph unicode="&#xeb02;" glyph-name="icon-autoflow-tabular" d="M192 960c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h64v1024h-64zM384 960h256v-1024h-256v1024zM832 960h-64v-704h256v512c0 105.6-86.4 192-192 192z" />
+<glyph unicode="&#xeb03;" glyph-name="icon-clock" d="M512 960c-282.8 0-512-229.2-512-512s229.2-512 512-512 512 229.2 512 512-229.2 512-512 512zM782 270c-12.8-22.2-36.6-36-62.4-36-12.6 0-25 3.4-36 9.6l-222 128.2c-0.8 0.4-1.6 1-2.4 1.4l-0.8 0.6-1.8 1.2-2.4 2-1.8 1.4-0.6 0.6c-0.8 0.6-1.4 1.2-2.2 1.8v0c-5 4.6-9.4 10-13 15.8-0.2 0.4-0.6 1-0.8 1.4s-0.6 1-0.8 1.4c-3.2 6-5.8 12.4-7.2 19.2v0.2c-0.2 1-0.4 1.8-0.6 2.8 0 0.2 0 0.6-0.2 0.8-0.2 0.6-0.2 1.4-0.2 2.2s-0.2 1-0.2 1.6 0 1-0.2 1.6-0.2 1.6-0.2 2.2c0 0.4 0 0.6 0 1 0 1 0 1.8 0 2.8 0 0 0 0.2 0 0.4v363.8c0 39.8 32.2 72 72 72s72-32.2 72-72v-322.4l185.8-107.2c34.2-20 45.8-64 26-98.4z" />
+<glyph unicode="&#xeb04;" glyph-name="icon-database" d="M1024 768c0-106.039-229.23-192-512-192s-512 85.961-512 192c0 106.039 229.23 192 512 192s512-85.961 512-192zM512 448c-282.77 0-512 85.962-512 192v-512c0-106.038 229.23-192 512-192s512 85.962 512 192v512c0-106.038-229.23-192-512-192z" />
+<glyph unicode="&#xeb05;" glyph-name="icon-database-query" d="M683.52 140.714c-50.782-28.456-109.284-44.714-171.52-44.714-194.094 0-352 157.906-352 352s157.906 352 352 352 352-157.906 352-352c0-62.236-16.258-120.738-44.714-171.52l191.692-191.692c8.516 13.89 13.022 28.354 13.022 43.212v640c0 106.038-229.23 192-512 192s-512-85.962-512-192v-640c0-106.038 229.23-192 512-192 126.11 0 241.548 17.108 330.776 45.46l-159.256 159.254zM352 448c0-88.224 71.776-160 160-160s160 71.776 160 160-71.776 160-160 160-160-71.776-160-160z" />
+<glyph unicode="&#xeb06;" glyph-name="icon-dataset" d="M896 768h-320c-16.4 16.4-96.8 96.8-109.2 109.2l-37.4 37.4c-25 25-74.2 45.4-109.4 45.4h-256c-35.2 0-64-28.8-64-64v-384c0 70.4 57.6 128 128 128h768c70.4 0 128-57.6 128-128v128c0 70.4-57.6 128-128 128zM896 512h-768c-70.4 0-128-57.6-128-128v-320c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v320c0 70.4-57.6 128-128 128zM320 64h-128v320h128v-320zM576 64h-128v320h128v-320zM832 64h-128v320h128v-320z" />
+<glyph unicode="&#xeb07;" glyph-name="icon-datatable" d="M1024 768c0-106.039-229.23-192-512-192s-512 85.961-512 192c0 106.039 229.23 192 512 192s512-85.961 512-192zM512 448c-282.8 0-512 86-512 192v-512c0-106 229.2-192 512-192s512 86 512 192v512c0-106-229.2-192-512-192zM896 385v-256c-36.6-15.6-79.8-28.8-128-39.4v256c48.2 10.6 91.4 23.8 128 39.4zM256 345.6v-256c-48.2 10.4-91.4 23.8-128 39.4v256c36.6-15.6 79.8-28.8 128-39.4zM384 70v256c41-4 83.8-6 128-6s87 2.2 128 6v-256c-41-4-83.8-6-128-6s-87 2.2-128 6z" />
+<glyph unicode="&#xeb08;" glyph-name="icon-dictionary" d="M832 320c105.6 0 192 86.4 192 192v256c0 105.6-86.4 192-192 192v-320l-128 64-128-64v320h-384c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h640c105.6 0 192 86.4 192 192v192c0-105.6-86.4-192-192-192h-640v192h640z" />
+<glyph unicode="&#xeb09;" glyph-name="icon-folder" d="M896 768h-320c-16.4 16.4-96.8 96.8-109.2 109.2l-37.4 37.4c-25 25-74.2 45.4-109.4 45.4h-256c-35.2 0-64-28.8-64-64v-384c0 70.4 57.6 128 128 128h768c70.4 0 128-57.6 128-128v128c0 70.4-57.6 128-128 128zM896 512h-768c-70.4 0-128-57.6-128-128v-320c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v320c0 70.4-57.6 128-128 128z" />
+<glyph unicode="&#xeb0a;" glyph-name="icon-image" d="M896 960h-768c-70.4 0-128-57.6-128-128v-768c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v768c0 70.4-57.6 128-128 128zM896 64h-768v768h768v-768zM320 704l-128-128v-448h640v320l-128 128-128-128z" />
+<glyph unicode="&#xeb0b;" glyph-name="icon-layout" d="M448 960h-256c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h256v1024zM832 960h-256v-577.664h448v385.664c0 105.6-86.4 192-192 192zM576-64h256c105.6 0 192 86.4 192 192v129.664h-448v-321.664z" />
+<glyph unicode="&#xeb0c;" glyph-name="icon-object" d="M512-64l512 320v384l-512.020 320-511.98-320v-384l512-320zM512 768l358.4-224-358.4-224-358.4 224 358.4 224z" />
+<glyph unicode="&#xeb0d;" glyph-name="icon-object-unknown" d="M511.98 960l-511.98-320v-384l512-320 512 320v384l-512.020 320zM586.22 64h-141.36v136.64h141.36v-136.64zM721.040 462.1c-11.94-17.020-34.9-38.78-68.84-65.24l-33.48-26c-18.24-14.18-30.34-30.74-36.32-49.64-3.78-11.98-5.82-30.58-6.14-55.8h-128.12c1.88 53.26 6.92 90.060 15.080 110.4 8.18 20.34 29.22 43.74 63.16 70.22l34.42 26.94c11.3 8.52 20.42 17.8 27.34 27.9 12.56 17.34 18.86 36.4 18.86 57.2 0 23.94-7 45.78-20.98 65.48-14 19.7-39.54 29.54-76.64 29.54s-62.34-12.14-77.6-36.4c-15.24-24.28-22.88-49.48-22.88-75.64h-136.64c3.78 89.84 35.14 153.5 94.080 191.020 37.18 23.94 82.9 35.94 137.12 35.94 71.22 0 130.42-17.020 177.54-51.060s70.68-84.48 70.68-151.3c0-40.98-10.22-75.5-30.66-103.54z" />
+<glyph unicode="&#xeb0e;" glyph-name="icon-packet" d="M512 960l-512-320v-512c0-105.6 86.4-192 192-192h640c105.6 0 192 86.4 192 192v512l-512 320zM512 768l358.4-224-358.4-224-358.4 224 358.4 224z" />
+<glyph unicode="&#xeb0f;" glyph-name="icon-page" d="M704 448c-105.6 0-192 86.4-192 192v320h-320c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h640c105.6 0 192 86.4 192 192v320h-320zM768 576h256l-384 384v-256c0-70.4 57.6-128 128-128z" />
+<glyph unicode="&#xeb10;" glyph-name="icon-plot-overlay" d="M830 960h-636c-106.7 0-194-87.3-194-194v-406.82c14.18-18.64 25.66-28.34 32-30.84 14.28 5.62 54.44 47.54 92.96 146 42.46 108.38 116.32 237.66 227.040 237.66 52.4 0 101.42-29.16 145.7-86.68 37.34-48.5 64.84-108.92 81.34-151.080 38.52-98.38 78.68-140.3 92.96-146 14.28 5.62 54.44 47.54 92.96 146 42.46 108.48 116.32 237.76 227.040 237.76 11.355-0.003 22.389-1.366 32.952-3.936l-0.952 0.196v57.74c0 106.7-87.3 194-194 194zM992 567.66c-14.28-5.62-54.44-47.52-92.96-146-42.46-108.38-116.32-237.66-227.040-237.66-52.4 0-101.42 29.16-145.7 86.68-37.34 48.5-64.84 108.92-81.34 151.080-38.52 98.38-78.68 140.3-92.96 146-14.28-5.62-54.44-47.52-92.96-146-42.46-108.48-116.32-237.76-227.040-237.76-11.355 0.003-22.389 1.367-32.952 3.936l0.952-0.196v-57.74c0-106.7 87.3-194 194-194h636c106.7 0 194 87.3 194 194v406.82c-14.18 18.64-25.66 28.34-32 30.84z" />
+<glyph unicode="&#xeb11;" glyph-name="icon-plot-stacked" d="M89.6 648c24.98 0 48.96 26.52 85.52 70.18 45.42 54.28 102 121.82 196 121.82 44.64 0 86.62-15.46 124.8-46 28.68-22.9 51.16-50.42 72.92-77.060 38.42-46.94 59.16-68.94 83.96-68.94h371.2v118c0 106.7-87.3 194-194 194h-636c-106.7 0-194-87.3-194-194v-118h89.6zM529.5 549.6c-28.24 22.64-50.52 50-72 76.28-35.5 43.48-58.76 70.12-86.3 70.12-25.060 0-49.080-26.54-85.66-70.24-45.4-54.24-102-121.76-196-121.76h-89.54v-112h371.2c44 0 85.54-15.34 123.3-45.6 28.24-22.64 50.52-50 72-76.28 35.5-43.48 58.76-70.12 86.3-70.12 25.060 0 49.080 26.54 85.66 70.24 45.4 54.24 102 121.76 196 121.76h89.54v112h-371.2c-44.060 0-85.54 15.34-123.3 45.6zM934.4 248c-24.98 0-48.96-26.52-85.52-70.18-45.42-54.28-102-121.82-196-121.82-44.64 0-86.62 15.46-124.8 46-28.68 22.9-51.16 50.42-72.92 77.060-38.42 46.94-59.16 68.94-83.96 68.94h-371.2v-118c0-106.7 87.3-194 194-194h636c106.7 0 194 87.3 194 194v118h-89.6z" />
+<glyph unicode="&#xeb12;" glyph-name="icon-session" d="M635.6 435.6c6.6-4.2 13.2-8.6 19.2-13.6l120.4-96.4c29.6-23.8 83.8-23.8 113.4 0l135.2 108c0.2 4.8 0.2 9.4 0.2 14.2 0 52.2-7.8 102.4-22.2 149.8l-154.8-123.6c-58.2-46.6-140.2-59.2-211.4-38.4zM248.6 325.8l120.4 96.4c58 46.4 140 59.2 211.2 38.4-6.6 4.2-13.2 8.6-19.2 13.6l-120.4 96.4c-29.6 23.8-83.8 23.8-113.4 0l-120.2-96.6c-40-32-91.4-48-143-48-21.6 0-43 2.8-63.8 8.4 0-0.6 0-1.2 0-1.6 5-3.4 10-6.8 14.6-10.6l120.4-96.4c29.8-23.8 83.8-23.8 113.4 0zM120.6 581.8l120.4 96.4c80.2 64.2 205.6 64.2 285.8 0l120.4-96.4c29.6-23.8 83.8-23.8 113.4 0l181 144.8c-91.2 140.4-249.6 233.4-429.6 233.4-238.6 0-439.2-163.2-496-384.2 30.8-17.6 77.8-15.6 104.6 6zM689 218l-120.4 96.4c-29.6 23.8-83.8 23.8-113.4 0l-120.2-96.4c-40-32-91.4-48-143-48-47.8 0-95.4 13.8-134.2 41.4 85.6-163.6 256.8-275.4 454.2-275.4s368.6 111.8 454.2 275.4c-80.4-57.4-199.8-55.2-277.2 6.6z" />
+<glyph unicode="&#xeb13;" glyph-name="icon-tabular" d="M896 960h-768c-70.4 0-128-57.6-128-128v-768c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v768c0 70.4-57.6 128-128 128zM640 512h-256v192h256v-192zM384 448h256v-192h-256v192zM320 256h-256v192h256v-192zM320 704v-192h-256v192h256zM128 0c-17 0-33 6.6-45.2 18.8s-18.8 28.2-18.8 45.2v128h256v-192h-192zM384 0v192h256v-192h-256zM960 64c0-17-6.6-33-18.8-45.2s-28.2-18.8-45.2-18.8h-192v192h256v-128zM960 256h-256v192h256v-192zM960 512h-256v192h256v-192z" />
+<glyph unicode="&#xeb14;" glyph-name="icon-tabular-lad" d="M896 960h-768c-70.6-0.2-127.8-57.4-128-128v-768c0.2-70.6 57.4-127.8 128-128h768c70.6 0.2 127.8 57.4 128 128v768c-0.2 70.6-57.4 127.8-128 128zM64 704h256v-192h-256v192zM64 448h256v-192h-256v192zM128 0c-35.2 0.2-63.8 28.8-64 64v128h256v-192h-192zM384 0v192h256v-192h-256zM960 64c-0.2-35.2-28.8-63.8-64-64h-192v192h256v-128zM960 448v-192h-576v192h64v64h-64v192h576v-192h-64v-64h64zM782.4 412.6l-110.4 55.2v172.2c0 17.6-14.4 32-32 32s-32-14.4-32-32v-211.8l145.6-72.8c15.8-8 35-1.6 43 14.4 8 15.6 1.6 35-14.2 42.8v0z" />
+<glyph unicode="&#xeb15;" glyph-name="icon-tabular-lad-set" d="M128 192v576c-70.6-0.2-127.8-57.4-128-128v-576c0.2-70.6 57.4-127.8 128-128h576c70.6 0.2 127.8 57.4 128 128h-576c-70.6 0.2-127.8 57.4-128 128zM896 960h-576c-70.6-0.2-127.8-57.4-128-128v-576c0.2-70.6 57.4-127.8 128-128h576c70.6 0.2 127.8 57.4 128 128v576c-0.2 70.6-57.4 127.8-128 128zM256 768h192v-128h-192v128zM256 576h192v-192h-192v192zM320 192c-35.2 0.2-63.8 28.8-64 64v64h192v-128h-128zM512 192v128h192v-128h-192zM960 256c-0.2-35.2-28.8-63.8-64-64h-128v128h192v-64zM960 384h-448v384h448v-384zM832 480c17.6 0 32 14.4 32 32 0 13.8-8.8 26-21.8 30.4l-74.2 24.6v105c0 17.6-14.4 32-32 32s-32-14.4-32-32v-151l117.8-39.2c3.4-1.2 6.8-1.8 10.2-1.8z" />
+<glyph unicode="&#xeb16;" glyph-name="icon-tabular-realtime" d="M896 960h-768c-70.606-0.215-127.785-57.394-128-127.979v-768.021c0.215-70.606 57.394-127.785 127.979-128h768.021c70.606 0.215 127.785 57.394 128 127.979v768.021c-0.215 70.606-57.394 127.785-127.979 128h-0.021zM448 668l25.060-25.32c7.916-7.922 18.856-12.822 30.94-12.822s23.023 4.9 30.94 12.822v0l75.5 76.3c29.97 30.338 71.571 49.128 117.56 49.128s87.59-18.79 117.544-49.112l0.016-0.016 50.44-50.98v-152.2c-24.111 8.83-44.678 22.255-61.542 39.342l-0.018 0.018-75.5 76.3c-7.916 7.922-18.856 12.822-30.94 12.822s-23.023-4.9-30.94-12.822v0l-75.5-76.3c-29.971-30.343-71.575-49.137-117.568-49.137-20.084 0-39.331 3.584-57.137 10.146l1.145-0.369v152.2zM320 0h-192c-35.26 0.214-63.786 28.74-64 63.98v128.020h256v-192zM320 256h-256v192h256v-192zM320 512h-256v192h256v-192zM640 0h-256v192h256v-192zM448 323.38v174.5c1.88-1.74 3.74-3.5 5.56-5.34l75.5-76.3c7.916-7.922 18.856-12.822 30.94-12.822s23.023 4.9 30.94 12.822v0l75.5 76.3c29.966 30.333 71.56 49.119 117.542 49.119 43.28 0 82.673-16.643 112.128-43.879l-0.11 0.1v-174.5c-1.88 1.74-3.74 3.5-5.56 5.34l-75.5 76.3c-7.916 7.922-18.856 12.822-30.94 12.822s-23.023-4.9-30.94-12.822v0l-75.5-76.3c-29.966-30.333-71.56-49.119-117.542-49.119-43.28 0-82.673 16.643-112.128 43.879l0.11-0.1zM960 64c-0.214-35.26-28.74-63.786-63.98-64h-192.020v192h256v-128z" />
+<glyph unicode="&#xeb17;" glyph-name="icon-tabular-scrolling" d="M64 960c-35.2 0-64-28.8-64-64v-192h448v256h-384zM1024 704v192c0 35.2-28.8 64-64 64h-384v-256h448zM0 576v-192c0-35.2 28.8-64 64-64h384v256h-448zM960 320c35.2 0 64 28.8 64 64v192h-448v-256h384zM512-64l-256 256h512z" />
+<glyph unicode="&#xeb18;" glyph-name="icon-telemetry" d="M32 328.34c14.28 5.62 54.44 47.54 92.96 146 42.46 108.38 116.32 237.66 227.040 237.66 52.4 0 101.42-29.16 145.7-86.68 37.34-48.5 64.84-108.92 81.34-151.080 38.52-98.38 78.68-140.3 92.96-146 14.28 5.62 54.44 47.54 92.96 146 37.4 95.5 99.14 207.14 188.94 232.46-90.462 152.598-254.314 253.3-441.686 253.3-0.075 0-0.15 0-0.225 0h0.011c-282.76 0-512-229.24-512-512 0-0.032 0-0.070 0-0.108 0-35.719 3.641-70.587 10.572-104.254l-0.572 3.323c9.54-10.78 17.22-16.74 22-18.62zM992 567.66c-14.28-5.62-54.44-47.52-92.96-146-42.46-108.38-116.32-237.66-227.040-237.66-52.4 0-101.42 29.16-145.7 86.68-37.34 48.5-64.84 108.92-81.34 151.080-38.52 98.38-78.68 140.3-92.96 146-14.28-5.62-54.44-47.52-92.96-146-37.4-95.5-99.14-207.14-188.94-232.46 90.462-152.598 254.314-253.3 441.686-253.3 0.075 0 0.15 0 0.225 0h-0.011c282.76 0 512 229.24 512 512 0 0.032 0 0.070 0 0.108 0 35.719-3.641 70.587-10.572 104.254l0.572-3.323c-9.54 10.78-17.22 16.74-22 18.62z" />
+<glyph unicode="&#xeb19;" glyph-name="icon-timeline" d="M832 960h-640c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h640c105.6 0 192 86.4 192 192v640c0 105.6-86.4 192-192 192zM128 640v128h256v-128zM256 512h384v-128h-384zM896 128h-448v128h448zM896 384h-128v128h128zM896 640h-384v128h384z" />
+<glyph unicode="&#xeb1a;" glyph-name="icon-timer" d="M640 813.4v82.58c0 35.346-28.654 64-64 64v0h-128c-35.346 0-64-28.654-64-64v0-82.58c-185.040-55.080-320-226.48-320-429.42 0-247.42 200.58-448 448-448s448 200.58 448 448c0 202.96-135 374.4-320 429.42zM532 363.98l-263.76-211c-57.105 59.935-92.24 141.25-92.24 230.772 0 0.080 0 0.16 0 0.24v-0.012c0 185.28 150.72 336 336 336 6.72 0 13.38-0.22 20-0.62v-355.38z" />
+<glyph unicode="&#xeb1b;" glyph-name="icon-topic" d="M454.36 483.36l86.3 86.3c9.088 8.965 21.577 14.502 35.36 14.502s26.272-5.537 35.366-14.507l86.294-86.294c19.328-19.358 42.832-34.541 69.047-44.082l1.313-0.418v172.14l-57.64 57.64c-34.408 34.33-81.9 55.558-134.35 55.558s-99.943-21.228-134.354-55.562l-86.296-86.296c-9.088-8.965-21.577-14.502-35.36-14.502s-26.272 5.537-35.366 14.507l-28.674 28.654v-172.14c19.045-7.022 41.040-11.084 63.984-11.084 52.463 0 99.966 21.239 134.379 55.587l-0.003-0.003zM505.64 412.64l-86.3-86.3c-9.088-8.965-21.577-14.502-35.36-14.502s-26.272 5.537-35.366 14.507l-86.294 86.294c-2 2-4.2 4-6.36 6v-197.36c33.664-30.721 78.65-49.537 128.031-49.537 52.44 0 99.923 21.22 134.333 55.541l86.296 86.296c9.088 8.965 21.577 14.502 35.36 14.502s26.272-5.537 35.366-14.507l86.294-86.294c2-2 4.2-4 6.36-6v197.36c-33.664 30.721-78.65 49.537-128.031 49.537-52.44 0-99.923-21.22-134.333-55.541l0.004 0.004zM832 960h-128v-192h127.66l0.34-0.34v-639.32l-0.34-0.34h-127.66v-192h128c105.6 0 192 86.4 192 192v640c0 105.6-86.4 192-192 192zM320 128h-127.66l-0.34 0.34v639.32l0.34 0.34h127.66v192h-128c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h128v192z" />
+<glyph unicode="&#xeb1c;" glyph-name="icon-box-with-dashed-lines-v2" d="M0 576h128v-256h-128v256zM128 831.78l0.22 0.22h191.78v128h-192c-70.606-0.215-127.785-57.394-128-127.979v-192.021h128v191.78zM128 64.22v191.78h-128v-192c0.215-70.606 57.394-127.785 127.979-128h192.021v128h-191.78zM384 960h256v-128h-256v128zM896 64.22l-0.22-0.22h-191.78v-128h192c70.606 0.215 127.785 57.394 128 127.979v192.021h-128v-191.78zM896 960h-192v-128h191.78l0.22-0.22v-191.78h128v192c-0.215 70.606-57.394 127.785-127.979 128h-0.021zM896 576h128v-256h-128v256zM384 64h256v-128h-256v128zM256 704h512v-512h-512v512z" />
+<glyph unicode="&#xeb1d;" glyph-name="icon-summary-widget" d="M896 960h-768c-70.4 0-128-57.6-128-128v-768c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v768c0 70.4-57.6 128-128 128zM847.8 349.6l-82.6-143.2-189.6 131.6 19.2-230h-165.4l19.2 230-189.6-131.6-82.6 143.2 208.6 98.4-208.8 98.4 82.6 143.2 189.6-131.6-19.2 230h165.4l-19.2-230 189.6 131.6 82.6-143.2-208.6-98.4 208.8-98.4z" />
+<glyph unicode="&#xeb1e;" glyph-name="icon-notebook" d="M896 849.2c0 79.8-55.4 127.4-123 105.4l-773-250.6h896v145.2zM896 640h-896v-576c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v448c0 70.4-57.6 128-128 128zM832 128h-384v320h384v-320z" />
+<glyph unicode="&#xeb1f;" glyph-name="icon-tabs-view" d="M0 64c0.227-70.601 57.399-127.773 127.978-128h768.022c70.601 0.227 127.773 57.399 128 127.978v608.022h-512l-50.2 225.6c-7.6 34.2-42.6 62.4-77.8 62.4h-256c-70.601-0.227-127.773-57.399-128-127.978v-0.022zM832 192h-640v256h640zM480 960c35.2 0 70.2-28.2 77.8-62.4l36-161.6h430.2v96c-0.227 70.601-57.399 127.773-127.978 128h-0.022z" />
+<glyph unicode="&#xeb20;" glyph-name="icon-flexible-layout" d="M0 128c0-105.6 86.4-192 192-192h64v576h-256zM0 768v-128h256v320h-64c-105.6 0-192-86.4-192-192zM768-64h64c105.6 0 192 86.4 192 192v128h-256zM384 960h256v-1024h-256v1024zM832 960h-64v-576h256v384c0 105.6-86.4 192-192 192z" />
+<glyph unicode="&#xeb21;" glyph-name="icon-generator-sine" d="M152 486.2c10.8 4.2 40.8 35.6 69.8 109.4 31.8 81.4 87.2 178.4 170.2 178.4 39.4 0 76-21.8 109.2-65 28-36.4 48.8-81.6 61-113.4 29-73.8 59-105.2 69.8-109.4 10.8 4.2 40.8 35.6 69.8 109.4s74.2 155.4 141.6 174.4c-67.89 114.467-190.82 190-331.391 190-0.003 0-0.007 0-0.010 0h0.001c-212 0-384-172-384-384 0.017-26.829 2.71-53.018 7.827-78.329l-0.427 2.529c7.2-8 13-12.6 16.6-14zM884.6 483c7.235 27.919 11.392 59.972 11.4 92.995v0.005c-0.017 26.829-2.71 53.018-7.827 78.329l0.427-2.529c-7.2 8-13 12.6-16.6 14-10.8-4.2-40.8-35.6-69.8-109.4-21.8-55.8-54.6-119-100-153.2zM512 320l135 59c-4.485-0.614-9.689-0.977-14.972-1h-0.028c-39.4 0-76 21.8-109.2 65-28 36.4-48.8 81.6-61 113.4-29 73.8-59 105.2-69.8 109.4-10.8-4.2-40.8-35.6-69.8-109.4-16.4-42.2-39.2-88.4-68.8-123.2zM1024 480l-512-224-512 224v-320l512-224 512 224v320z" />
+<glyph unicode="&#xeb22;" glyph-name="icon-generator-event" d="M320 768h384v-64h-384v64zM320 512h384v-64h-384v64zM320 640h320v-64h-320v64zM256 831.8h512v-399.8l128 56v344c-0.227 70.601-57.399 127.773-127.978 128h-512.022c-70.601-0.227-127.773-57.399-128-127.978v-344.022l128-56zM658.2 384h-292.4l146.2-64 146.2 64zM512 256l-512 224v-320l512-224 512 224v320l-512-224z" />
+<glyph unicode="&#xeb23;" glyph-name="icon-gauge-v2" d="M512 960c-282.8 0-512-229.2-512-512 0-226.4 147-418.4 350.6-486l257.4 486v-503c236.8 45 416 253 416 503 0 282.8-229.2 512-512 512zM754.8 432.2c-58.967 68.597-145.842 111.772-242.8 111.772s-183.833-43.176-242.445-111.35l-0.355-0.422-146 125c8.6 10 17.4 19.6 26.8 28.8 92.628 92.679 220.619 150.006 362 150.006s269.372-57.326 361.997-150.003l0.003-0.003c9.4-9.2 18.2-18.8 26.8-28.8z" />
+<glyph unicode="&#xeb24;" glyph-name="icon-spectra" d="M768 256h-512l102.4 179.2-358.4-51.2v-254c0-106.6 87.4-194 194-194h636c106.8 0 194 87.4 194 194v62l-325.8 186.2zM830 960h-636c-106.6 0-194-87.2-194-194v-318l400 60.2 112 195.8 109.8-192h402.2v254c-0.227 107.052-86.948 193.773-193.978 194h-0.022zM1024 320v64l-384 64 384-128z" />
+<glyph unicode="&#xeb25;" glyph-name="icon-telemetry-spectra" d="M512 704l109.8-192h398.2c-31.4 252.6-247 448-508 448-282.8 0-512-229.2-512-512l400 60.2zM768 256h-512l102.4 179.2-354.4-50.6c31.2-252.8 246.8-448.6 508-448.6 201.6 0 376 116.6 459.6 286l-273.4 156.2zM640 448l384-128v64l-384 64z" />
+<glyph unicode="&#xeb26;" glyph-name="icon-pushbutton" d="M370.2 500.6c9.326-8.53 19.666-16.261 30.729-22.914l0.871-0.486c-11.077 19.209-17.664 42.221-17.8 66.76v0.040c0 39.6 17.8 77.6 50.2 107.4 37 34 87.4 52.6 141.8 52.6 40.2 0 78.2-10.2 110.2-29.2-8.918 15.653-19.693 29.040-32.268 40.482l-0.132 0.118c-37 34-87.4 52.6-141.8 52.6s-104.8-18.6-141.8-52.6c-32.4-29.8-50.2-67.8-50.2-107.4s17.8-77.6 50.2-107.4zM885.4 690.4c-40.6 154.6-192.4 269.6-373.4 269.6s-332.8-115-373.4-269.6c-86-80-138.6-187.8-138.6-306.4 0-247.4 229.2-448 512-448s512 200.6 512 448c0 118.6-52.6 226.4-138.6 306.4zM512 832c141.2 0 256-100.4 256-224s-114.8-224-256-224-256 100.4-256 224 114.8 224 256 224zM512 128c-175.4 0-318.4 127.8-320 285.4 68.8-94.8 186.4-157.4 320-157.4s251.2 62.6 320 157.4c-1.6-157.6-144.6-285.4-320-285.4z" />
+<glyph unicode="&#xeb27;" glyph-name="icon-conditional" d="M512 960c-282.76 0-512-229.24-512-512s229.24-512 512-512 512 229.24 512 512-229.24 512-512 512zM512 192l-384 256 384 256 384-256z" />
+<glyph unicode="&#xeb28;" glyph-name="icon-condition-widget" d="M832 960h-640c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h640c105.6 0 192 86.4 192 192v640c0 105.6-86.4 192-192 192zM512 192l-384 256 384 256 384-256z" />
+<glyph unicode="&#xeb29;" glyph-name="icon-alphanumeric" d="M535.6 429.4c-8.4-1.6-17.2-3-26.2-4s-18.2-2.4-27.2-4c-10.196-1.861-18.808-4.010-27.21-6.633l1.61 0.433c-8.609-2.674-16.105-6.348-22.89-10.987l0.29 0.187c-6.693-4.517-12.283-10.107-16.663-16.585l-0.137-0.215c-4.6-6.8-7.4-15.6-8.8-26s-0.4-18.4 2.4-25.2c2.746-6.688 7.224-12.195 12.881-16.122l0.119-0.078c5.967-4.053 13.057-6.94 20.704-8.161l0.296-0.039c7.592-1.527 16.319-2.4 25.25-2.4 0.123 0 0.246 0 0.369 0h-0.019c22.2 0 39.6 3.6 52.6 11s23.2 16.2 30.2 26.4c6.273 8.873 11.271 19.191 14.426 30.285l0.174 0.715c1.853 6.809 3.601 15.41 4.855 24.169l0.145 1.231 5.2 41.6c-5.4-4.217-11.723-7.564-18.583-9.689l-0.417-0.111c-6.489-2.241-14.362-4.255-22.444-5.662l-0.956-0.138zM1024 576v192h-152l24 192h-192l-24-192h-256l24 192h-192l-24-192h-232v-192h208l-32-256h-176v-192h152l-24-192h192l24 192h256l-24-192h192l24 192h232v192h-208l32 256zM702.8 548.2l-26.4-211.8c-2.231-15.809-3.537-34.122-3.6-52.727v-0.073c0-16.8 2.2-29.4 6.4-37.8h-113.4c-1.342 5.556-2.338 12.122-2.781 18.84l-0.019 0.36c-0.261 3.524-0.409 7.634-0.409 11.778 0 2.962 0.076 5.907 0.226 8.832l-0.017-0.41c-18.663-17.401-41.395-30.694-66.597-38.289l-1.203-0.311c-22.627-6.956-48.639-10.974-75.586-11h-0.014c-0.764-0.011-1.666-0.018-2.569-0.018-18.098 0-35.598 2.563-52.156 7.345l1.325-0.328c-15.991 4.512-29.851 12.090-41.545 22.122l0.145-0.122c-11.233 9.982-19.792 22.733-24.624 37.192l-0.176 0.608c-5.2 15.2-6.4 33.4-3.8 54.4s9.4 42.2 19.4 57.2c9.524 14.399 21.535 26.346 35.532 35.512l0.468 0.288c13.387 8.662 28.922 15.533 45.512 19.765l1.088 0.235c13.436 3.792 30.801 7.554 48.47 10.41l2.93 0.39c17 2.6 33.8 4.6 50.4 6.2 16.628 1.527 31.69 4.070 46.349 7.643l-2.149-0.443c13 3 23.6 7.6 31.6 13.6s12.6 15 13.6 26.4 0.8 21.8-2.4 28.8c-2.849 6.902-7.542 12.56-13.468 16.517l-0.132 0.083c-6.217 4.011-13.604 6.78-21.543 7.774l-0.257 0.026c-7.897 1.277-17 2.007-26.274 2.007-0.537 0-1.073-0.002-1.609-0.007l0.082 0.001c-22 0-40-4.6-53.8-14.2s-23-25.2-28-47.2h-111.8c4.8 26.2 14.2 48 27.8 65.4 13.475 16.978 29.89 30.968 48.574 41.377l0.826 0.423c18.192 10.038 39.297 17.806 61.619 22.175l1.381 0.225c20.488 4.162 44.053 6.563 68.171 6.6h0.029c21.8-0.005 43.239-1.532 64.222-4.479l-2.422 0.279c20.641-2.809 39.324-8.783 56.401-17.461l-1.001 0.461c15.909-8.108 28.858-20.031 37.967-34.601l0.233-0.399c9-15 12.2-34.8 9-59.6z" />
+<glyph unicode="&#xeb2a;" glyph-name="icon-image-telemetry" d="M512 960c-282.8 0-512-229.2-512-512s229.2-512 512-512 512 229.2 512 512-229.2 512-512 512zM783.6 176.4c-69.581-69.675-165.757-112.776-272-112.776-212.298 0-384.4 172.102-384.4 384.4s172.102 384.4 384.4 384.4c212.298 0 384.4-172.102 384.4-384.4 0-0.008 0-0.017 0-0.025v0.001c0.001-0.264 0.001-0.575 0.001-0.887 0-105.769-42.964-201.503-112.391-270.703l-0.010-0.010zM704 576l-128-128-192 192-192-192c0-176.731 143.269-320 320-320s320 143.269 320 320v0z" />
+<glyph unicode="&#xeb2b;" glyph-name="icon-telemetry-aggregate" d="M78 564.56c14 41.44 37.48 100.8 69.2 148.36 38.62 57.78 82.38 87.080 130.14 87.080s91.5-29.3 130-87.080c31.72-47.56 55.14-106.92 69.2-148.36 30.88-90.96 63.12-134.98 78-146.54 14.94 11.56 47.2 55.58 78 146.54 14 41.44 37.48 100.8 69.22 148.36q27.8 41.7 59.12 63.5c-75.7 111.377-201.81 183.58-344.783 183.58-0.034 0-0.068 0-0.103 0h0.006c-229.76 0-416-186.24-416-416 0-0.071 0-0.156 0-0.24 0-39.119 5.396-76.977 15.484-112.871l-0.704 2.931c16.78 21.74 40.4 63.34 63.22 130.74zM754 523.44c-14-41.44-37.48-100.8-69.2-148.36-38.56-57.78-82.32-87.080-130-87.080s-91.5 29.3-130 87.080c-31.72 47.56-55.14 106.92-69.2 148.36-30.88 90.96-63.14 134.98-78 146.54-14.94-11.56-47.2-55.58-78-146.54-14.38-41.44-37.8-100.8-69.6-148.36q-27.8-41.7-59.12-63.5c75.7-111.378 201.81-183.58 344.783-183.58 0.119 0 0.237 0 0.356 0h-0.019c229.76 0 416 186.24 416 416 0 0.071 0 0.156 0 0.24 0 39.119-5.396 76.977-15.484 112.871l0.704-2.931c-16.78-21.74-40.4-63.34-63.22-130.74zM921.56 625.38c4.098-24.449 6.44-52.617 6.44-81.332 0-0.017 0-0.034 0-0.051v0.003c0-0.095 0-0.208 0-0.32 0-282.593-229.087-511.68-511.68-511.68-0.113 0-0.225 0-0.338 0h0.018c-0.014 0-0.031 0-0.048 0-28.716 0-56.884 2.342-84.325 6.845l2.993-0.405c72.483-63.623 168.109-102.44 272.802-102.44 0.203 0 0.406 0 0.61 0h-0.031c229.76 0 416 186.24 416 416 0 0.172 0 0.375 0 0.578 0 104.692-38.817 200.319-102.844 273.271l0.404-0.47z" />
+<glyph unicode="&#xeb2c;" glyph-name="icon-bar-graph" d="M832 960h-640c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h640c105.6 0 192 86.4 192 192v640c0 105.6-86.4 192-192 192zM267.64 64h-139.64v448h139.64zM477.1 64h-139.64v768h139.64zM686.54 64h-139.64v320h139.64zM896 64h-139.64v640h139.64z" />
+<glyph unicode="&#xeb2d;" glyph-name="icon-map" d="M896 894.6l-128-62.6v-896l128 62.6c70.4 34.42 128 120.2 128 190.6v640c0 70.4-57.6 99.82-128 65.4zM320 48l387.2-96.8v896l-387.2 96.8v-896zM259.2 959.2l-3.2 0.8-128-62.6c-70.4-34.42-128-120.2-128-190.6v-640c0-70.4 57.6-99.82 128-65.4l128 62.6 3.2-0.8z" />
+<glyph unicode="&#xeb2e;" glyph-name="icon-plan" d="M256 768v64c0.215 70.606 57.394 127.785 127.979 128h256.021c70.606-0.215 127.785-57.394 128-127.979v-64.021zM832 832v-128h-640v128c-105.6 0-192-86.4-192-192v-512c0-105.6 86.4-192 192-192h640c105.6 0 192 86.4 192 192v512c0 105.6-86.4 192-192 192zM128 384v128h256v-128zM640 128h-384v128h384zM896 128h-128v128h128zM896 384h-384v128h384z" />
+<glyph unicode="&#xeb2f;" glyph-name="icon-timelist" d="M896 960h-768c-70.606-0.215-127.785-57.394-128-127.979v-768.021c0.215-70.606 57.394-127.785 127.979-128h768.021c70.606 0.215 127.785 57.394 128 127.979v768.021c-0.215 70.606-57.394 127.785-127.979 128h-0.021zM426.94 426.54c-8.054-15.864-24.249-26.545-42.938-26.545-7.823 0-15.209 1.871-21.734 5.191l0.273-0.126-154.54 77.28v221.66c0 26.51 21.49 48 48 48s48-21.49 48-48v0-162.34l101.46-50.72c15.864-8.054 26.545-24.249 26.545-42.938 0-7.823-1.871-15.209-5.191-21.734l0.126 0.273zM896 64h-320v128h320zM896 256h-320v128h320zM896 448h-320v128h320zM896 640h-320v128h320z" />
+<glyph unicode="&#xeb30;" glyph-name="icon-plot-scatter" d="M192 960c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h640c105.6 0 192 86.4 192 192v640c0 105.6-86.4 192-192 192zM128 608c0 53.019 42.981 96 96 96s96-42.981 96-96c0-53.019-42.981-96-96-96v0c-53.019 0-96 42.981-96 96v0zM288 128c-53.019 0-96 42.981-96 96s42.981 96 96 96c53.019 0 96-42.981 96-96v0c0-53.019-42.981-96-96-96v0zM544 320c-53.019 0-96 42.981-96 96s42.981 96 96 96c53.019 0 96-42.981 96-96v0c0-53.019-42.981-96-96-96v0zM544 640c-53.019 0-96 42.981-96 96s42.981 96 96 96c53.019 0 96-42.981 96-96v0c0-53.019-42.981-96-96-96v0zM800 128c-53.019 0-96 42.981-96 96s42.981 96 96 96c53.019 0 96-42.981 96-96v0c0-53.019-42.981-96-96-96v0z" />
+<glyph unicode="&#xeb31;" glyph-name="icon-notebook-restricted" d="M896 849.28c0 79.9-55.38 127.32-123.080 105.36l-772.92-250.64h896v145.28zM896 640h-896v-576c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v448c0 70.4-57.6 128-128 128zM256 128h-128v128h128v-128zM256 320h-128v128h128v-128zM896 128h-512v128h512v-128zM896 320h-512v128h512v-128z" />
</font></defs></svg> \ No newline at end of file
diff --git a/src/styles/fonts/Open-MCT-Symbols-16px.ttf b/src/styles/fonts/Open-MCT-Symbols-16px.ttf
index 9504509ce..33008462f 100644
--- a/src/styles/fonts/Open-MCT-Symbols-16px.ttf
+++ b/src/styles/fonts/Open-MCT-Symbols-16px.ttf
Binary files differ
diff --git a/src/styles/fonts/Open-MCT-Symbols-16px.woff b/src/styles/fonts/Open-MCT-Symbols-16px.woff
index 18347a354..4fbc1d1e4 100644
--- a/src/styles/fonts/Open-MCT-Symbols-16px.woff
+++ b/src/styles/fonts/Open-MCT-Symbols-16px.woff
Binary files differ
diff --git a/src/styles/notebook.scss b/src/styles/notebook.scss
index e335b9037..09d74e057 100644
--- a/src/styles/notebook.scss
+++ b/src/styles/notebook.scss
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -45,13 +45,13 @@
&__nav {
flex: 0 0 auto;
+
* {
overflow: hidden;
}
}
.c-sidebar {
- background: $sideBarBg;
.c-sidebar__pane {
flex-basis: 50%;
}
@@ -75,8 +75,10 @@
flex: 1 1 auto;
flex-direction: column;
width: 100%;
+
> * {
flex: 0 0 auto;
+
+ * {
margin-top: $interiorMargin;
}
@@ -111,18 +113,23 @@
flex: 1 1 auto;
}
+
+ &__page-locked,
&__drag-area {
- // TODO: recast this element to use c-drop-hint element
- background: rgba($colorKey, 0.1);
- border: 1px dashed rgba($colorKey, 0.7);
border-radius: $controlCr;
- color: rgba($colorBodyFg, 0.7);
padding: 10px;
- cursor: pointer;
&:before {
margin-right: 7px !important;
}
+ }
+
+ &__drag-area {
+ background: rgba($colorKey, 0.1);
+ border: 1px dashed rgba($colorKey, 0.7);
+ color: $colorKey;
+ cursor: pointer;
+ justify-content: center;
[class*="__label"] {
font-style: italic;
@@ -131,7 +138,7 @@
&:hover {
background: rgba($colorKey, 0.2);
- color: $colorBodyFg;
+ //color: $colorBodyFg;
}
&.drag-active,
@@ -151,6 +158,7 @@
display: flex;
flex-wrap: wrap; // Allows wrapping in mobile portrait and narrow placements
line-height: 220%;
+
> * {
flex: 0 0 auto;
}
@@ -162,9 +170,11 @@
overflow: hidden;
white-space: nowrap;
font-size: $headerFontSize;
+
> * {
// Section
flex: 0 0 auto;
+
+ * {
// Page
display: inline;
@@ -188,6 +198,13 @@
[class*="__entry"] + [class*="__entry"] {
margin-top: $interiorMarginSm;
}
+
+ .commit-button {
+ @include cButton();
+ position: absolute;
+ right: 5px;
+ bottom: 5px;
+ }
}
/***** SEARCH RESULTS */
@@ -218,6 +235,30 @@
}
}
}
+
+ /***** RESTRICTED NOTEBOOK */
+ &__page-locked {
+ background: rgba($colorAlert, 0.2);
+ display: flex;
+ padding: 5px;
+
+ > * + * {
+ margin-left: $interiorMargin;
+ }
+
+ [class*='icon'] {
+ flex: 0 0 auto;
+ }
+
+ [class*='__message'] {
+ flex: 1 1 auto;
+ }
+ }
+
+ &__commit-entries-control {
+ display: flex;
+ justify-content: flex-end;
+ }
}
.is-notebook-default,
@@ -243,40 +284,53 @@
display: flex;
padding: $interiorMarginSm $interiorMarginSm $interiorMarginSm $interiorMargin;
- &__time,
&__text,
&__local-controls {
padding-top: $p;
padding-bottom: $p;
}
- &__time,
+ &__creator,
&__embed__time {
opacity: 0.7;
}
+ &__time-and-creator,
+ &__input {
+ padding: $p;
+ }
+
+ &__creator [class*='icon'] {
+ font-size: 0.95em;
+ }
+
&__time-and-content {
- display: flex;
+ display: block;
flex: 1 1 auto;
- flex-wrap: wrap;
+
+ > * + * {
+ margin-top: $interiorMarginSm;
+ }
+
+ [class*='created-'] {
+ color: pullForward($colorBodyFg, 20%);
+ }
}
&__time {
- flex: 0 1 auto;
- min-width: 130px;
- margin-right: $interiorMarginLg;
-
* {
white-space: nowrap;
}
}
&__content {
+ display: flex;
+ flex-direction: column;
flex: 1 1 auto;
- > [class*="__"] + [class*="__"] {
+ > [class*="__"] + [class*="__"] {
margin-top: $interiorMarginSm;
- }
+ }
}
&__text {
@@ -294,11 +348,11 @@
&__input {
// Appended to __text element when Notebook is not in readOnly mode
@include inlineInput;
- padding-left: $inputTextPLeftRight;
- padding-right: $inputTextPLeftRight;
+ padding-left: $p;
+ padding-right: $p;
@include hover {
- &:not(:focus) {
+ &:not(:focus, .locked) {
background: rgba($colorBodyFg, 0.1);
}
}
@@ -399,6 +453,7 @@
@include snapThumb();
}
}
+
/****************************** SNAPSHOTTING */
// LEGACY: TODO: refactor these names
.t-contents,
@@ -413,7 +468,10 @@
color: $colorBodyFg;
padding: $interiorMarginSm !important; // Prevents items from going right to the edge of the image
- .l-sticky-headers .l-tabular-body { overflow: auto; }
+ .l-sticky-headers .l-tabular-body {
+ overflow: auto;
+ }
+
.l-browse-bar {
display: none; // Suppress browse-bar when snapshotting from view-large overlay
+ * {
@@ -457,6 +515,7 @@
> * {
flex: 1 1 auto;
+
&:first-child {
flex: 0 0 auto;
}
@@ -494,13 +553,19 @@
display: flex;
flex-direction: column;
position: absolute;
- top: $m; right: 0; bottom: $m; left: 0; // LEGACY, deal with .editor border-radius clipping stuff
+ top: $m;
+ right: 0;
+ bottom: $m;
+ left: 0; // LEGACY, deal with .editor border-radius clipping stuff
}
#snap-annotation-wrapper,
#snap-annotation-bar {
position: relative;
- top: auto; right: auto; bottom: auto; left: auto;
+ top: auto;
+ right: auto;
+ bottom: auto;
+ left: auto;
}
#snap-annotation-wrapper {
@@ -528,7 +593,9 @@
> div {
display: contents;
- > * + * { margin-left: $interiorMargin !important; }
+ > * + * {
+ margin-left: $interiorMargin !important;
+ }
}
.ptro-tool-controls {
@@ -600,7 +667,7 @@
}
.ptro-color-active-control {
- background: $colorBtnMajorBg !important;
+ background: $colorBtnMajorBg !important;
color: $colorBtnMajorFg !important;
}
@@ -618,7 +685,10 @@
/****************************** MOBILE */
body.mobile {
- .c-notebook__drag-area { display: none; }
+ .c-notebook__drag-area {
+ display: none;
+ }
+
.c-notebook__entry {
[class*="local-controls"] {
display: none;
@@ -651,3 +721,26 @@ body.mobile {
$c: $colorOk;
@include pulseProp($animName: flashSnapshot, $dur: 500ms, $iter: infinite, $prop: background, $valStart: rgba($c, 0.4), $valEnd: rgba($c, 0));
}
+
+/****************************** RESTRICTED NOTEBOOK / SHIFT LOG */
+.c-notebook--restricted {
+ .c-notebook__pages {
+ .c-list__item {
+ // Can display lock icon when a page is committed.
+ &:before {
+ $s: 0.8em;
+ color: $colorAlert;
+ display: block;
+ font-size: $s;
+ width: $s;
+ margin-right: $interiorMarginSm;
+ }
+
+ &:not([class*='lock']) {
+ &:before {
+ content: '';
+ }
+ }
+ }
+ }
+}
diff --git a/src/styles/plotly.scss b/src/styles/plotly.scss
index 073bb2d40..04d037ca5 100644
--- a/src/styles/plotly.scss
+++ b/src/styles/plotly.scss
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/styles/vue-styles.scss b/src/styles/vue-styles.scss
index 3521ff4d0..bf172cef1 100644
--- a/src/styles/vue-styles.scss
+++ b/src/styles/vue-styles.scss
@@ -15,7 +15,6 @@
@import "../plugins/flexibleLayout/components/flexible-layout.scss";
@import "../plugins/folderView/components/grid-view.scss";
@import "../plugins/folderView/components/list-item.scss";
-@import "../plugins/folderView/components/list-view.scss";
@import "../plugins/imagery/components/imagery-view.scss";
@import "../plugins/imagery/components/Compass/compass.scss";
@import "../plugins/telemetryTable/components/table-row.scss";
@@ -28,6 +27,7 @@
@import "../plugins/timeConductor/conductor-mode-icon.scss";
@import "../plugins/timeConductor/date-picker.scss";
@import "../plugins/timeline/timeline.scss";
+@import "../plugins/timelist/timelist.scss";
@import "../plugins/plan/plan";
@import "../plugins/viewDatumAction/components/metadata-list.scss";
@import "../ui/components/object-frame.scss";
@@ -35,8 +35,10 @@
@import "../ui/components/progress-bar.scss";
@import "../ui/components/search.scss";
@import "../ui/components/swim-lane/swimlane.scss";
+@import "../ui/components/tags/tags.scss";
@import "../ui/components/toggle-switch.scss";
@import "../ui/components/timesystem-axis.scss";
+@import "../ui/components/List/list-view.scss";
@import "../ui/inspector/elements.scss";
@import "../ui/inspector/inspector.scss";
@import "../ui/inspector/location.scss";
@@ -44,7 +46,7 @@
@import "../ui/layout/create-button.scss";
@import "../ui/layout/layout.scss";
@import "../ui/layout/mct-tree.scss";
-@import "../ui/layout/mct-search.scss";
+@import "../ui/layout/search/search.scss";
@import "../ui/layout/pane.scss";
@import "../ui/layout/status-bar/indicators.scss";
@import "../ui/layout/status-bar/notification-banner.scss";
@@ -52,6 +54,9 @@
@import "../ui/toolbar/components/toolbar-checkbox.scss";
@import "./notebook.scss";
@import "../plugins/notebook/components/sidebar.scss";
+@import "../plugins/gauge/gauge.scss";
+@import "../plugins/faultManagement/fault-manager.scss";
+@import "../plugins/operatorStatus/operator-status";
#splash-screen {
display: none;
diff --git a/src/tools/url.js b/src/tools/url.js
index fdd704375..add2573e3 100644
--- a/src/tools/url.js
+++ b/src/tools/url.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -25,7 +25,7 @@
*/
export function paramsToArray(openmct) {
- // parse urParams from an object to an array.
+ // parse urlParams from an object to an array.
let urlParams = openmct.router.getParams();
let newTabParams = [];
for (let key in urlParams) {
diff --git a/src/tools/urlSpec.js b/src/tools/urlSpec.js
new file mode 100644
index 000000000..36a31324d
--- /dev/null
+++ b/src/tools/urlSpec.js
@@ -0,0 +1,70 @@
+import { createOpenMct, resetApplicationState } from "../utils/testing";
+import {paramsToArray, identifierToString, default as objectPathToUrl} from "./url";
+
+describe('the url tool', function () {
+ let openmct;
+ let mockObjectPath;
+
+ beforeEach((done) => {
+ mockObjectPath = [
+ {
+ name: 'mock folder',
+ type: 'fake-folder',
+ identifier: {
+ key: 'mock-folder',
+ namespace: ''
+ }
+ },
+ {
+ name: 'mock parent folder',
+ type: 'fake-folder',
+ identifier: {
+ key: 'mock-parent-folder',
+ namespace: ''
+ }
+ }
+ ];
+ openmct = createOpenMct();
+ openmct.on('start', () => {
+ openmct.router.setPath(' http://localhost:8020/foobar?testParam1=testValue1');
+ done();
+ });
+ openmct.startHeadless();
+ });
+
+ afterEach(() => {
+ return resetApplicationState(openmct);
+ });
+
+ describe('paramsToArray', () => {
+ it('exists', () => {
+ expect(paramsToArray).toBeDefined();
+ });
+ it('can construct an array properly from query parameters', () => {
+ const arrayOfParams = paramsToArray(openmct);
+ expect(arrayOfParams.length).toBeDefined();
+ expect(arrayOfParams.length).toBeGreaterThan(0);
+ });
+ });
+
+ describe('identifierToString', () => {
+ it('exists', () => {
+ expect(identifierToString).toBeDefined();
+ });
+ it('can construct a String properly from a path', () => {
+ const constructedString = identifierToString(openmct, mockObjectPath);
+ expect(constructedString).toEqual('#/browse/mock-parent-folder/mock-folder');
+ });
+
+ });
+
+ describe('objectPathToUrl', () => {
+ it('exists', () => {
+ expect(objectPathToUrl).toBeDefined();
+ });
+ it('can construct URL properly from a path', () => {
+ const constructedURL = objectPathToUrl(openmct, mockObjectPath);
+ expect(constructedURL).toContain('#/browse/mock-parent-folder/mock-folder');
+ });
+ });
+});
diff --git a/src/ui/color/Color.js b/src/ui/color/Color.js
index 378ae5295..de7488e9d 100644
--- a/src/ui/color/Color.js
+++ b/src/ui/color/Color.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/ui/color/ColorHelper.js b/src/ui/color/ColorHelper.js
index b92158747..87980cc03 100644
--- a/src/ui/color/ColorHelper.js
+++ b/src/ui/color/ColorHelper.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/ui/color/ColorPalette.js b/src/ui/color/ColorPalette.js
index 7463a4678..e8a92a103 100644
--- a/src/ui/color/ColorPalette.js
+++ b/src/ui/color/ColorPalette.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/ui/color/ColorSwatch.vue b/src/ui/color/ColorSwatch.vue
index 953b4abbb..e6de34c6c 100644
--- a/src/ui/color/ColorSwatch.vue
+++ b/src/ui/color/ColorSwatch.vue
@@ -1,5 +1,5 @@
<!--
- Open MCT, Copyright (c) 2014-2020, United States Government
+ Open MCT, Copyright (c) 2014-2022, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
@@ -20,58 +20,62 @@
at runtime from the About dialog for additional information.
-->
<template>
-<div class="u-contents">
- <div v-if="canEdit"
- class="grid-row"
- >
- <div class="grid-cell label"
- :title="editTitle"
+<div class="grid-row grid-row--pad-label-for-button">
+ <template v-if="canEdit">
+ <div
+ class="grid-cell label"
+ :title="editTitle"
>{{ shortLabel }}</div>
<div class="grid-cell value">
- <div class="c-click-swatch c-click-swatch--menu"
- @click="toggleSwatch()"
+ <div
+ class="c-click-swatch c-click-swatch--menu"
+ @click="toggleSwatch()"
>
- <span class="c-color-swatch"
- :style="{ background: currentColor }"
+ <span
+ class="c-color-swatch"
+ :style="{ background: currentColor }"
>
</span>
</div>
<div class="c-palette c-palette--color">
- <div v-show="swatchActive"
- class="c-palette__items"
+ <div
+ v-show="swatchActive"
+ class="c-palette__items"
>
- <div v-for="group in colorPaletteGroups"
- :key="group.id"
- class="u-contents"
+ <div
+ v-for="group in colorPaletteGroups"
+ :key="group.id"
+ class="u-contents"
>
- <div v-for="color in group"
- :key="color.id"
- class="c-palette__item"
- :class="{ 'selected': currentColor === color.hexString }"
- :style="{ background: color.hexString }"
- @click="setColor(color)"
+ <div
+ v-for="color in group"
+ :key="color.id"
+ class="c-palette__item"
+ :class="{ 'selected': currentColor === color.hexString }"
+ :style="{ background: color.hexString }"
+ @click="setColor(color)"
>
</div>
</div>
</div>
</div>
</div>
- </div>
- <div v-else
- class="grid-row"
- >
- <div class="grid-cell label"
- :title="viewTitle"
+ </template>
+ <template v-else>
+ <div
+ class="grid-cell label"
+ :title="viewTitle"
>{{ shortLabel }}</div>
<div class="grid-cell value">
- <span class="c-color-swatch"
- :style="{
- 'background': currentColor
- }"
+ <span
+ class="c-color-swatch"
+ :style="{
+ 'background': currentColor
+ }"
>
</span>
</div>
- </div>
+ </template>
</div>
</template>
diff --git a/src/ui/components/List/ListHeader.vue b/src/ui/components/List/ListHeader.vue
new file mode 100644
index 000000000..99f4192bd
--- /dev/null
+++ b/src/ui/components/List/ListHeader.vue
@@ -0,0 +1,56 @@
+<template>
+<th
+ v-if="isSortable"
+ class="is-sortable"
+ :class="{
+ 'is-sorting': currentSort === property,
+ 'asc': direction,
+ 'desc': !direction
+ }"
+ @click="sort(property, direction)"
+>
+ {{ title }}
+</th>
+<th v-else>
+ {{ title }}
+</th>
+</template>
+
+<script>
+
+export default {
+ inject: ['openmct'],
+ props: {
+ property: {
+ type: String,
+ required: true
+ },
+ currentSort: {
+ type: String,
+ required: true
+ },
+ title: {
+ type: String,
+ required: true
+ },
+ direction: {
+ type: Boolean,
+ required: true
+ },
+ isSortable: {
+ type: Boolean,
+ default() {
+ return false;
+ }
+ }
+ },
+ methods: {
+ sort(property, direction) {
+ this.$emit('sort', {
+ property,
+ direction
+ });
+ }
+ }
+};
+</script>
diff --git a/src/ui/components/List/ListItem.vue b/src/ui/components/List/ListItem.vue
new file mode 100644
index 000000000..adabd4db3
--- /dev/null
+++ b/src/ui/components/List/ListItem.vue
@@ -0,0 +1,53 @@
+<template>
+<tr
+ class="c-list-item js-list-item"
+ :class="item.cssClass || ''"
+>
+ <td
+ v-for="itemValue in formattedItemValues"
+ :key="itemValue.key"
+ class="c-list-item__value js-list-item__value"
+ :class="['--' + itemValue.key]"
+ :title="itemValue.text"
+ >
+ {{ itemValue.text }}
+ </td>
+</tr>
+</template>
+
+<script>
+import _ from 'lodash';
+
+export default {
+ inject: ['openmct'],
+ props: {
+ item: {
+ type: Object,
+ required: true
+ },
+ itemProperties: {
+ type: Array,
+ required: true
+ }
+ },
+ computed: {
+ formattedItemValues() {
+ let values = [];
+ this.itemProperties.forEach((property) => {
+ // eslint-disable-next-line you-dont-need-lodash-underscore/get
+ let value = _.get(this.item, property.key);
+ if (property.format) {
+ value = property.format(value, this.item, property.key);
+ }
+
+ values.push({
+ text: value,
+ key: property.key
+ });
+ });
+
+ return values;
+ }
+ }
+};
+</script>
diff --git a/src/ui/components/List/ListView.vue b/src/ui/components/List/ListView.vue
new file mode 100644
index 000000000..08bac410d
--- /dev/null
+++ b/src/ui/components/List/ListView.vue
@@ -0,0 +1,142 @@
+<template>
+<div class="c-table c-table--sortable c-list-view c-list-view--sticky-header">
+ <table class="c-table__body js-table__body">
+ <thead class="c-table__header">
+ <tr>
+ <list-header
+ v-for="headerItem in headerItems"
+ :key="headerItem.property"
+ :direction="sortBy === headerItem.property ? ascending : headerItem.defaultDirection"
+ :is-sortable="headerItem.isSortable"
+ :title="headerItem.name"
+ :property="headerItem.property"
+ :current-sort="sortBy"
+ @sort="sort"
+ />
+ </tr>
+ </thead>
+ <tbody>
+ <list-item
+ v-for="item in sortedItems"
+ :key="item.key"
+ :item="item"
+ :item-properties="itemProperties"
+ />
+ </tbody>
+ </table>
+</div>
+</template>
+
+<script>
+import ListItem from './ListItem.vue';
+import ListHeader from './ListHeader.vue';
+import _ from 'lodash';
+
+export default {
+ components: {
+ ListItem,
+ ListHeader
+ },
+ inject: ['domainObject', 'openmct'],
+ props: {
+ headerItems: {
+ type: Array,
+ required: true
+ },
+ items: {
+ type: Array,
+ required: true
+ },
+ defaultSort: {
+ type: Object,
+ default() {
+ return {
+ property: '',
+ defaultDirection: true
+ };
+ }
+ },
+ storageKey: {
+ type: String,
+ default() {
+ return undefined;
+ }
+ }
+ },
+ data() {
+ let sortBy = this.defaultSort.property;
+ let ascending = this.defaultSort.defaultDirection;
+ if (this.storageKey) {
+ let persistedSortOrder = window.localStorage.getItem(this.storageKey);
+
+ if (persistedSortOrder) {
+ let parsed = JSON.parse(persistedSortOrder);
+
+ sortBy = parsed.sortBy;
+ ascending = parsed.ascending;
+ }
+ }
+
+ return {
+ sortBy,
+ ascending
+ };
+ },
+ computed: {
+ sortedItems() {
+ let sortedItems = _.sortBy(this.items, this.sortBy);
+ if (!this.ascending) {
+ sortedItems = sortedItems.reverse();
+ }
+
+ return sortedItems;
+ },
+ itemProperties() {
+ return this.headerItems.map((headerItem) => {
+ return {
+ key: headerItem.property,
+ format: headerItem.format
+ };
+ });
+ }
+ },
+ watch: {
+ defaultSort: {
+ handler() {
+ this.setSort();
+ },
+ deep: true
+ }
+ },
+ methods: {
+ setSort() {
+ this.sortBy = this.defaultSort.property;
+ this.ascending = this.defaultSort.defaultDirection;
+ },
+ sort(data) {
+ const property = data.property;
+ const direction = data.direction;
+
+ if (this.sortBy === property) {
+ this.ascending = !this.ascending;
+ } else {
+ this.sortBy = property;
+ this.ascending = direction;
+ }
+
+ if (this.storageKey) {
+ window.localStorage
+ .setItem(
+ this.storageKey,
+ JSON.stringify(
+ {
+ sortBy: this.sortBy,
+ ascending: this.ascending
+ }
+ )
+ );
+ }
+ }
+ }
+};
+</script>
diff --git a/src/ui/components/List/list-view.scss b/src/ui/components/List/list-view.scss
new file mode 100644
index 000000000..ab1484814
--- /dev/null
+++ b/src/ui/components/List/list-view.scss
@@ -0,0 +1,40 @@
+/******************************* LIST VIEW */
+.c-list-view {
+ tbody tr {
+ background: $colorListItemBg;
+ transition: $transOut;
+ }
+
+ td {
+ $p: floor($interiorMargin * 1.5);
+ @include ellipsize();
+ line-height: 120%; // Needed for icon alignment
+ max-width: 0;
+ padding-top: $p;
+ padding-bottom: $p;
+ width: 25%;
+ }
+
+ &--selectable {
+ body.desktop & {
+ tbody tr {
+ cursor: pointer;
+
+ &:hover {
+ background: $colorListItemBgHov;
+ filter: $filterHov;
+ transition: $transIn;
+ }
+ }
+ }
+ }
+
+ &--sticky-header {
+ thead tr {
+ position: -webkit-sticky;
+ position: sticky;
+ top: 0;
+ z-index: 2;
+ }
+ }
+}
diff --git a/src/ui/components/ObjectFrame.vue b/src/ui/components/ObjectFrame.vue
index 90c824cf3..694ba2786 100644
--- a/src/ui/components/ObjectFrame.vue
+++ b/src/ui/components/ObjectFrame.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -21,9 +21,11 @@
*****************************************************************************/
<template>
<div
+ ref="soView"
class="c-so-view js-notebook-snapshot-item-wrapper"
:class="[
statusClass,
+ widthClass,
'c-so-view--' + domainObject.type,
{
'c-so-view--no-frame': !hasFrame,
@@ -34,14 +36,17 @@
<div
class="c-so-view__header"
>
- <div class="c-object-label"
- :class="[ statusClass ]"
+ <div
+ class="c-object-label"
+ :class="[ statusClass ]"
>
- <div class="c-object-label__type-icon"
- :class="cssClass"
+ <div
+ class="c-object-label__type-icon"
+ :class="cssClass"
>
- <span class="is-status__indicator"
- :title="`This item is ${status}`"
+ <span
+ class="is-status__indicator"
+ :title="`This item is ${status}`"
></span>
</div>
<div class="c-object-label__name">
@@ -56,13 +61,15 @@
'has-complex-content': complexContent
}"
>
- <NotebookMenuSwitcher v-if="notebookEnabled"
- :domain-object="domainObject"
- :object-path="objectPath"
- class="c-notebook-snapshot-menubutton"
+ <NotebookMenuSwitcher
+ v-if="notebookEnabled"
+ :domain-object="domainObject"
+ :object-path="objectPath"
+ class="c-notebook-snapshot-menubutton"
/>
- <div v-if="statusBarItems.length > 0"
- class="c-so-view__frame-controls__btns"
+ <div
+ v-if="statusBarItems.length > 0"
+ class="c-so-view__frame-controls__btns"
>
<button
v-for="(item, index) in statusBarItems"
@@ -106,6 +113,7 @@ const SIMPLE_CONTENT_TYPES = [
'hyperlink',
'conditionWidget'
];
+const CSS_WIDTH_LESS_STR = '--width-less-than-';
export default {
components: {
@@ -145,6 +153,7 @@ export default {
return {
cssClass,
+ widthClass: '',
complexContent,
notebookEnabled: this.openmct.types.get('notebook'),
statusBarItems: [],
@@ -163,6 +172,11 @@ export default {
if (provider) {
this.$refs.objectView.show(this.domainObject, provider.key, false, this.objectPath);
}
+
+ if (this.$refs.soView) {
+ this.soViewResizeObserver = new ResizeObserver(this.resizeSoView);
+ this.soViewResizeObserver.observe(this.$refs.soView);
+ }
},
beforeDestroy() {
this.removeStatusListener();
@@ -170,6 +184,10 @@ export default {
if (this.actionCollection) {
this.unlistenToActionCollection();
}
+
+ if (this.soViewResizeObserver) {
+ this.soViewResizeObserver.disconnect();
+ }
},
methods: {
getSelectionContext() {
@@ -202,6 +220,19 @@ export default {
},
setStatus(status) {
this.status = status;
+ },
+ resizeSoView() {
+ let cW = this.$refs.soView.offsetWidth;
+ let widths = [220, 600];
+ let wClass = '';
+
+ for (let width of widths) {
+ if (cW < width) {
+ wClass = wClass.concat(' ', CSS_WIDTH_LESS_STR, width);
+ }
+ }
+
+ this.widthClass = wClass.trimStart();
}
}
};
diff --git a/src/ui/components/ObjectLabel.vue b/src/ui/components/ObjectLabel.vue
index c721288f6..a6247eacd 100644
--- a/src/ui/components/ObjectLabel.vue
+++ b/src/ui/components/ObjectLabel.vue
@@ -10,8 +10,9 @@
class="c-tree__item__type-icon c-object-label__type-icon"
:class="typeClass"
>
- <span class="is-status__indicator"
- :title="`This item is ${status}`"
+ <span
+ class="is-status__indicator"
+ :title="`This item is ${status}`"
></span>
</div>
<div class="c-tree__item__name c-object-label__name">
@@ -41,6 +42,13 @@ export default {
navigateToPath: {
type: String,
default: undefined
+ },
+ readOnly: {
+ type: Boolean,
+ required: false,
+ default() {
+ return false;
+ }
}
},
data() {
diff --git a/src/ui/components/ObjectPath.vue b/src/ui/components/ObjectPath.vue
new file mode 100644
index 000000000..7e149ce37
--- /dev/null
+++ b/src/ui/components/ObjectPath.vue
@@ -0,0 +1,139 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+<template>
+<ul
+ v-if="originalPath.length"
+ class="c-location"
+>
+ <li
+ v-for="pathObject in orderedOriginalPath"
+ :key="pathObject.key"
+ class="c-location__item"
+ >
+ <object-label
+ :domain-object="pathObject.domainObject"
+ :object-path="pathObject.objectPath"
+ :read-only="readOnly"
+ />
+ </li>
+</ul>
+</template>
+
+<script>
+import ObjectLabel from './ObjectLabel.vue';
+
+export default {
+ components: {
+ ObjectLabel
+ },
+ inject: ['openmct'],
+ props: {
+ enableSelectionListening: {
+ type: Boolean,
+ required: false,
+ default() {
+ return true;
+ }
+ },
+ readOnly: {
+ type: Boolean,
+ required: false,
+ default() {
+ return false;
+ }
+ }
+ },
+ data() {
+ return {
+ domainObject: {},
+ originalPath: [],
+ keyString: ''
+ };
+ },
+ computed: {
+ orderedOriginalPath() {
+ return this.originalPath.slice().reverse();
+ }
+ },
+ mounted() {
+ if (this.enableSelectionListening) {
+ this.openmct.selection.on('change', this.updateSelection);
+ this.updateSelection(this.openmct.selection.get());
+ }
+ },
+ beforeDestroy() {
+ this.openmct.selection.off('change', this.updateSelection);
+ },
+ methods: {
+ setOriginalPath(path, skipSlice) {
+ let originalPath = path;
+
+ if (!skipSlice) {
+ originalPath = path.slice(1, -1);
+ }
+
+ this.originalPath = originalPath.map((domainObject, index, pathArray) => {
+ let key = this.openmct.objects.makeKeyString(domainObject.identifier);
+
+ return {
+ domainObject,
+ key,
+ objectPath: pathArray.slice(index)
+ };
+ });
+ },
+ clearData() {
+ this.domainObject = {};
+ this.originalPath = [];
+ this.keyString = '';
+ },
+ updateSelection(selection) {
+ if (!selection.length || !selection[0].length) {
+ this.clearData();
+
+ return;
+ }
+
+ this.domainObject = selection[0][0].context.item;
+ let parentObject = selection[0][1];
+
+ if (!this.domainObject && parentObject && parentObject.context.item) {
+ this.setOriginalPath([parentObject.context.item], true);
+ this.keyString = '';
+
+ return;
+ }
+
+ let keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
+
+ if (keyString && this.keyString !== keyString) {
+ this.keyString = keyString;
+ this.originalPath = [];
+
+ this.openmct.objects.getOriginalPath(this.keyString)
+ .then(this.setOriginalPath);
+ }
+ }
+ }
+};
+</script>
diff --git a/src/ui/components/ObjectView.vue b/src/ui/components/ObjectView.vue
index 38111dc39..2976f7dc7 100644
--- a/src/ui/components/ObjectView.vue
+++ b/src/ui/components/ObjectView.vue
@@ -1,15 +1,19 @@
<template>
<div>
- <div v-if="domainObject && domainObject.type === 'time-strip'"
- class="c-conductor-holder--compact l-shell__main-independent-time-conductor"
+ <div
+ v-if="supportsIndependentTime"
+ class="c-conductor-holder--compact l-shell__main-independent-time-conductor"
>
- <independent-time-conductor :domain-object="domainObject"
- @stateChanged="updateIndependentTimeState"
- @updated="saveTimeOptions"
+ <independent-time-conductor
+ :domain-object="domainObject"
+ @stateChanged="updateIndependentTimeState"
+ @updated="saveTimeOptions"
/>
</div>
- <div ref="objectViewWrapper"
- class="c-object-view"
+ <div
+ ref="objectViewWrapper"
+ class="c-object-view"
+ :class="objectTypeClass"
></div>
</div>
</template>
@@ -20,6 +24,13 @@ import StyleRuleManager from "@/plugins/condition/StyleRuleManager";
import {STYLE_CONSTANTS} from "@/plugins/condition/utils/constants";
import IndependentTimeConductor from '@/plugins/timeConductor/independent/IndependentTimeConductor.vue';
+const SupportedViewTypes = [
+ 'plot-stacked',
+ 'plot-overlay',
+ 'bar-graph.view',
+ 'scatter-plot.view',
+ 'time-strip.view'
+];
export default {
components: {
IndependentTimeConductor
@@ -64,6 +75,14 @@ export default {
},
font() {
return this.objectFontStyle ? this.objectFontStyle.font : this.layoutFont;
+ },
+ supportsIndependentTime() {
+ const viewKey = this.getViewKey();
+
+ return this.domainObject && SupportedViewTypes.includes(viewKey);
+ },
+ objectTypeClass() {
+ return this.domainObject && ('is-object-type-' + this.domainObject.type);
}
},
destroyed() {
@@ -426,4 +445,3 @@ export default {
}
};
</script>
-
diff --git a/src/ui/components/ProgressBar.vue b/src/ui/components/ProgressBar.vue
index 001849733..aaa2c911d 100644
--- a/src/ui/components/ProgressBar.vue
+++ b/src/ui/components/ProgressBar.vue
@@ -1,8 +1,9 @@
<template>
<div class="c-progress-bar">
- <div class="c-progress-bar__bar"
- :class="{ '--indeterminate': model.progressPerc === undefined }"
- :style="styleBarWidth"
+ <div
+ class="c-progress-bar__bar"
+ :class="{ '--indeterminate': model.progressPerc === undefined }"
+ :style="styleBarWidth"
></div>
<div
v-if="model.progressText !== undefined"
diff --git a/src/ui/components/SelectorDialogTree.vue b/src/ui/components/SelectorDialogTree.vue
deleted file mode 100644
index 9d24d0be4..000000000
--- a/src/ui/components/SelectorDialogTree.vue
+++ /dev/null
@@ -1,240 +0,0 @@
-/*****************************************************************************
-* Open MCT, Copyright (c) 2014-2021, United States Government
-* as represented by the Administrator of the National Aeronautics and Space
-* Administration. All rights reserved.
-*
-* Open MCT is licensed under the Apache License, Version 2.0 (the
-* "License"); you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-* http://www.apache.org/licenses/LICENSE-2.0.
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-* License for the specific language governing permissions and limitations
-* under the License.
-*
-* Open MCT includes source code licensed under additional open source
-* licenses. See the Open Source Licenses file (LICENSES.md) included with
-* this source code distribution or the Licensing information page available
-* at runtime from the About dialog for additional information.
-*****************************************************************************/
-
-<template>
-<div class="u-contents">
- <div v-if="title.length"
- class="c-overlay__top-bar"
- >
- <div class="c-overlay__dialog-title">{{ title }}</div>
- </div>
- <div class="c-selector c-tree-and-search"
- :class="cssClass"
- >
- <div class="c-tree-and-search__search">
- <Search ref="shell-search"
- class="c-search"
- :value="searchValue"
- @input="searchTree"
- @clear="searchTree"
- />
- </div>
-
- <div v-if="isLoading"
- class="c-tree-and-search__loading loading"
- ></div>
-
- <div v-if="shouldDisplayNoResultsText"
- class="c-tree-and-search__no-results"
- >
- No results found
- </div>
-
- <ul v-if="!isLoading"
- v-show="!searchValue"
- class="c-tree-and-search__tree c-tree"
- >
- <SelectorDialogTreeItem
- v-for="treeItem in allTreeItems"
- :key="treeItem.id"
- :node="treeItem"
- :selected-item="selectedItem"
- :handle-item-selected="handleItemSelection"
- :navigate-to-parent="navigateToParent"
- />
- </ul>
-
- <ul v-if="searchValue && !isLoading"
- class="c-tree-and-search__tree c-tree"
- >
- <SelectorDialogTreeItem
- v-for="treeItem in filteredTreeItems"
- :key="treeItem.id"
- :node="treeItem"
- :selected-item="selectedItem"
- :handle-item-selected="handleItemSelection"
- />
- </ul>
- </div>
-</div>
-</template>
-
-<script>
-import debounce from 'lodash/debounce';
-import Search from '@/ui/components/search.vue';
-import SelectorDialogTreeItem from './SelectorDialogTreeItem.vue';
-
-export default {
- name: 'SelectorDialogTree',
- components: {
- Search,
- SelectorDialogTreeItem
- },
- inject: ['openmct'],
- props: {
- cssClass: {
- type: String,
- required: false,
- default() {
- return '';
- }
- },
- title: {
- type: String,
- required: false,
- default() {
- return '';
- }
- },
- ignoreTypeCheck: {
- type: Boolean,
- required: false,
- default() {
- return false;
- }
- },
- parent: {
- type: Object,
- required: false,
- default() {
- return undefined;
- }
- }
- },
- data() {
- return {
- allTreeItems: [],
- expanded: false,
- filteredTreeItems: [],
- isLoading: false,
- navigateToParent: undefined,
- searchValue: '',
- selectedItem: undefined
- };
- },
- computed: {
- shouldDisplayNoResultsText() {
- if (this.isLoading) {
- return false;
- }
-
- return this.allTreeItems.length === 0
- || (this.searchValue && this.filteredTreeItems.length === 0);
- }
- },
- created() {
- this.getDebouncedFilteredChildren = debounce(this.getFilteredChildren, 400);
- },
- mounted() {
- if (this.parent) {
- (async () => {
- const objectPath = await this.openmct.objects.getOriginalPath(this.parent.identifier);
- this.navigateToParent = '/browse/'
- + objectPath
- .map(parent => this.openmct.objects.makeKeyString(parent.identifier))
- .reverse()
- .join('/');
-
- this.getAllChildren(this.navigateToParent);
- })();
- } else {
- this.getAllChildren();
- }
- },
- methods: {
- async aggregateFilteredChildren(results) {
- for (const object of results) {
- const objectPath = await this.openmct.objects.getOriginalPath(object.identifier);
-
- const navigateToParent = '/browse/'
- + objectPath.slice(1)
- .map(parent => this.openmct.objects.makeKeyString(parent.identifier))
- .join('/');
-
- const filteredChild = {
- id: this.openmct.objects.makeKeyString(object.identifier),
- object,
- objectPath,
- navigateToParent
- };
-
- this.filteredTreeItems.push(filteredChild);
- }
- },
- getAllChildren(navigateToParent) {
- this.isLoading = true;
- this.openmct.objects.get('ROOT')
- .then(root => {
- return this.openmct.composition.get(root).load();
- })
- .then(children => {
- this.isLoading = false;
- this.allTreeItems = children.map(c => {
- return {
- id: this.openmct.objects.makeKeyString(c.identifier),
- object: c,
- objectPath: [c],
- navigateToParent: navigateToParent || '/browse'
- };
- });
- });
- },
- getFilteredChildren() {
- // clear any previous search results
- this.filteredTreeItems = [];
-
- const promises = this.openmct.objects.search(this.searchValue)
- .map(promise => promise
- .then(results => this.aggregateFilteredChildren(results)));
-
- Promise.all(promises).then(() => {
- this.isLoading = false;
- });
- },
- handleItemSelection(item, node) {
- if (item && (this.ignoreTypeCheck || item.type === 'conditionSet')) {
- const parentId = (node.objectPath && node.objectPath.length > 1) ? node.objectPath[1].identifier : undefined;
- this.selectedItem = {
- itemId: item.identifier,
- parentId
- };
-
- this.$emit('treeItemSelected',
- {
- item,
- parentObjectPath: node.objectPath
- });
- }
- },
- searchTree(value) {
- this.searchValue = value;
- this.isLoading = true;
-
- if (this.searchValue !== '') {
- this.getDebouncedFilteredChildren();
- } else {
- this.isLoading = false;
- }
- }
- }
-};
-</script>
diff --git a/src/ui/components/SelectorDialogTreeItem.vue b/src/ui/components/SelectorDialogTreeItem.vue
deleted file mode 100644
index 52d65daaf..000000000
--- a/src/ui/components/SelectorDialogTreeItem.vue
+++ /dev/null
@@ -1,206 +0,0 @@
-/*****************************************************************************
-* Open MCT, Copyright (c) 2014-2021, United States Government
-* as represented by the Administrator of the National Aeronautics and Space
-* Administration. All rights reserved.
-*
-* Open MCT is licensed under the Apache License, Version 2.0 (the
-* "License"); you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-* http://www.apache.org/licenses/LICENSE-2.0.
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-* License for the specific language governing permissions and limitations
-* under the License.
-*
-* Open MCT includes source code licensed under additional open source
-* licenses. See the Open Source Licenses file (LICENSES.md) included with
-* this source code distribution or the Licensing information page available
-* at runtime from the About dialog for additional information.
-*****************************************************************************/
-
-<template>
-<li class="c-tree__item-h">
- <div
- class="c-tree__item"
- :class="{ 'is-alias': isAlias, 'is-navigated-object': navigated }"
- @click="handleItemSelected(node.object, node)"
- >
- <view-control
- v-model="expanded"
- class="c-tree__item__view-control"
- :enabled="hasChildren"
- />
- <div class="c-tree__item__label c-object-label">
- <div
- class="c-tree__item__type-icon c-object-label__type-icon"
- :class="typeClass"
- ></div>
- <div class="c-tree__item__name c-object-label__name">{{ node.object.name }}</div>
- </div>
- </div>
- <ul
- v-if="expanded && !isLoading"
- class="c-tree"
- >
- <li
- v-if="isLoading && !loaded"
- class="c-tree__item-h"
- >
- <div class="c-tree__item loading">
- <span class="c-tree__item__label">Loading...</span>
- </div>
- </li>
- <SelectorDialogTreeItem
- v-for="child in children"
- :key="child.id"
- :node="child"
- :selected-item="selectedItem"
- :handle-item-selected="handleItemSelected"
- :navigate-to-parent="navigateToParent"
- />
- </ul>
-</li>
-</template>
-
-<script>
-import viewControl from '@/ui/components/viewControl.vue';
-
-export default {
- name: 'SelectorDialogTreeItem',
- components: {
- viewControl
- },
- inject: ['openmct'],
- props: {
- node: {
- type: Object,
- required: true
- },
- selectedItem: {
- type: Object,
- default() {
- return undefined;
- }
- },
- handleItemSelected: {
- type: Function,
- default() {
- return (item) => {};
- }
- },
- navigateToParent: {
- type: String,
- default() {
- return undefined;
- }
- }
- },
- data() {
- return {
- hasChildren: false,
- isLoading: false,
- loaded: false,
- children: [],
- expanded: false
- };
- },
- computed: {
- navigated() {
- const itemId = this.selectedItem && this.selectedItem.itemId;
- const isSelectedObject = itemId && this.openmct.objects.areIdsEqual(this.node.object.identifier, itemId);
- if (isSelectedObject && this.node.objectPath && this.node.objectPath.length > 1) {
- const isParent = this.openmct.objects.areIdsEqual(this.node.objectPath[1].identifier, this.selectedItem.parentId);
-
- return isSelectedObject && isParent;
- }
-
- return isSelectedObject;
- },
- isAlias() {
- let parent = this.node.objectPath[1];
- if (!parent) {
- return false;
- }
-
- let parentKeyString = this.openmct.objects.makeKeyString(parent.identifier);
-
- return parentKeyString !== this.node.object.location;
- },
- typeClass() {
- let type = this.openmct.types.get(this.node.object.type);
- if (!type) {
- return 'icon-object-unknown';
- }
-
- return type.definition.cssClass;
- }
- },
- watch: {
- expanded() {
- if (!this.hasChildren) {
- return;
- }
-
- if (!this.loaded && !this.isLoading) {
- this.composition = this.openmct.composition.get(this.domainObject);
- this.composition.on('add', this.addChild);
- this.composition.on('remove', this.removeChild);
- this.composition.load().then(this.finishLoading);
- this.isLoading = true;
- }
- }
- },
- mounted() {
- this.domainObject = this.node.object;
-
- if (this.navigateToParent && this.navigateToParent.includes(this.openmct.objects.makeKeyString(this.domainObject.identifier))) {
- this.expanded = true;
- }
-
- if (this.navigateToParent && this.navigateToParent.endsWith(this.openmct.objects.makeKeyString(this.domainObject.identifier))) {
- this.handleItemSelected(this.node.object, this.node);
- }
-
- let removeListener = this.openmct.objects.observe(this.domainObject, '*', (newObject) => {
- this.domainObject = newObject;
- });
-
- this.$once('hook:destroyed', removeListener);
- if (this.openmct.composition.get(this.node.object)) {
- this.hasChildren = true;
- }
-
- },
- beforeDestroy() {
- this.expanded = false;
- },
- destroyed() {
- if (this.composition) {
- this.composition.off('add', this.addChild);
- this.composition.off('remove', this.removeChild);
- delete this.composition;
- }
- },
- methods: {
- addChild(child) {
- this.children.push({
- id: this.openmct.objects.makeKeyString(child.identifier),
- object: child,
- objectPath: [child].concat(this.node.objectPath),
- navigateToParent: this.navigateToPath
- });
- },
- removeChild(identifier) {
- let removeId = this.openmct.objects.makeKeyString(identifier);
- this.children = this.children
- .filter(c => c.id !== removeId);
- },
- finishLoading() {
- this.isLoading = false;
- this.loaded = true;
- }
- }
-};
-</script>
diff --git a/src/ui/components/TimeSystemAxis.vue b/src/ui/components/TimeSystemAxis.vue
index f2ad538c1..b8e48d521 100644
--- a/src/ui/components/TimeSystemAxis.vue
+++ b/src/ui/components/TimeSystemAxis.vue
@@ -1,6 +1,7 @@
<template>
-<div ref="axisHolder"
- class="c-timesystem-axis"
+<div
+ ref="axisHolder"
+ class="c-timesystem-axis"
>
<div class="nowMarker"><span class="icon-arrow-down"></span></div>
</div>
@@ -122,7 +123,7 @@ export default {
}
},
drawAxis(bounds, timeSystem) {
- let viewBounds = Object.assign({}, bounds);
+ let viewBounds = Object.create(bounds);
this.setScale(viewBounds, timeSystem);
this.setAxis(viewBounds);
diff --git a/platform/persistence/queue/src/PersistenceFailureConstants.js b/src/ui/components/components.js
index f3d35a503..3a45f1bc7 100644
--- a/platform/persistence/queue/src/PersistenceFailureConstants.js
+++ b/src/ui/components/components.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -20,9 +20,10 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
-define({
- REVISION_ERROR_KEY: "revision",
- OVERWRITE_KEY: "overwrite",
- TIMESTAMP_FORMAT: "YYYY-MM-DD HH:mm:ss\\Z",
- UNKNOWN_USER: "unknown user"
-});
+import ObjectView from './ObjectView.vue';
+import StackedPlot from '../../plugins/plot/stackedPlot/StackedPlot.vue';
+
+export default {
+ ObjectView,
+ StackedPlot
+};
diff --git a/src/plugins/nonEditableFolder/pluginSpec.js b/src/ui/components/componentsSpec.js
index bded713c8..be9a637f4 100644
--- a/src/plugins/nonEditableFolder/pluginSpec.js
+++ b/src/ui/components/componentsSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -19,32 +19,30 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
+
import {
createOpenMct,
resetApplicationState
} from 'utils/testing';
-describe("the plugin", () => {
- const NON_EDITABLE_FOLDER_KEY = 'noneditable.folder';
+describe('UI Components', () => {
let openmct;
- beforeEach((done) => {
+ beforeEach(done => {
openmct = createOpenMct();
- openmct.install(openmct.plugins.NonEditableFolder());
-
openmct.on('start', done);
openmct.startHeadless();
});
afterEach(() => {
- return resetApplicationState(openmct);
+ return resetApplicationState();
});
- it('adds the new non-editable folder type', () => {
- const type = openmct.types.get(NON_EDITABLE_FOLDER_KEY);
-
- expect(type).toBeDefined();
- expect(type.definition.creatable).toBeFalse();
+ it('are exposed to users', () => {
+ expect(openmct.components).toBeDefined();
});
+ it('exposes the object view', () => {
+ expect(openmct.components.ObjectView).toBeDefined();
+ });
});
diff --git a/src/ui/components/object-frame.scss b/src/ui/components/object-frame.scss
index 36b9188ff..5c65f095d 100644
--- a/src/ui/components/object-frame.scss
+++ b/src/ui/components/object-frame.scss
@@ -11,6 +11,7 @@
margin-bottom: $interiorMarginSm;
overflow: hidden;
padding: 3px;
+ @include smallerControlButtons; // Make button in frame headers a bit smaller
.c-object-label {
font-size: 1.05em;
@@ -43,11 +44,11 @@
flex: 0 0 auto;
}
- .is-in-small-container &,
- .c-fl-frame & {
+ .--width-less-than-220 &,
+ .--width-less-than-600 & {
[class*="__label"] {
// button labels
- display: none;
+ display: none !important;
}
}
@@ -122,6 +123,9 @@
flex: 1 1 auto;
height: 0; // Chrome 73 overflow bug fix
overflow: auto;
+ //To accommodate independent time conductor controls
+ display: flex;
+ flex-direction: column;
.u-fills-container {
// Expand component types that fill a container
@@ -129,8 +133,6 @@
}
}
- @include smallerControlButtons;
-
&.has-complex-content {
> .c-so-view__view-large { display: block; }
}
@@ -138,6 +140,10 @@
&.is-status--missing {
border: $borderMissing;
}
+
+ // Leave for debugging
+ //&.--width-less-than-600 { background: rgba(orange, 0.2) !important; }
+ //&.--width-less-than-220 { background: rgba(red, 0.2) !important; }
}
.l-angular-ov-wrapper {
@@ -146,3 +152,5 @@
display: block;
height: 100%;
}
+
+
diff --git a/src/ui/components/object-label.scss b/src/ui/components/object-label.scss
index 3128eaba3..174b02277 100644
--- a/src/ui/components/object-label.scss
+++ b/src/ui/components/object-label.scss
@@ -2,7 +2,7 @@
// <a> tag and draggable element that holds type icon and name.
// Used mostly in trees and lists
display: flex;
- align-items: baseline; // Provides better vertical alignment than center
+ align-items: center;
flex: 0 1 auto;
overflow: hidden;
white-space: nowrap;
diff --git a/src/ui/components/search.vue b/src/ui/components/search.vue
index 46d069bff..57ea2d141 100644
--- a/src/ui/components/search.vue
+++ b/src/ui/components/search.vue
@@ -5,6 +5,7 @@
>
<input
class="c-search__input"
+ aria-label="Search Input"
tabindex="10000"
type="search"
v-bind="$attrs"
diff --git a/src/ui/components/swim-lane/SwimLane.vue b/src/ui/components/swim-lane/SwimLane.vue
index f2ea344e1..7b10e3820 100644
--- a/src/ui/components/swim-lane/SwimLane.vue
+++ b/src/ui/components/swim-lane/SwimLane.vue
@@ -1,23 +1,27 @@
<template>
-<div class="u-contents"
- :class="[
- {'c-swimlane': !isNested},
- statusClass
- ]"
+<div
+ class="u-contents"
+ :class="[
+ {'c-swimlane': !isNested},
+ statusClass
+ ]"
>
- <div v-if="hideLabel === false"
- class="c-swimlane__lane-label c-object-label"
- :class="[swimlaneClass, statusClass]"
- :style="gridRowSpan"
+ <div
+ v-if="hideLabel === false"
+ class="c-swimlane__lane-label c-object-label"
+ :class="[swimlaneClass, statusClass]"
+ :style="gridRowSpan"
>
- <div v-if="iconClass"
- class="c-object-label__type-icon"
- :class="iconClass"
+ <div
+ v-if="iconClass"
+ class="c-object-label__type-icon"
+ :class="iconClass"
>
- <span v-if="status"
- class="is-status__indicator"
- :title="`This item is ${status}`"
+ <span
+ v-if="status"
+ class="is-status__indicator"
+ :title="`This item is ${status}`"
></span>
</div>
@@ -26,9 +30,10 @@
</div>
</div>
- <div class="c-swimlane__lane-object"
- :style="{'min-height': minHeight}"
- :class="{'u-contents': showUcontents}"
+ <div
+ class="c-swimlane__lane-object"
+ :style="{'min-height': minHeight}"
+ :class="{'u-contents': showUcontents}"
>
<slot name="object"></slot>
</div>
diff --git a/src/ui/components/swim-lane/swimlane.scss b/src/ui/components/swim-lane/swimlane.scss
index a884ece87..150963794 100644
--- a/src/ui/components/swim-lane/swimlane.scss
+++ b/src/ui/components/swim-lane/swimlane.scss
@@ -29,4 +29,7 @@
@include smallerControlButtons;
}
+
+ // Yet more brittle special case selecting...
+ .is-object-type-plan { display: contents; }
}
diff --git a/src/ui/components/tags/TagEditor.vue b/src/ui/components/tags/TagEditor.vue
new file mode 100644
index 000000000..4f581d996
--- /dev/null
+++ b/src/ui/components/tags/TagEditor.vue
@@ -0,0 +1,152 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+<template>
+<div class="c-tag-applier">
+ <TagSelection
+ v-for="(addedTag, index) in addedTags"
+ :key="index"
+ :selected-tag="addedTag.newTag ? null : addedTag"
+ :new-tag="addedTag.newTag"
+ :added-tags="addedTags"
+ @tagRemoved="tagRemoved"
+ @tagAdded="tagAdded"
+ />
+ <button
+ v-show="!userAddingTag && !maxTagsAdded"
+ class="c-tag-applier__add-btn c-icon-button c-icon-button--major icon-plus"
+ title="Add new tag"
+ @click="addTag"
+ >
+ <div class="c-icon-button__label">Add Tag</div>
+ </button>
+</div>
+</template>
+
+<script>
+import TagSelection from './TagSelection.vue';
+
+export default {
+ components: {
+ TagSelection
+ },
+ inject: ['openmct'],
+ props: {
+ annotationQuery: {
+ type: Object,
+ required: true
+ },
+ annotationType: {
+ type: String,
+ required: true
+ },
+ annotationSearchType: {
+ type: String,
+ required: true
+ },
+ targetSpecificDetails: {
+ type: Object,
+ required: true
+ },
+ domainObject: {
+ type: Object,
+ default() {
+ return null;
+ }
+ }
+ },
+ data() {
+ return {
+ annontation: null,
+ addedTags: [],
+ userAddingTag: false
+ };
+ },
+ computed: {
+ availableTags() {
+ return this.openmct.annotation.getAvailableTags();
+ },
+ maxTagsAdded() {
+ const availableTags = this.openmct.annotation.getAvailableTags();
+
+ return !(availableTags && availableTags.length && (this.addedTags.length < availableTags.length));
+ }
+ },
+ watch: {
+ annotation: {
+ handler() {
+ this.tagsChanged(this.annotation.tags);
+ },
+ deep: true
+ }
+ },
+ async mounted() {
+ this.annotation = await this.openmct.annotation.getAnnotation(this.annotationQuery, this.annotationSearchType);
+ this.addAnnotationListener(this.annotation);
+ if (this.annotation && this.annotation.tags) {
+ this.tagsChanged(this.annotation.tags);
+ }
+ },
+ destroyed() {
+ if (this.removeTagsListener) {
+ this.removeTagsListener();
+ }
+ },
+ methods: {
+ addAnnotationListener(annotation) {
+ if (annotation && !this.removeTagsListener) {
+ this.removeTagsListener = this.openmct.objects.observe(annotation, 'tags', this.tagsChanged);
+ }
+ },
+ tagsChanged(newTags) {
+ if (newTags.length < this.addedTags.length) {
+ this.addedTags = this.addedTags.slice(0, newTags.length);
+ }
+
+ for (let index = 0; index < newTags.length; index += 1) {
+ this.$set(this.addedTags, index, newTags[index]);
+ }
+ },
+ addTag() {
+ const newTagValue = {
+ newTag: true
+ };
+ this.addedTags.push(newTagValue);
+ this.userAddingTag = true;
+ },
+ tagRemoved(tagToRemove) {
+ return this.openmct.annotation.removeAnnotationTag(this.annotation, tagToRemove);
+ },
+ async tagAdded(newTag) {
+ const annotationWasCreated = this.annotation === null || this.annotation === undefined;
+ this.annotation = await this.openmct.annotation.addAnnotationTag(this.annotation,
+ this.domainObject, this.targetSpecificDetails, this.annotationType, newTag);
+ if (annotationWasCreated) {
+ this.addAnnotationListener(this.annotation);
+ }
+
+ this.tagsChanged(this.annotation.tags);
+ this.userAddingTag = false;
+ }
+ }
+};
+</script>
diff --git a/src/ui/components/tags/TagSelection.vue b/src/ui/components/tags/TagSelection.vue
new file mode 100644
index 000000000..3163ae045
--- /dev/null
+++ b/src/ui/components/tags/TagSelection.vue
@@ -0,0 +1,152 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+<template>
+<div class="c-tag__parent">
+ <div class="c-tag_selection">
+ <AutoCompleteField
+ v-if="newTag"
+ ref="tagSelection"
+ :model="availableTagModel"
+ :place-holder-text="'Type to select tag'"
+ class="c-tag-selection"
+ :item-css-class="'icon-circle'"
+ @onChange="tagSelected"
+ />
+ <div
+ v-else
+ class="c-tag"
+ :style="{ background: selectedBackgroundColor, color: selectedForegroundColor }"
+ >
+ <div class="c-tag__label">{{ selectedTagLabel }} </div>
+ <button
+ class="c-completed-tag-deletion c-tag__remove-btn icon-x-in-circle"
+ @click="removeTag"
+ ></button>
+ </div>
+ </div>
+</div>
+</template>
+
+<script>
+
+import AutoCompleteField from '../../../api/forms/components/controls/AutoCompleteField.vue';
+
+export default {
+ components: {
+ AutoCompleteField
+ },
+ inject: ['openmct'],
+ props: {
+ addedTags: {
+ type: Array,
+ default() {
+ return [];
+ }
+ },
+ selectedTag: {
+ type: String,
+ default() {
+ return "";
+ }
+ },
+ newTag: {
+ type: Boolean,
+ default() {
+ return false;
+ }
+ }
+ },
+ data() {
+ return {
+ };
+ },
+ computed: {
+ availableTagModel() {
+ const availableTags = this.openmct.annotation.getAvailableTags().filter(tag => {
+ return (!this.addedTags.includes(tag.id));
+ }).map(tag => {
+ return {
+ name: tag.label,
+ color: tag.backgroundColor,
+ id: tag.id
+ };
+ });
+
+ return {
+ options: availableTags
+ };
+ },
+ selectedBackgroundColor() {
+ const selectedTag = this.getAvailableTagByID(this.selectedTag);
+ if (selectedTag) {
+ return selectedTag.backgroundColor;
+ } else {
+ // missing available tag color, use default
+ return '#00000';
+ }
+ },
+ selectedForegroundColor() {
+ const selectedTag = this.getAvailableTagByID(this.selectedTag);
+ if (selectedTag) {
+ return selectedTag.foregroundColor;
+ } else {
+ // missing available tag color, use default
+ return '#FFFFF';
+ }
+ },
+ selectedTagLabel() {
+ const selectedTag = this.getAvailableTagByID(this.selectedTag);
+ if (selectedTag) {
+ return selectedTag.label;
+ } else {
+ // missing available tag color, use default
+ return '¡UNKNOWN!';
+ }
+ }
+ },
+ mounted() {
+ },
+ methods: {
+ getAvailableTagByID(tagID) {
+ return this.openmct.annotation.getAvailableTags().find(tag => {
+ return tag.id === tagID;
+ });
+ },
+ removeTag() {
+ this.$emit('tagRemoved', this.selectedTag);
+ },
+ tagSelected(autoField) {
+ const tagAdded = autoField.model.options.find(option => {
+ if (option.name === autoField.value) {
+ return true;
+ }
+
+ return false;
+ });
+ if (tagAdded) {
+ this.$emit('tagAdded', tagAdded.id);
+ }
+ }
+ }
+};
+</script>
diff --git a/src/ui/components/tags/tags.scss b/src/ui/components/tags/tags.scss
new file mode 100644
index 000000000..ebd3e7a18
--- /dev/null
+++ b/src/ui/components/tags/tags.scss
@@ -0,0 +1,67 @@
+/******************************* TAGS */
+.c-tag {
+ border-radius: 10px; //TODO: convert to theme constant
+ display: inline-flex;
+ padding: 1px 10px; //TODO: convert to theme constant
+
+ > * + * {
+ margin-left: $interiorMargin;
+ }
+
+ &__remove-btn {
+ color: inherit !important;
+ display: none;
+ opacity: 0;
+ overflow: hidden;
+ padding: 1px !important;
+ transition: $transIn;
+ width: 0;
+
+ &:hover {
+ opacity: 1;
+ }
+ }
+
+ /* SEARCH RESULTS */
+ &.--is-not-search-match {
+ opacity: 0.5;
+ }
+}
+
+/******************************* TAG EDITOR */
+.c-tag-applier {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ align-items: center;
+
+ > * + * {
+ margin-left: $interiorMargin;
+ }
+
+ &__add-btn {
+ &:before { font-size: 0.9em; }
+ }
+
+ .c-tag {
+ flex-direction: row;
+ align-items: center;
+ padding-right: 3px !important;
+
+ &__remove-btn {
+ display: block;
+ }
+ }
+}
+
+/******************************* HOVERS */
+.has-tag-applier {
+ // Apply this class to all components that should trigger tag removal btn on hover
+ &:hover {
+ .c-tag__remove-btn {
+ width: 1.1em;
+ opacity: 0.7;
+ transition: $transOut;
+ }
+ }
+ }
diff --git a/src/ui/inspector/ElementItem.vue b/src/ui/inspector/ElementItem.vue
index 8cf6c3de5..9d7be7085 100644
--- a/src/ui/inspector/ElementItem.vue
+++ b/src/ui/inspector/ElementItem.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/ui/inspector/ElementsPool.vue b/src/ui/inspector/ElementsPool.vue
index 34b7f4305..e5ecfecbf 100644
--- a/src/ui/inspector/ElementsPool.vue
+++ b/src/ui/inspector/ElementsPool.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/ui/inspector/Inspector.vue b/src/ui/inspector/Inspector.vue
index 2720185ba..dab49de24 100644
--- a/src/ui/inspector/Inspector.vue
+++ b/src/ui/inspector/Inspector.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -23,22 +23,25 @@
<template>
<div class="c-inspector">
<object-name />
- <div v-if="showStyles"
- class="c-inspector__tabs c-tabs"
+ <div
+ v-if="showStyles"
+ class="c-inspector__tabs c-tabs"
>
- <div v-for="tabbedView in tabbedViews"
- :key="tabbedView.key"
- class="c-inspector__tab c-tab"
- :class="{'is-current': isCurrent(tabbedView)}"
- @click="updateCurrentTab(tabbedView)"
+ <div
+ v-for="tabbedView in tabbedViews"
+ :key="tabbedView.key"
+ class="c-inspector__tab c-tab"
+ :class="{'is-current': isCurrent(tabbedView)}"
+ @click="updateCurrentTab(tabbedView)"
>
{{ tabbedView.name }}
</div>
</div>
<div class="c-inspector__content">
- <multipane v-show="currentTabbedView.key === '__properties'"
- type="vertical"
+ <multipane
+ v-show="currentTabbedView.key === '__properties'"
+ type="vertical"
>
<pane class="c-inspector__properties">
<Properties
diff --git a/src/ui/inspector/InspectorDetailsSpec.js b/src/ui/inspector/InspectorDetailsSpec.js
index 57174261b..24553c5c4 100644
--- a/src/ui/inspector/InspectorDetailsSpec.js
+++ b/src/ui/inspector/InspectorDetailsSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/ui/inspector/InspectorStylesSpec.js b/src/ui/inspector/InspectorStylesSpec.js
index 9f22a1e53..bc67732af 100644
--- a/src/ui/inspector/InspectorStylesSpec.js
+++ b/src/ui/inspector/InspectorStylesSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2020, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/ui/inspector/InspectorStylesSpecMocks.js b/src/ui/inspector/InspectorStylesSpecMocks.js
index 02c7eab2f..2d37470e7 100644
--- a/src/ui/inspector/InspectorStylesSpecMocks.js
+++ b/src/ui/inspector/InspectorStylesSpecMocks.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/ui/inspector/InspectorViews.vue b/src/ui/inspector/InspectorViews.vue
index 83f813707..7ac66a7d2 100644
--- a/src/ui/inspector/InspectorViews.vue
+++ b/src/ui/inspector/InspectorViews.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/ui/inspector/Location.vue b/src/ui/inspector/Location.vue
index 7ff27c006..9d661dfbc 100644
--- a/src/ui/inspector/Location.vue
+++ b/src/ui/inspector/Location.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -21,7 +21,10 @@
*****************************************************************************/
<template>
-<div class="c-inspect-properties c-inspect-properties--location">
+<div
+ v-if="originalPath.length"
+ class="c-inspect-properties c-inspect-properties--location"
+>
<div
class="c-inspect-properties__header"
title="The location of this linked object."
diff --git a/src/ui/inspector/ObjectName.vue b/src/ui/inspector/ObjectName.vue
index fb0ca300e..9703d4a91 100644
--- a/src/ui/inspector/ObjectName.vue
+++ b/src/ui/inspector/ObjectName.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -22,28 +22,34 @@
<template>
<div class="c-inspector__header">
- <div v-if="!multiSelect"
- class="c-inspector__selected c-object-label"
- :class="[statusClass]"
+ <div
+ v-if="!multiSelect"
+ class="c-inspector__selected c-object-label"
+ :class="[statusClass]"
>
- <div class="c-object-label__type-icon"
- :class="typeCssClass"
+ <div
+ class="c-object-label__type-icon"
+ :class="typeCssClass"
>
- <span class="is-status__indicator"
- :title="`This item is ${status}`"
+ <span
+ class="is-status__indicator"
+ :title="`This item is ${status}`"
></span>
</div>
- <span v-if="!singleSelectNonObject"
- class="c-inspector__selected c-object-label__name"
+ <span
+ v-if="!singleSelectNonObject"
+ class="c-inspector__selected c-object-label__name"
>{{ item.name }}</span>
- <div v-if="singleSelectNonObject"
- class="c-inspector__selected c-inspector__selected--non-domain-object c-object-label"
+ <div
+ v-if="singleSelectNonObject"
+ class="c-inspector__selected c-inspector__selected--non-domain-object c-object-label"
>
<span class="c-object-label__name">Layout Object</span>
</div>
</div>
- <div v-if="multiSelect"
- class="c-inspector__multiple-selected"
+ <div
+ v-if="multiSelect"
+ class="c-inspector__multiple-selected"
>
{{ itemsSelected }} items selected
</div>
diff --git a/src/ui/inspector/details/DetailText.vue b/src/ui/inspector/details/DetailText.vue
index 14fa95380..5fed5de55 100644
--- a/src/ui/inspector/details/DetailText.vue
+++ b/src/ui/inspector/details/DetailText.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/ui/inspector/details/Properties.vue b/src/ui/inspector/details/Properties.vue
index 2e2079ba7..148a2d3dd 100644
--- a/src/ui/inspector/details/Properties.vue
+++ b/src/ui/inspector/details/Properties.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -166,7 +166,8 @@ export default {
}
return definition.form
- .map((field) => {
+ .filter(field => !field.hideFromInspector)
+ .map(field => {
let path = field.property;
if (typeof path === 'string') {
path = [path];
diff --git a/src/ui/inspector/inspector.scss b/src/ui/inspector/inspector.scss
index ba79a243a..3aafd98ba 100644
--- a/src/ui/inspector/inspector.scss
+++ b/src/ui/inspector/inspector.scss
@@ -79,6 +79,11 @@
padding-right: 0;
}
+ textarea {
+ // When a textarea is in the Inspector, only allow vertical resize
+ resize: vertical;
+ }
+
/************************************************************** LEGACY */
.l-inspector-part {
display: contents;
@@ -151,7 +156,8 @@
padding: 3px $interiorMarginLg 3px 0;
}
- &__label {
+ &__label,
+ &__hint {
color: $colorInspectorPropName;
&[title]:not([title=""]) {
@@ -169,6 +175,20 @@
}
}
+.is-editing {
+ .c-inspect-properties {
+ &__value, &__label {
+ line-height: 160%; // Prevent buttons/selects from overlapping when wrapping
+ }
+ }
+ .grid-row--pad-label-for-button {
+ // Add extra space at the top of the label grid cell because there's a button to the right
+ [class*='label'] {
+ line-height: 1.8em;
+ }
+ }
+}
+
/********************************************* INSPECTOR PROPERTIES TAB */
.c-saved-style {
cursor: default;
@@ -183,17 +203,17 @@
}
}
- li.grid-row + li.grid-row {
+ .grid-row + .grid-row {
> * {
border-top: 1px solid $colorInspectorSectionHeaderBg;
}
}
- li.grid-row .label {
+ .grid-row .label {
color: $colorInspectorPropName;
}
- li.grid-row .value {
+ .grid-row .value {
color: $colorInspectorPropVal;
word-break: break-all;
&:first-child {
diff --git a/src/ui/inspector/styles/FontStyleEditor.vue b/src/ui/inspector/styles/FontStyleEditor.vue
index 0a1e7cc53..aa78cd319 100644
--- a/src/ui/inspector/styles/FontStyleEditor.vue
+++ b/src/ui/inspector/styles/FontStyleEditor.vue
@@ -1,7 +1,8 @@
<template>
<div class="c-toolbar">
- <div ref="fontSizeMenu"
- class="c-menu-button c-ctrl-wrapper c-ctrl-wrapper--menus-left"
+ <div
+ ref="fontSizeMenu"
+ class="c-menu-button c-ctrl-wrapper c-ctrl-wrapper--menus-left"
>
<button
class="c-icon-button c-button--menu icon-font-size"
@@ -10,8 +11,9 @@
<span class="c-button__label">{{ fontSizeLabel }}</span>
</button>
</div>
- <div ref="fontMenu"
- class="c-menu-button c-ctrl-wrapper c-ctrl-wrapper--menus-left"
+ <div
+ ref="fontMenu"
+ class="c-menu-button c-ctrl-wrapper c-ctrl-wrapper--menus-left"
>
<button
class="c-icon-button c-button--menu icon-font"
diff --git a/src/ui/inspector/styles/SavedStyleSelector.vue b/src/ui/inspector/styles/SavedStyleSelector.vue
index b92630b54..c69f57b2b 100644
--- a/src/ui/inspector/styles/SavedStyleSelector.vue
+++ b/src/ui/inspector/styles/SavedStyleSelector.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
-* Open MCT, Copyright (c) 2014-2021, United States Government
+* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -23,9 +23,10 @@
<template>
<div>
<div class="c-style c-style--saved has-local-controls c-toolbar">
- <div class="c-style__controls"
- :title="description"
- @click="selectStyle()"
+ <div
+ class="c-style__controls"
+ :title="description"
+ @click="selectStyle()"
>
<div
class="c-style-thumb"
diff --git a/src/ui/inspector/styles/SavedStylesInspectorView.vue b/src/ui/inspector/styles/SavedStylesInspectorView.vue
index 310feebcb..dc152eb7c 100644
--- a/src/ui/inspector/styles/SavedStylesInspectorView.vue
+++ b/src/ui/inspector/styles/SavedStylesInspectorView.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
-* Open MCT, Copyright (c) 2014-2021, United States Government
+* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/ui/inspector/styles/SavedStylesView.vue b/src/ui/inspector/styles/SavedStylesView.vue
index f5be86fdf..93f6b78d3 100644
--- a/src/ui/inspector/styles/SavedStylesView.vue
+++ b/src/ui/inspector/styles/SavedStylesView.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
-* Open MCT, Copyright (c) 2014-2021, United States Government
+* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/ui/inspector/styles/StylesInspectorView.vue b/src/ui/inspector/styles/StylesInspectorView.vue
index f03f70340..56bd70150 100644
--- a/src/ui/inspector/styles/StylesInspectorView.vue
+++ b/src/ui/inspector/styles/StylesInspectorView.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
-* Open MCT, Copyright (c) 2014-2021, United States Government
+* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/ui/layout/AboutDialog.vue b/src/ui/layout/AboutDialog.vue
index c2fc25f00..51d771942 100644
--- a/src/ui/layout/AboutDialog.vue
+++ b/src/ui/layout/AboutDialog.vue
@@ -13,7 +13,7 @@
Open MCT
</h1>
<div class="l-description s-description">
- <p>Open MCT, Copyright &copy; 2014-2021, United States Government as represented by the Administrator of the National Aeronautics and Space Administration. All rights reserved.</p>
+ <p>Open MCT, Copyright &copy; 2014-2022, United States Government as represented by the Administrator of the National Aeronautics and Space Administration. All rights reserved.</p>
<p>
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 <a
target="_blank"
diff --git a/src/ui/layout/AppLogo.vue b/src/ui/layout/AppLogo.vue
index 998446d31..8ad860f9f 100644
--- a/src/ui/layout/AppLogo.vue
+++ b/src/ui/layout/AppLogo.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/ui/layout/BrowseBar.vue b/src/ui/layout/BrowseBar.vue
index 9dacff691..ba427d9c5 100644
--- a/src/ui/layout/BrowseBar.vue
+++ b/src/ui/layout/BrowseBar.vue
@@ -11,11 +11,13 @@
class="l-browse-bar__object-name--w c-object-label"
:class="[statusClass]"
>
- <div class="c-object-label__type-icon"
- :class="type.cssClass"
+ <div
+ class="c-object-label__type-icon"
+ :class="type.cssClass"
>
- <span class="is-status__indicator"
- :title="`This item is ${status}`"
+ <span
+ class="is-status__indicator"
+ :title="`This item is ${status}`"
></span>
</div>
<span
@@ -38,10 +40,11 @@
:views="views"
/>
<!-- Action buttons -->
- <NotebookMenuSwitcher v-if="notebookEnabled"
- :domain-object="domainObject"
- :object-path="openmct.router.path"
- class="c-notebook-snapshot-menubutton"
+ <NotebookMenuSwitcher
+ v-if="notebookEnabled"
+ :domain-object="domainObject"
+ :object-path="openmct.router.path"
+ class="c-notebook-snapshot-menubutton"
/>
<div class="l-browse-bar__actions">
<button
diff --git a/src/ui/layout/CreateButton.vue b/src/ui/layout/CreateButton.vue
index a1450599e..2443f6b66 100644
--- a/src/ui/layout/CreateButton.vue
+++ b/src/ui/layout/CreateButton.vue
@@ -1,6 +1,7 @@
<template>
-<div ref="createButton"
- class="c-create-button--w"
+<div
+ ref="createButton"
+ class="c-create-button--w"
>
<button
class="c-create-button c-button--menu c-button--major icon-plus"
@@ -18,32 +19,18 @@ import objectUtils from 'objectUtils';
export default {
inject: ['openmct'],
data: function () {
- let items = [];
-
- this.openmct.types.listKeys().forEach(key => {
- let menuItem = this.openmct.types.get(key).definition;
-
- if (menuItem.creatable) {
- let menuItemTemplate = {
- cssClass: menuItem.cssClass,
- name: menuItem.name,
- description: menuItem.description,
- onItemClicked: () => this.create(key)
- };
-
- items.push(menuItemTemplate);
- }
- });
return {
- items: items,
+ menuItems: {},
selectedMenuItem: {},
opened: false
};
},
computed: {
sortedItems() {
- return this.items.slice().sort((a, b) => {
+ let items = this.getItems();
+
+ return items.sort((a, b) => {
if (a.name < b.name) {
return -1;
} else if (a.name > b.name) {
@@ -55,6 +42,26 @@ export default {
}
},
methods: {
+ getItems() {
+ let keys = this.openmct.types.listKeys();
+
+ keys.forEach(key => {
+ if (!this.menuItems[key]) {
+ let typeDef = this.openmct.types.get(key).definition;
+
+ if (typeDef.creatable) {
+ this.menuItems[key] = {
+ cssClass: typeDef.cssClass,
+ name: typeDef.name,
+ description: typeDef.description,
+ onItemClicked: () => this.create(key)
+ };
+ }
+ }
+ });
+
+ return Object.values(this.menuItems);
+ },
showCreateMenu() {
const elementBoundingClientRect = this.$refs.createButton.getBoundingClientRect();
const x = elementBoundingClientRect.x;
diff --git a/src/ui/layout/Layout.vue b/src/ui/layout/Layout.vue
index 2ded6498d..ee5296436 100644
--- a/src/ui/layout/Layout.vue
+++ b/src/ui/layout/Layout.vue
@@ -18,6 +18,9 @@
}"
>
<CreateButton class="l-shell__create-button" />
+ <GrandSearch
+ ref="grand-search"
+ />
<indicators class="l-shell__head-section l-shell__indicators" />
<button
class="l-shell__head__collapse-button c-icon-button"
@@ -122,6 +125,7 @@ import Inspector from '../inspector/Inspector.vue';
import MctTree from './mct-tree.vue';
import ObjectView from '../components/ObjectView.vue';
import CreateButton from './CreateButton.vue';
+import GrandSearch from './search/GrandSearch.vue';
import multipane from './multipane.vue';
import pane from './pane.vue';
import BrowseBar from './BrowseBar.vue';
@@ -136,6 +140,7 @@ export default {
MctTree,
ObjectView,
CreateButton,
+ GrandSearch,
multipane,
pane,
BrowseBar,
diff --git a/src/ui/layout/LayoutSpec.js b/src/ui/layout/LayoutSpec.js
index 1f0ecd3cc..625f86704 100644
--- a/src/ui/layout/LayoutSpec.js
+++ b/src/ui/layout/LayoutSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/ui/layout/MCTSearch.vue b/src/ui/layout/MCTSearch.vue
deleted file mode 100644
index 9208f0ca8..000000000
--- a/src/ui/layout/MCTSearch.vue
+++ /dev/null
@@ -1,13 +0,0 @@
-<template>
-<div class="c-search c-search--major">
- <input
- type="search"
- placeholder="Search"
- >
-</div>
-</template>
-
-<script>
-export default {
-};
-</script>
diff --git a/src/ui/layout/layout.scss b/src/ui/layout/layout.scss
index f3568d005..f708dbbed 100644
--- a/src/ui/layout/layout.scss
+++ b/src/ui/layout/layout.scss
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -38,7 +38,7 @@
transition: $transIn;
&.is-expanded {
- height: max-content;
+ min-height: 100px;
}
}
diff --git a/src/ui/layout/mct-search.scss b/src/ui/layout/mct-search.scss
deleted file mode 100644
index a6b1a8f18..000000000
--- a/src/ui/layout/mct-search.scss
+++ /dev/null
@@ -1,10 +0,0 @@
-/******************************* SEARCH */
-.c-search {
- input[type=search] {
- width: 100%;
- }
-
- &--major {
- display: flex;
- }
-}
diff --git a/src/ui/layout/mct-tree.scss b/src/ui/layout/mct-tree.scss
index 95c900a83..1f31c3c7b 100644
--- a/src/ui/layout/mct-tree.scss
+++ b/src/ui/layout/mct-tree.scss
@@ -6,7 +6,9 @@
flex: 1 1 auto;
overflow: auto;
- > * + * { margin-top: $interiorMargin; }
+ > * + * {
+ margin-top: $interiorMargin;
+ }
&__search {
flex: 0 0 auto;
@@ -59,7 +61,6 @@
@include userSelectNone();
overflow-x: hidden;
overflow-y: auto;
- padding-right: $interiorMarginSm;
.icon-arrow-nav-to-parent {
visibility: hidden;
@@ -71,6 +72,7 @@
li {
position: relative;
+
&[class*="__item-h"] {
display: block;
width: 100%;
@@ -82,7 +84,6 @@
}
&__item {
- border-radius: $controlCr;
display: flex;
align-items: center;
cursor: pointer;
@@ -107,12 +108,14 @@
color: $colorItemTreeSelectedFg;
}
}
+
&.is-new {
animation-name: animTemporaryHighlight;
animation-timing-function: ease-out;
animation-duration: 3s;
animation-iteration-count: 1;
}
+
&.is-context-clicked {
box-shadow: inset $colorItemTreeSelectedBg 0 0 0 1px;
}
@@ -128,11 +131,15 @@
}
.c-tree {
+ padding-right: $interiorMarginSm;
+
.c-tree {
margin-left: 15px;
}
&__item {
+ border-radius: $smallCr;
+
[class*="view-control"] {
padding: 2px 10px;
}
@@ -161,6 +168,7 @@
@include button($bg: $colorMobilePaneLeftTreeItemBg, $fg: $colorMobilePaneLeftTreeItemFg);
height: $mobileTreeItemH;
margin-bottom: $interiorMarginSm;
+
[class*="view-control"] {
width: ceil($mobileTreeItemH * 0.5);
}
@@ -202,10 +210,11 @@
.c-tree {
&__item {
- body.mobile & {
+ body.mobile & {
@include button($bg: $colorMobilePaneLeftTreeItemBg, $fg: $colorMobilePaneLeftTreeItemFg);
height: $mobileTreeItemH;
margin-bottom: $interiorMarginSm;
+
[class*="view-control"] {
width: ceil($mobileTreeItemH * 0.5);
}
@@ -218,9 +227,9 @@
}
.c-list {
- padding-right: $interiorMargin;
-
&__item {
+ border-radius: $smallCr;
+
&__name {
$p: $interiorMarginSm;
@include ellipsize();
@@ -254,7 +263,8 @@
content: '';
display: block;
position: absolute;
- left: 50%; top: 50%;
+ left: 50%;
+ top: 50%;
height: $dimension;
width: $dimension;
}
@@ -280,38 +290,5 @@
border: 1px solid $colorFormLines;
border-radius: $controlCr;
padding: $interiorMargin;
-
- > .c-tree {
- overflow: auto;
- }
- }
-}
-
-// TRANSITIONS
-.children-enter-active {
- &.down {
- animation: animSlideLeft 500ms;
- }
-
- &.up {
- animation: animSlideRight 500ms;
}
}
-
-@keyframes animSlideLeft {
- 0% {opacity: 0; transform: translateX(100%);}
- 10% {opacity: 1;}
- 100% {transform: translateX(0);}
-}
-
-@keyframes animSlideRight {
- 0% {opacity: 0; transform: translateX(-100%);}
- 10% {opacity: 1;}
- 100% {transform: translateX(0);}
-}
-
-@keyframes animTemporaryHighlight {
- from { background: transparent; }
- 30% { background: $colorItemTreeNewNode; }
- 100% { background: transparent; }
-}
diff --git a/src/ui/layout/mct-tree.vue b/src/ui/layout/mct-tree.vue
index e02c4264c..220f82b42 100644
--- a/src/ui/layout/mct-tree.vue
+++ b/src/ui/layout/mct-tree.vue
@@ -1,11 +1,18 @@
<template>
-<div class="c-tree-and-search">
-
+<div
+ ref="treeContainer"
+ class="c-tree-and-search"
+ :class="{
+ 'c-selector': isSelectorTree
+ }"
+ :style="treeHeight"
+>
<div
ref="search"
class="c-tree-and-search__search"
>
<search
+ v-show="isSelectorTree"
ref="shell-search"
class="c-search"
:value="searchValue"
@@ -72,6 +79,8 @@
v-for="(treeItem, index) in visibleItems"
:key="treeItem.navigationPath"
:node="treeItem"
+ :is-selector-tree="isSelectorTree"
+ :selected-item="selectedItem"
:active-search="activeSearch"
:left-offset="!activeSearch ? treeItem.leftOffset : '0px'"
:is-new="treeItem.isNew"
@@ -82,7 +91,8 @@
:loading-items="treeItemLoading"
@tree-item-mounted="scrollToCheck($event)"
@tree-item-destroyed="removeCompositionListenerFor($event)"
- @navigation-click="treeItemAction(treeItem, $event)"
+ @tree-item-action="treeItemAction(treeItem, $event)"
+ @tree-item-selection="treeItemSelection(treeItem)"
/>
<!-- main loading -->
<div
@@ -115,6 +125,7 @@ const ITEM_BUFFER = 25;
const LOCAL_STORAGE_KEY__TREE_EXPANDED = 'mct-tree-expanded';
const SORT_MY_ITEMS_ALPH_ASC = true;
const TREE_ITEM_INDENT_PX = 18;
+const LOCATOR_ITEM_COUNT_HEIGHT = 10; // how many tree items to make the locator selection box show
export default {
name: 'MctTree',
@@ -124,13 +135,27 @@ export default {
},
inject: ['openmct'],
props: {
+ isSelectorTree: {
+ type: Boolean,
+ required: false,
+ default() {
+ return false;
+ }
+ },
+ initialSelection: {
+ type: Object,
+ required: false,
+ default() {
+ return {};
+ }
+ },
syncTreeNavigation: {
type: Boolean,
- required: true
+ required: false
},
resetTreeNavigation: {
type: Boolean,
- required: true
+ required: false
}
},
data() {
@@ -149,7 +174,9 @@ export default {
itemHeight: 27,
itemOffset: 0,
activeSearch: false,
- mainTreeTopMargin: undefined
+ mainTreeTopMargin: undefined,
+ selectedItem: {},
+ observers: {}
};
},
computed: {
@@ -179,6 +206,13 @@ export default {
},
showNoSearchResults() {
return this.searchValue && this.searchResultItems.length === 0 && !this.searchLoading;
+ },
+ treeHeight() {
+ if (!this.isSelectorTree) {
+ return {};
+ } else {
+ return { height: this.itemHeight * LOCATOR_ITEM_COUNT_HEIGHT + 'px' };
+ }
}
},
watch: {
@@ -223,7 +257,16 @@ export default {
await this.loadRoot();
this.isLoading = false;
- await this.syncTreeOpenItems();
+ if (!this.isSelectorTree) {
+ await this.syncTreeOpenItems();
+ } else {
+ if (this.initialSelection.identifier) {
+ const objectPath = await this.openmct.objects.getOriginalPath(this.initialSelection.identifier);
+ const navigationPath = this.buildNavigationPath(objectPath);
+
+ this.openAndScrollTo(navigationPath);
+ }
+ }
},
created() {
this.getSearchResults = _.debounce(this.getSearchResults, 400);
@@ -234,6 +277,8 @@ export default {
if (this.treeResizeObserver) {
this.treeResizeObserver.disconnect();
}
+
+ this.destroyObservers(this.observers);
},
methods: {
async initialize() {
@@ -265,6 +310,10 @@ export default {
this.openTreeItem(parentItem);
}
},
+ treeItemSelection(item) {
+ this.selectedItem = item;
+ this.$emit('tree-item-selection', item);
+ },
async openTreeItem(parentItem) {
let parentPath = parentItem.navigationPath;
@@ -358,6 +407,10 @@ export default {
}
},
openAndScrollTo(navigationPath) {
+ if (navigationPath.includes('/ROOT')) {
+ navigationPath = navigationPath.split('/ROOT').join('');
+ }
+
let idArray = navigationPath.split('/');
let fullPathArray = [];
let pathsToOpen;
@@ -367,7 +420,6 @@ export default {
// skip root
idArray.splice(0, 2);
idArray[0] = 'browse/' + idArray[0];
-
idArray.reduce((parentPath, childPath) => {
let fullPath = [parentPath, childPath].join('/');
@@ -383,7 +435,11 @@ export default {
return this.openTreeItem(this.getTreeItemByPath(childPath));
- }, Promise.resolve());
+ }, Promise.resolve()).then(() => {
+ if (this.isSelectorTree) {
+ this.treeItemSelection(this.getTreeItemByPath(navigationPath));
+ }
+ });
},
scrollToCheck(navigationPath) {
if (this.scrollToPath && this.scrollToPath === navigationPath) {
@@ -392,7 +448,7 @@ export default {
},
scrollTo(navigationPath) {
- if (this.isItemInView(navigationPath)) {
+ if (!this.$refs.scrollable || this.isItemInView(navigationPath)) {
return;
}
@@ -411,6 +467,10 @@ export default {
}
},
scrollEndEvent() {
+ if (!this.$refs.srcrollable) {
+ return;
+ }
+
this.$nextTick(() => {
if (this.scrollToPath) {
if (!this.isItemInView(this.scrollToPath)) {
@@ -430,32 +490,36 @@ export default {
return scrollTopAmount >= treeStart && scrollTopAmount < treeEnd;
},
- sortNameAscending(a, b) {
- // sorting tree children items
- if (!(a.name && b.name)) {
- if (a.object.name.toLowerCase()
- > b.object.name.toLowerCase()) {
- return 1;
- }
+ getLowercaseObjectName(domainObject) {
+ let objectName;
+ if (!domainObject) {
+ return objectName;
+ }
- if (b.object.name.toLowerCase()
- > a.object.name.toLowerCase()) {
- return -1;
- }
+ if (domainObject.name) {
+ objectName = domainObject.name.toLowerCase();
}
- // sorting composition items
- if (!a.name || !b.name) {
+ if (domainObject.object && domainObject.object.name) {
+ objectName = domainObject.object.name.toLowerCase();
+ }
+
+ return objectName;
+ },
+ sortNameAscending(a, b) {
+ // sorting tree children items
+ let objectAName = this.getLowercaseObjectName(a);
+ let objectBName = this.getLowercaseObjectName(b);
+ if (!objectAName || !objectBName) {
return 0;
}
- if (a.name.toLowerCase()
- > b.name.toLowerCase()) {
+ // sorting composition items
+ if (objectAName > objectBName) {
return 1;
}
- if (b.name.toLowerCase()
- > a.name.toLowerCase()) {
+ if (objectBName > objectAName) {
return -1;
}
@@ -493,6 +557,8 @@ export default {
}
return composition.map((object) => {
+ this.addTreeItemObserver(object, parentObjectPath);
+
return this.buildTreeItem(object, parentObjectPath);
});
},
@@ -509,6 +575,41 @@ export default {
navigationPath
};
},
+ addTreeItemObserver(domainObject, parentObjectPath) {
+ if (this.observers[domainObject.identifier.key]) {
+ this.observers[domainObject.identifier.key]();
+ }
+
+ this.observers[domainObject.identifier.key] = this.openmct.objects.observe(
+ domainObject,
+ 'name',
+ this.updateTreeItems.bind(this, parentObjectPath)
+ );
+ },
+ async updateTreeItems(parentObjectPath) {
+ let children;
+
+ if (parentObjectPath.length) {
+ const parentItem = this.treeItems.find(item => item.objectPath === parentObjectPath);
+ const descendants = this.getChildrenInTreeFor(parentItem, true);
+ const parentIndex = this.treeItems.map(e => e.object).indexOf(parentObjectPath[0]);
+
+ children = await this.loadAndBuildTreeItemsFor(parentItem.object, parentItem.objectPath);
+
+ this.treeItems.splice(parentIndex + 1, descendants.length, ...children);
+ } else {
+ const root = await this.openmct.objects.get('ROOT');
+ children = await this.loadAndBuildTreeItemsFor(root, []);
+
+ this.treeItems = [...children];
+ }
+
+ for (let item of children) {
+ if (this.isTreeItemOpen(item)) {
+ this.openTreeItem(item);
+ }
+ }
+ },
buildNavigationPath(objectPath) {
return '/browse/' + [...objectPath].reverse()
.map((object) => this.openmct.objects.makeKeyString(object.identifier))
@@ -521,6 +622,8 @@ export default {
const descendants = this.getChildrenInTreeFor(parentItem, true);
const directDescendants = this.getChildrenInTreeFor(parentItem);
+ this.addTreeItemObserver(domainObject, parentItem.objectPath);
+
if (directDescendants.length === 0) {
this.addItemToTreeAfter(newItem, parentItem);
@@ -555,6 +658,7 @@ export default {
let removeItem = directDescendants.find(item => item.id === removeKeyString);
this.removeItemFromTree(removeItem);
+ this.removeItemFromObservers(removeItem);
};
},
removeCompositionListenerFor(navigationPath) {
@@ -576,6 +680,13 @@ export default {
const removeIndex = this.getTreeItemIndex(item.navigationPath);
this.treeItems.splice(removeIndex, 1);
},
+ removeItemFromObservers(item) {
+ if (this.observers[item.id]) {
+ this.observers[item.id]();
+
+ delete this.observers[item.id];
+ }
+ },
addItemToTreeBefore(addItem, beforeItem) {
const addIndex = this.getTreeItemIndex(beforeItem.navigationPath);
@@ -721,18 +832,26 @@ export default {
let checkHeights = () => {
let treeTopMargin = this.getElementStyleValue(this.$refs.mainTree, 'marginTop');
+ let paddingOffset = 0;
+
if (
this.$el
&& this.$refs.search
&& this.$refs.mainTree
+ && this.$refs.treeContainer
&& this.$refs.dummyItem
&& this.$el.offsetHeight !== 0
&& treeTopMargin > 0
) {
+ if (this.isSelectorTree) {
+ paddingOffset = this.getElementStyleValue(this.$refs.treeContainer, 'padding');
+ }
+
this.mainTreeTopMargin = treeTopMargin;
this.mainTreeHeight = this.$el.offsetHeight
- this.$refs.search.offsetHeight
- - this.mainTreeTopMargin;
+ - this.mainTreeTopMargin
+ - (paddingOffset * 2);
this.itemHeight = this.getElementStyleValue(this.$refs.dummyItem, 'height');
resolve();
@@ -783,14 +902,31 @@ export default {
return Number(styleString.slice(0, index));
},
getSavedOpenItems() {
+ if (this.isSelectorTree) {
+ return;
+ }
+
let openItems = localStorage.getItem(LOCAL_STORAGE_KEY__TREE_EXPANDED);
this.openTreeItems = openItems ? JSON.parse(openItems) : [];
},
setSavedOpenItems() {
+ if (this.isSelectorTree) {
+ return;
+ }
+
localStorage.setItem(LOCAL_STORAGE_KEY__TREE_EXPANDED, JSON.stringify(this.openTreeItems));
},
handleTreeResize() {
this.calculateHeights();
+ },
+ destroyObservers(observers) {
+ Object.entries(observers).forEach(([keyString, unobserve]) => {
+ if (typeof unobserve === 'function') {
+ unobserve();
+ }
+
+ delete observers[keyString];
+ });
}
}
};
diff --git a/src/ui/layout/pane.vue b/src/ui/layout/pane.vue
index 569a3d3f8..270df6fb9 100644
--- a/src/ui/layout/pane.vue
+++ b/src/ui/layout/pane.vue
@@ -17,8 +17,9 @@
@mousedown="start"
></div>
<div class="l-pane__header">
- <span v-if="label"
- class="l-pane__label"
+ <span
+ v-if="label"
+ class="l-pane__label"
>{{ label }}</span>
<slot name="controls"></slot>
<button
diff --git a/src/ui/layout/search/AnnotationSearchResult.vue b/src/ui/layout/search/AnnotationSearchResult.vue
new file mode 100644
index 000000000..67b60cef4
--- /dev/null
+++ b/src/ui/layout/search/AnnotationSearchResult.vue
@@ -0,0 +1,150 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+<template>
+<div
+ class="c-gsearch-result c-gsearch-result--annotation"
+ aria-label="Search Result"
+ role="presentation"
+>
+ <div
+ class="c-gsearch-result__type-icon"
+ :class="resultTypeIcon"
+ ></div>
+ <div
+ class="c-gsearch-result__body"
+ aria-label="Annotation Search Result"
+ >
+ <div
+ class="c-gsearch-result__title"
+ @click="clickedResult"
+ >
+ {{ getResultName }}
+ </div>
+
+ <ObjectPath
+ ref="location"
+ :read-only="false"
+ />
+
+ <div class="c-gsearch-result__tags">
+ <div
+ v-for="(tag, index) in result.fullTagModels"
+ :key="index"
+ class="c-tag"
+ :class="{ '--is-not-search-match': !isSearchMatched(tag) }"
+ :style="{ backgroundColor: tag.backgroundColor, color: tag.foregroundColor }"
+ >
+ {{ tag.label }}
+ </div>
+ </div>
+ </div>
+ <div class="c-gsearch-result__more-options-button">
+ <button class="c-icon-button icon-3-dots"></button>
+ </div>
+</div>
+</template>
+
+<script>
+import ObjectPath from '../../components/ObjectPath.vue';
+import objectPathToUrl from '../../../tools/url';
+
+export default {
+ name: 'AnnotationSearchResult',
+ components: {
+ ObjectPath
+ },
+ inject: ['openmct'],
+ props: {
+ result: {
+ type: Object,
+ required: true,
+ default() {
+ return {};
+ }
+ }
+ },
+ data() {
+ return {
+ };
+ },
+ computed: {
+ domainObject() {
+ return this.result.targetModels[0];
+ },
+ getResultName() {
+ if (this.result.annotationType === this.openmct.annotation.ANNOTATION_TYPES.NOTEBOOK) {
+ const targetID = Object.keys(this.result.targets)[0];
+ const entryIdToFind = this.result.targets[targetID].entryId;
+ const notebookModel = this.result.targetModels[0].configuration.entries;
+
+ const sections = Object.values(notebookModel);
+ for (const section of sections) {
+ const pages = Object.values(section);
+ for (const entries of pages) {
+ for (const entry of entries) {
+ if (entry.id === entryIdToFind) {
+ return entry.text;
+ }
+ }
+ }
+ }
+
+ return "Could not find any matching Notebook entries";
+ } else {
+ return this.result.targetModels[0].name;
+ }
+ },
+ resultTypeIcon() {
+ return this.openmct.types.get(this.result.type).definition.cssClass;
+ },
+ tagBackgroundColor() {
+ return this.result.fullTagModels[0].backgroundColor;
+ },
+ tagForegroundColor() {
+ return this.result.fullTagModels[0].foregroundColor;
+ }
+ },
+ mounted() {
+ const selectionObject = {
+ context: {
+ item: this.domainObject
+ }
+ };
+ this.$refs.location.updateSelection([[selectionObject]]);
+ },
+ methods: {
+ clickedResult() {
+ const objectPath = this.domainObject.originalPath;
+ const resultUrl = objectPathToUrl(this.openmct, objectPath);
+ this.openmct.router.navigate(resultUrl);
+ },
+ isSearchMatched(tag) {
+ if (this.result.matchingTagKeys) {
+ return this.result.matchingTagKeys.includes(tag.tagID);
+ }
+
+ return false;
+ }
+ }
+};
+</script>
diff --git a/src/ui/layout/search/GrandSearch.vue b/src/ui/layout/search/GrandSearch.vue
new file mode 100644
index 000000000..49a48baaf
--- /dev/null
+++ b/src/ui/layout/search/GrandSearch.vue
@@ -0,0 +1,146 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+<template>
+<div
+ ref="GrandSearch"
+ aria-label="OpenMCT Search"
+ class="c-gsearch"
+ role="searchbox"
+>
+ <search
+ ref="shell-search"
+ class="c-gsearch__input"
+ tabindex="0"
+ :value="searchValue"
+ @input="searchEverything"
+ @clear="searchEverything"
+ @click="showSearchResults"
+ />
+ <SearchResultsDropDown
+ ref="searchResultsDropDown"
+ />
+
+</div>
+</template>
+
+<script>
+import search from '../../components/search.vue';
+import SearchResultsDropDown from './SearchResultsDropDown.vue';
+
+export default {
+ name: 'GrandSearch',
+ components: {
+ search,
+ SearchResultsDropDown
+ },
+ inject: ['openmct'],
+ props: {
+ },
+ data() {
+ return {
+ searchValue: '',
+ searchLoading: false,
+ annotationSearchResults: [],
+ objectSearchResults: []
+ };
+ },
+ destroyed() {
+ document.body.removeEventListener('click', this.handleOutsideClick);
+ },
+ methods: {
+ async searchEverything(value) {
+ // if an abort controller exists, regardless of the value passed in,
+ // there is an active search that should be canceled
+ if (this.abortSearchController) {
+ this.abortSearchController.abort();
+ delete this.abortSearchController;
+ }
+
+ this.searchValue = value;
+ this.searchLoading = true;
+ // clear any previous search results
+ this.annotationSearchResults = [];
+ this.objectSearchResults = [];
+
+ if (this.searchValue) {
+ await this.getSearchResults();
+ } else {
+ this.searchLoading = false;
+ this.$refs.searchResultsDropDown.showResults(this.annotationSearchResults, this.objectSearchResults);
+ }
+ },
+ getPathsForObjects(objectsNeedingPaths) {
+ return Promise.all(objectsNeedingPaths.map(async (domainObject) => {
+ const keyStringForObject = this.openmct.objects.makeKeyString(domainObject.identifier);
+ const originalPathObjects = await this.openmct.objects.getOriginalPath(keyStringForObject);
+
+ return {
+ originalPath: originalPathObjects,
+ ...domainObject
+ };
+ }));
+ },
+ async getSearchResults() {
+ // an abort controller will be passed in that will be used
+ // to cancel an active searches if necessary
+ this.abortSearchController = new AbortController();
+ const abortSignal = this.abortSearchController.signal;
+ try {
+ this.annotationSearchResults = await this.openmct.annotation.searchForTags(this.searchValue, abortSignal);
+ const fullObjectSearchResults = await Promise.all(this.openmct.objects.search(this.searchValue, abortSignal));
+ const aggregatedObjectSearchResults = fullObjectSearchResults.flat();
+ const aggregatedObjectSearchResultsWithPaths = await this.getPathsForObjects(aggregatedObjectSearchResults);
+ const filterAnnotations = aggregatedObjectSearchResultsWithPaths.filter(result => {
+ return result.type !== 'annotation';
+ });
+ this.objectSearchResults = filterAnnotations;
+ this.showSearchResults();
+ } catch (error) {
+ console.error(`😞 Error searching`, error);
+ this.searchLoading = false;
+
+ if (this.abortSearchController) {
+ delete this.abortSearchController;
+ }
+ }
+ },
+ showSearchResults() {
+ this.$refs.searchResultsDropDown.showResults(this.annotationSearchResults, this.objectSearchResults);
+ document.body.addEventListener('click', this.handleOutsideClick);
+ },
+ handleOutsideClick(event) {
+ // if click event is detected outside the dropdown while the
+ // dropdown is visible, this will collapse the dropdown.
+ if (this.$refs.GrandSearch) {
+ const clickedInsideDropdown = this.$refs.GrandSearch.contains(event.target);
+ const clickedPreviewClose = event.target.parentElement && event.target.parentElement.querySelector('.js-preview-window');
+ const searchResultsDropDown = this.$refs.searchResultsDropDown._data;
+ if (!clickedInsideDropdown && searchResultsDropDown.resultsShown && !searchResultsDropDown.previewVisible && !clickedPreviewClose) {
+ searchResultsDropDown.resultsShown = false;
+ document.body.removeEventListener('click', this.handleOutsideClick);
+ }
+ }
+ }
+ }
+};
+</script>
diff --git a/src/ui/layout/search/GrandSearchSpec.js b/src/ui/layout/search/GrandSearchSpec.js
new file mode 100644
index 000000000..bb8d6dbc0
--- /dev/null
+++ b/src/ui/layout/search/GrandSearchSpec.js
@@ -0,0 +1,203 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+import {
+ createOpenMct,
+ resetApplicationState
+} from 'utils/testing';
+import Vue from 'vue';
+import GrandSearch from './GrandSearch.vue';
+import ExampleTagsPlugin from '../../../../example/exampleTags/plugin';
+import DisplayLayoutPlugin from '../../../plugins/displayLayout/plugin';
+
+describe("GrandSearch", () => {
+ let openmct;
+ let grandSearchComponent;
+ let viewContainer;
+ let parent;
+ let sharedWorkerToRestore;
+ let mockDomainObject;
+ let mockAnnotationObject;
+ let mockDisplayLayout;
+ let mockFolderObject;
+ let originalRouterPath;
+
+ beforeEach((done) => {
+ openmct = createOpenMct();
+ originalRouterPath = openmct.router.path;
+ openmct.router.path = [mockDisplayLayout];
+ openmct.editor.edit();
+
+ openmct.install(new ExampleTagsPlugin());
+ openmct.install(new DisplayLayoutPlugin());
+ const availableTags = openmct.annotation.getAvailableTags();
+ mockDomainObject = {
+ type: 'notebook',
+ name: 'fooRabbitNotebook',
+ identifier: {
+ key: 'some-object',
+ namespace: 'fooNameSpace'
+ },
+ configuration: {
+ entries: {
+ someSection: {
+ somePage: [
+ {
+ id: 'fooBarEntry',
+ text: 'Foo Bar Text'
+ }
+ ]
+ }
+ }
+ }
+ };
+ mockFolderObject = {
+ type: 'folder',
+ name: 'Test Folder',
+ identifier: {
+ key: 'some-folder',
+ namespace: 'fooNameSpace'
+ }
+ };
+ mockDisplayLayout = {
+ type: 'layout',
+ name: 'Bar Layout',
+ identifier: {
+ key: 'some-layout',
+ namespace: 'fooNameSpace'
+ },
+ configuration: {
+ items: [],
+ layoutGrid: [10, 10]
+ }
+ };
+ mockAnnotationObject = {
+ type: 'annotation',
+ name: 'Some Notebook Annotation',
+ annotationType: openmct.annotation.ANNOTATION_TYPES.NOTEBOOK,
+ tags: [availableTags[0].id, availableTags[1].id],
+ identifier: {
+ key: 'anAnnotationKey',
+ namespace: 'fooNameSpace'
+ },
+ targets: {
+ 'fooNameSpace:some-object': {
+ entryId: 'fooBarEntry'
+ }
+ }
+ };
+
+ openmct.router.isNavigatedObject = jasmine.createSpy().and.returnValue(false);
+ const mockObjectProvider = jasmine.createSpyObj("mock object provider", [
+ "create",
+ "update",
+ "get"
+ ]);
+ // eslint-disable-next-line require-await
+ mockObjectProvider.get = async (identifier) => {
+ if (identifier.key === mockDomainObject.identifier.key) {
+ return mockDomainObject;
+ } else if (identifier.key === mockAnnotationObject.identifier.key) {
+ return mockAnnotationObject;
+ } else if (identifier.key === mockDisplayLayout.identifier.key) {
+ return mockDisplayLayout;
+ } else if (identifier.key === mockFolderObject.identifier.key) {
+ return mockFolderObject;
+ } else {
+ return null;
+ }
+ };
+
+ mockObjectProvider.create.and.returnValue(Promise.resolve(true));
+ mockObjectProvider.update.and.returnValue(Promise.resolve(true));
+
+ openmct.objects.addProvider('fooNameSpace', mockObjectProvider);
+
+ const mockViewProvider = jasmine.createSpyObj("mock view provider", [
+ "key",
+ "view",
+ "canView"
+ ]);
+
+ openmct.objectViews.addProvider(mockViewProvider);
+
+ openmct.on('start', async () => {
+ // use local worker
+ sharedWorkerToRestore = openmct.objects.inMemorySearchProvider.worker;
+ openmct.objects.inMemorySearchProvider.worker = null;
+ await openmct.objects.inMemorySearchProvider.index(mockDomainObject);
+ await openmct.objects.inMemorySearchProvider.index(mockDisplayLayout);
+ await openmct.objects.inMemorySearchProvider.index(mockFolderObject);
+ await openmct.objects.inMemorySearchProvider.index(mockAnnotationObject);
+ parent = document.createElement('div');
+ document.body.appendChild(parent);
+ viewContainer = document.createElement('div');
+ parent.append(viewContainer);
+ grandSearchComponent = new Vue({
+ el: viewContainer,
+ components: {
+ GrandSearch
+ },
+ provide: {
+ openmct
+ },
+ template: '<GrandSearch/>'
+ }).$mount();
+ await Vue.nextTick();
+ done();
+ });
+ openmct.startHeadless();
+ });
+
+ afterEach(() => {
+ openmct.objects.inMemorySearchProvider.worker = sharedWorkerToRestore;
+ openmct.router.path = originalRouterPath;
+ grandSearchComponent.$destroy();
+
+ return resetApplicationState(openmct);
+ });
+
+ it("should render an object search result", async () => {
+ await grandSearchComponent.$children[0].searchEverything('foo');
+ await Vue.nextTick();
+ const searchResult = document.querySelector('[aria-label="fooRabbitNotebook notebook result"]');
+ expect(searchResult).toBeDefined();
+ });
+
+ it("should render an annotation search result", async () => {
+ await grandSearchComponent.$children[0].searchEverything('S');
+ await Vue.nextTick();
+ const annotationResult = document.querySelector('[aria-label="Search Result"]');
+ expect(annotationResult).toBeDefined();
+ });
+
+ it("should preview object search results in edit mode if object clicked", async () => {
+ await grandSearchComponent.$children[0].searchEverything('Folder');
+ grandSearchComponent._provided.openmct.router.path = [mockDisplayLayout];
+ await Vue.nextTick();
+ const searchResult = document.querySelector('[name="Test Folder"]');
+ expect(searchResult).toBeDefined();
+ searchResult.click();
+ const previewWindow = document.querySelector('.js-preview-window');
+ expect(previewWindow).toBeDefined();
+ });
+});
diff --git a/src/ui/layout/search/ObjectSearchResult.vue b/src/ui/layout/search/ObjectSearchResult.vue
new file mode 100644
index 000000000..489f80be6
--- /dev/null
+++ b/src/ui/layout/search/ObjectSearchResult.vue
@@ -0,0 +1,142 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+<template>
+<div
+ class="c-gsearch-result c-gsearch-result--object"
+ aria-label="Search Result"
+ role="presentation"
+>
+ <div
+ class="c-gsearch-result__type-icon"
+ :class="resultTypeIcon"
+ ></div>
+ <div
+ class="c-gsearch-result__body"
+ role="option"
+ :aria-label="`${resultName} ${resultType} result`"
+ >
+ <div
+ class="c-gsearch-result__title"
+ :name="resultName"
+ draggable="true"
+ @dragstart="dragStart"
+ @click="clickedResult"
+ >
+ {{ resultName }}
+ </div>
+
+ <ObjectPath
+ ref="objectpath"
+ :read-only="false"
+ />
+ </div>
+ <div class="c-gsearch-result__more-options-button">
+ <button class="c-icon-button icon-3-dots"></button>
+ </div>
+</div>
+</template>
+
+<script>
+import ObjectPath from '../../components/ObjectPath.vue';
+import objectPathToUrl from '../../../tools/url';
+import PreviewAction from '../../preview/PreviewAction';
+
+export default {
+ name: 'ObjectSearchResult',
+ components: {
+ ObjectPath
+ },
+ inject: ['openmct'],
+ props: {
+ result: {
+ type: Object,
+ required: true,
+ default() {
+ return {};
+ }
+ }
+ },
+ computed: {
+ resultName() {
+ return this.result.name;
+ },
+ resultTypeIcon() {
+ return this.openmct.types.get(this.result.type).definition.cssClass;
+ },
+ resultType() {
+ return this.result.type;
+ }
+ },
+ mounted() {
+ const selectionObject = {
+ context: {
+ item: this.result
+ }
+ };
+ this.$refs.objectpath.updateSelection([[selectionObject]]);
+ this.previewAction = new PreviewAction(this.openmct);
+ this.previewAction.on('isVisible', this.togglePreviewState);
+ },
+ destroyed() {
+ this.previewAction.off('isVisible', this.togglePreviewState);
+ },
+ methods: {
+ clickedResult(event) {
+ if (this.openmct.editor.isEditing()) {
+ event.preventDefault();
+ this.preview();
+ } else {
+ const objectPath = this.result.originalPath;
+ let resultUrl = objectPathToUrl(this.openmct, objectPath);
+ // get rid of ROOT if extant
+ if (resultUrl.includes('/ROOT')) {
+ resultUrl = resultUrl.split('/ROOT').join('');
+ }
+
+ this.openmct.router.navigate(resultUrl);
+ }
+ },
+ togglePreviewState(previewState) {
+ this.$emit('preview-changed', previewState);
+ },
+ preview() {
+ const objectPath = this.result.originalPath;
+ if (this.previewAction.appliesTo(objectPath)) {
+ this.previewAction.invoke(objectPath);
+ }
+ },
+ dragStart(event) {
+ const navigatedObject = this.openmct.router.path[0];
+ const objectPath = this.result.originalPath;
+ const serializedPath = JSON.stringify(objectPath);
+ const keyString = this.openmct.objects.makeKeyString(this.result.identifier);
+ if (this.openmct.composition.checkPolicy(navigatedObject, this.result)) {
+ event.dataTransfer.setData("openmct/composable-domain-object", JSON.stringify(this.result));
+ }
+
+ event.dataTransfer.setData("openmct/domain-object-path", serializedPath);
+ event.dataTransfer.setData(`openmct/domain-object/${keyString}`, this.result);
+ }
+ }
+};
+</script>
diff --git a/src/ui/layout/search/SearchResultsDropDown.vue b/src/ui/layout/search/SearchResultsDropDown.vue
new file mode 100644
index 000000000..793989e3e
--- /dev/null
+++ b/src/ui/layout/search/SearchResultsDropDown.vue
@@ -0,0 +1,106 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+<template>
+<div
+ v-if="(annotationResults && annotationResults.length) ||
+ (objectResults && objectResults.length)"
+ class="c-gsearch__dropdown"
+>
+ <div
+ v-show="resultsShown"
+ class="c-gsearch__results-wrapper"
+ >
+ <div class="c-gsearch__results">
+ <div
+ v-if="objectResults && objectResults.length"
+ ref="objectResults"
+ class="c-gsearch__results-section"
+ role="listbox"
+ >
+ <div class="c-gsearch__results-section-title">Object Results</div>
+ <object-search-result
+ v-for="(objectResult, index) in objectResults"
+ :key="index"
+ :result="objectResult"
+ @preview-changed="previewChanged"
+ @click.native="selectedResult"
+ />
+ </div>
+ <div
+ v-if="annotationResults && annotationResults.length"
+ ref="annotationResults"
+ >
+ <div class="c-gsearch__results-section-title">Annotation Results</div>
+ <annotation-search-result
+ v-for="(annotationResult, index) in annotationResults"
+ :key="index"
+ :result="annotationResult"
+ @click.native="selectedResult"
+ />
+ </div>
+ </div>
+ </div>
+</div>
+</template>
+
+<script>
+import AnnotationSearchResult from './AnnotationSearchResult.vue';
+import ObjectSearchResult from './ObjectSearchResult.vue';
+
+export default {
+ name: 'SearchResultsDropDown',
+ components: {
+ AnnotationSearchResult,
+ ObjectSearchResult
+ },
+ data() {
+ return {
+ resultsShown: false,
+ annotationResults: [],
+ objectResults: [],
+ previewVisible: false
+ };
+ },
+ methods: {
+ selectedResult() {
+ if (!this.previewVisible) {
+ this.resultsShown = false;
+ }
+ },
+ previewChanged(changedPreviewState) {
+ this.previewVisible = changedPreviewState;
+ },
+ showResults(passedAnnotationResults, passedObjectResults) {
+ if ((passedAnnotationResults && passedAnnotationResults.length)
+ || (passedObjectResults && passedObjectResults.length)) {
+ this.resultsShown = true;
+ this.annotationResults = passedAnnotationResults;
+ this.objectResults = passedObjectResults;
+ } else {
+ this.resultsShown = false;
+ }
+ }
+ },
+ template: 'Dropdown'
+};
+</script>
diff --git a/src/ui/layout/search/search.scss b/src/ui/layout/search/search.scss
new file mode 100644
index 000000000..49c3585a0
--- /dev/null
+++ b/src/ui/layout/search/search.scss
@@ -0,0 +1,138 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2022, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is licensed under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+
+/******************************* EXPANDED SEARCH 2022 */
+.c-gsearch {
+ .l-shell__head & {
+ // Search input in the shell head
+ width: 20%;
+
+ .c-search {
+ background: rgba($colorHeadFg, 0.2);
+ box-shadow: none;
+ }
+ }
+
+ &__results-wrapper {
+ @include menuOuter();
+ display: flex;
+ flex-direction: column;
+ padding: $interiorMarginLg;
+ min-width: 500px;
+ max-height: 500px;
+ z-index: 60;
+ }
+
+ &__results,
+ &__results-section {
+ flex: 1 1 auto;
+ }
+
+ &__results {
+ // Holds n __results-sections
+ padding-right: $interiorMargin; // Fend off scrollbar
+ overflow-y: auto;
+
+ > * + * {
+ margin-top: $interiorMarginLg;
+ }
+ }
+
+ &__results-section {
+ > * + * {
+ margin-top: $interiorMarginSm;
+ }
+ }
+
+ &__results-section-title {
+ @include propertiesHeader();
+ }
+}
+
+.c-gsearch-result {
+ display: flex;
+ padding: $interiorMargin $interiorMarginSm;
+
+ > * + * {
+ margin-left: $interiorMarginLg;
+ }
+
+ + .c-gsearch-result {
+ border-top: 1px solid $colorInteriorBorder;
+ }
+
+ &__type-icon,
+ &__more-options-button {
+ flex: 0 0 auto;
+ }
+
+ &__type-icon {
+ color: $colorItemTreeIcon;
+ font-size: 2.2em;
+
+ // TEMP: uses object-label component, hide label part
+ .c-object-label__name {
+ display: none;
+ }
+ }
+
+ &__more-options-button {
+ display: none; // TEMP until enabled
+ }
+
+ &__body {
+ flex: 1 1 auto;
+
+ > * + * {
+ margin-top: $interiorMarginSm;
+ }
+
+ .c-location {
+ font-size: 0.9em;
+ opacity: 0.8;
+ }
+ }
+
+ &__tags {
+ display: flex;
+
+ > * + * {
+ margin-left: $interiorMargin;
+ }
+ }
+
+ &__title {
+ border-radius: $basicCr;
+ color: pullForward($colorBodyFg, 30%);
+ cursor: pointer;
+ font-size: 1.15em;
+ padding: 3px $interiorMarginSm;
+
+ &:hover {
+ background-color: $colorItemTreeHoverBg;
+ }
+ }
+
+ .c-tag {
+ font-size: 0.9em;
+ }
+}
diff --git a/src/ui/layout/status-bar/Indicators.vue b/src/ui/layout/status-bar/Indicators.vue
index bbdd3f152..fd7c3f122 100644
--- a/src/ui/layout/status-bar/Indicators.vue
+++ b/src/ui/layout/status-bar/Indicators.vue
@@ -1,5 +1,5 @@
<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
+ Open MCT, Copyright (c) 2014-2022, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
Open MCT is licensed under the Apache License, Version 2.0 (the
@@ -24,10 +24,19 @@
export default {
inject: ['openmct'],
+ beforeDestroy() {
+ this.openmct.indicators.off('addIndicator', this.addIndicator);
+ },
mounted() {
- this.openmct.indicators.getIndicatorObjectsByPriority().forEach((indicator) => {
+ this.openmct.indicators.getIndicatorObjectsByPriority().forEach(this.addIndicator);
+
+ this.openmct.indicators.on('addIndicator', this.addIndicator);
+ },
+ methods: {
+ addIndicator(indicator) {
this.$el.appendChild(indicator.element);
- });
+ }
}
+
};
</script>
diff --git a/src/ui/layout/status-bar/NotificationBanner.vue b/src/ui/layout/status-bar/NotificationBanner.vue
index a1741fbef..757e98c60 100644
--- a/src/ui/layout/status-bar/NotificationBanner.vue
+++ b/src/ui/layout/status-bar/NotificationBanner.vue
@@ -1,5 +1,5 @@
<!--
- Open MCT, Copyright (c) 2014-2021, United States Government
+ Open MCT, Copyright (c) 2014-2022, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
Open MCT is licensed under the Apache License, Version 2.0 (the
@@ -29,9 +29,10 @@
@click="maximize()"
>
<span class="c-message-banner__message">{{ activeModel.message }}</span>
- <span v-if="haslink"
- class="c-message-banner__message"
- :class="[haslink ? getLinkProps.cssClass : '']"
+ <span
+ v-if="haslink"
+ class="c-message-banner__message"
+ :class="[haslink ? getLinkProps.cssClass : '']"
>{{ getLinkProps.text }}</span>
<progress-bar
diff --git a/src/ui/layout/tree-item.vue b/src/ui/layout/tree-item.vue
index ca94611a7..7283e3bd3 100644
--- a/src/ui/layout/tree-item.vue
+++ b/src/ui/layout/tree-item.vue
@@ -7,19 +7,19 @@
class="c-tree__item"
:class="{
'is-alias': isAlias,
- 'is-navigated-object': navigated,
+ 'is-navigated-object': shouldHightlight,
'is-context-clicked': contextClickActive,
'is-new': isNewItem
}"
- @click.capture="handleClick"
+ @click.capture="itemClick"
@contextmenu.capture="handleContextMenu"
>
<view-control
- ref="navigate"
+ ref="action"
class="c-tree__item__view-control"
:value="isOpen || isLoading"
:enabled="!activeSearch && hasComposition"
- @input="navigationClick()"
+ @input="itemAction()"
/>
<object-label
ref="objectLabel"
@@ -52,6 +52,14 @@ export default {
type: Object,
required: true
},
+ isSelectorTree: {
+ type: Boolean,
+ required: true
+ },
+ selectedItem: {
+ type: Object,
+ required: true
+ },
activeSearch: {
type: Boolean,
default: false
@@ -109,6 +117,9 @@ export default {
return parentKeyString !== this.node.object.location;
},
+ isSelectedItem() {
+ return this.selectedItem.objectPath === this.node.objectPath;
+ },
isNewItem() {
return this.isNew;
},
@@ -118,6 +129,13 @@ export default {
isOpen() {
return this.openItems.includes(this.navigationPath);
},
+ shouldHightlight() {
+ if (this.isSelectorTree) {
+ return this.isSelectedItem;
+ } else {
+ return this.navigated;
+ }
+ },
treeItemStyles() {
let itemTop = (this.itemOffset + this.itemIndex) * this.itemHeight + 'px';
@@ -144,20 +162,30 @@ export default {
this.$emit('tree-item-destoyed', this.navigationPath);
},
methods: {
- navigationClick() {
- this.$emit('navigation-click', this.isOpen || this.isLoading ? 'close' : 'open');
+ itemAction() {
+ this.$emit('tree-item-action', this.isOpen || this.isLoading ? 'close' : 'open');
},
- handleClick(event) {
+ itemClick(event) {
// skip for navigation, let viewControl handle click
- if (this.$refs.navigate.$el === event.target) {
+ if (this.$refs.action.$el === event.target) {
return;
}
event.stopPropagation();
- this.$refs.objectLabel.navigateOrPreview(event);
+
+ if (!this.isSelectorTree) {
+ this.$refs.objectLabel.navigateOrPreview(event);
+ } else {
+ this.$emit('tree-item-selection', this.node);
+ }
},
handleContextMenu(event) {
event.stopPropagation();
+
+ if (this.isSelectorTree) {
+ return;
+ }
+
this.$refs.objectLabel.showContextMenu(event);
},
isNavigated() {
diff --git a/src/ui/mixins/context-menu-gesture.js b/src/ui/mixins/context-menu-gesture.js
index 404015f38..ad7fa0de3 100644
--- a/src/ui/mixins/context-menu-gesture.js
+++ b/src/ui/mixins/context-menu-gesture.js
@@ -33,6 +33,10 @@ export default {
},
methods: {
showContextMenu(event) {
+ if (this.readOnly) {
+ return;
+ }
+
event.preventDefault();
event.stopPropagation();
diff --git a/src/ui/preview/Preview.vue b/src/ui/preview/Preview.vue
index 6a1500db5..b4a9fa33b 100644
--- a/src/ui/preview/Preview.vue
+++ b/src/ui/preview/Preview.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -22,10 +22,10 @@
<template>
<div class="l-preview-window js-preview-window">
<PreviewHeader
- :current-view="currentView"
+ :current-view="currentViewProvider"
:action-collection="actionCollection"
:domain-object="domainObject"
- :views="views"
+ :views="viewProviders"
/>
<div class="l-preview-window__object-view js-notebook-snapshot-item">
<div ref="objectView"></div>
@@ -52,6 +52,12 @@ export default {
default() {
return undefined;
}
+ },
+ existingView: {
+ type: Object,
+ default() {
+ return undefined;
+ }
}
},
data() {
@@ -60,21 +66,25 @@ export default {
return {
domainObject: domainObject,
viewKey: undefined,
- views: [],
- currentView: {},
- actionCollection: undefined
+ viewProviders: [],
+ currentViewProvider: {},
+ actionCollection: undefined,
+ existingViewIndex: 0
};
},
mounted() {
- this.views = this.openmct.objectViews.get(this.domainObject, this.objectPath).map((view) => {
- view.onItemClicked = () => {
- return this.setView(view);
- };
+ this.viewProviders = this.openmct.objectViews.get(this.domainObject, this.objectPath);
+ this.viewProviders.forEach((provider, index) => {
+ provider.onItemClicked = () => {
+ if (this.existingView && provider.key === this.existingView.key) {
+ this.existingViewIndex = index;
+ }
- return view;
+ this.setView(provider);
+ };
});
- this.setView(this.views[0]);
+ this.setView(this.viewProviders[0]);
},
beforeDestroy() {
if (this.stopListeningStyles) {
@@ -91,41 +101,74 @@ export default {
}
},
destroyed() {
- this.view.destroy();
+ if (!this.existingView) {
+ this.view.destroy();
+ } else if (this.existingViewElement) {
+ // if the existing view element is populated, it's the currently viewed view
+ // in preview and we need to add it back to the parent.
+ this.addExistingViewBackToParent();
+ }
},
methods: {
clear() {
if (this.view) {
- this.view.destroy();
+ if (this.view !== this.existingView) {
+ this.view.destroy();
+ } else {
+ this.addExistingViewBackToParent();
+ }
+
this.$refs.objectView.innerHTML = '';
+ delete this.view;
+ delete this.viewContainer;
}
-
- delete this.view;
- delete this.viewContainer;
},
- setView(view) {
- if (this.viewKey === view.key) {
+ setView(viewProvider) {
+ if (this.viewKey === viewProvider.key) {
return;
}
+ const isExistingView = viewProvider.key === this.existingView?.key;
+
this.clear();
- this.viewKey = view.key;
- this.currentView = view;
+
+ this.viewKey = viewProvider.key;
+ this.initializeViewContainer();
+
+ if (isExistingView) {
+ this.view = this.existingView;
+ this.existingViewElement = this.existingView.parentElement.firstChild;
+ this.currentViewProvider = this.viewProviders[this.existingViewIndex];
+ } else {
+ this.currentViewProvider = viewProvider;
+ this.view = this.currentViewProvider.view(this.domainObject, this.objectPath);
+ }
+
+ this.getActionsCollection(this.view);
+
+ if (isExistingView) {
+ this.viewContainer.appendChild(this.existingViewElement);
+ } else {
+ this.view.show(this.viewContainer, false, this.viewOptions);
+ }
+
+ this.initObjectStyles();
+ },
+ addExistingViewBackToParent() {
+ this.existingView.parentElement.appendChild(this.existingViewElement);
+ delete this.existingViewElement;
+ },
+ initializeViewContainer() {
this.viewContainer = document.createElement('div');
this.viewContainer.classList.add('l-angular-ov-wrapper');
this.$refs.objectView.append(this.viewContainer);
- this.view = this.currentView.view(this.domainObject, this.objectPath);
-
- this.getActionsCollection();
- this.view.show(this.viewContainer, false, this.viewOptions);
- this.initObjectStyles();
},
- getActionsCollection() {
+ getActionsCollection(view) {
if (this.actionCollection) {
this.actionCollection.destroy();
}
- this.actionCollection = this.openmct.actions.getActionsCollection(this.objectPath, this.view);
+ this.actionCollection = this.openmct.actions.getActionsCollection(this.objectPath, view);
},
initObjectStyles() {
if (!this.styleRuleManager) {
diff --git a/src/ui/preview/PreviewAction.js b/src/ui/preview/PreviewAction.js
index b0514aa6f..f077032da 100644
--- a/src/ui/preview/PreviewAction.js
+++ b/src/ui/preview/PreviewAction.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -21,9 +21,11 @@
*****************************************************************************/
import Preview from './Preview.vue';
import Vue from 'vue';
+import EventEmitter from 'EventEmitter';
-export default class PreviewAction {
+export default class PreviewAction extends EventEmitter {
constructor(openmct) {
+ super();
/**
* Metadata
*/
@@ -75,10 +77,12 @@ export default class PreviewAction {
onDestroy: () => {
PreviewAction.isVisible = false;
preview.$destroy();
+ this.emit('isVisible', false);
}
});
PreviewAction.isVisible = true;
+ this.emit('isVisible', true);
}
appliesTo(objectPath, view = {}) {
diff --git a/src/ui/preview/ViewHistoricalDataAction.js b/src/ui/preview/ViewHistoricalDataAction.js
index e5c207dfc..dfc1953c9 100644
--- a/src/ui/preview/ViewHistoricalDataAction.js
+++ b/src/ui/preview/ViewHistoricalDataAction.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/ui/preview/plugin.js b/src/ui/preview/plugin.js
index d3f8e388d..3bde96a44 100644
--- a/src/ui/preview/plugin.js
+++ b/src/ui/preview/plugin.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/ui/preview/preview-header.vue b/src/ui/preview/preview-header.vue
index b01c6203e..20d94851d 100644
--- a/src/ui/preview/preview-header.vue
+++ b/src/ui/preview/preview-header.vue
@@ -4,9 +4,10 @@
<div
class="l-browse-bar__object-name--w c-object-label"
>
- <div v-if="type"
- class="c-object-label__type-icon"
- :class="type.definition.cssClass"
+ <div
+ v-if="type"
+ class="c-object-label__type-icon"
+ :class="type.definition.cssClass"
></div>
<span class="l-browse-bar__object-name c-object-label__name">
{{ domainObject.name }}
@@ -19,11 +20,12 @@
:views="views"
:current-view="currentView"
/>
- <NotebookMenuSwitcher :domain-object="domainObject"
- :object-path="objectPath"
- :is-preview="true"
- :current-view="currentView"
- class="c-notebook-snapshot-menubutton"
+ <NotebookMenuSwitcher
+ :domain-object="domainObject"
+ :object-path="objectPath"
+ :is-preview="true"
+ :current-view="currentView"
+ class="c-notebook-snapshot-menubutton"
/>
<div class="l-browse-bar__actions">
<button
diff --git a/src/ui/registries/InspectorViewRegistry.js b/src/ui/registries/InspectorViewRegistry.js
index 7d53a81d4..ce77fcf79 100644
--- a/src/ui/registries/InspectorViewRegistry.js
+++ b/src/ui/registries/InspectorViewRegistry.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/ui/registries/ToolbarRegistry.js b/src/ui/registries/ToolbarRegistry.js
index b7195f451..ec82a7aa9 100644
--- a/src/ui/registries/ToolbarRegistry.js
+++ b/src/ui/registries/ToolbarRegistry.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/ui/registries/ViewRegistry.js b/src/ui/registries/ViewRegistry.js
index 18704d7ce..c77083ff5 100644
--- a/src/ui/registries/ViewRegistry.js
+++ b/src/ui/registries/ViewRegistry.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -90,6 +90,7 @@ define(['EventEmitter'], function (EventEmitter) {
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);
diff --git a/src/ui/router/ApplicationRouter.js b/src/ui/router/ApplicationRouter.js
index 8bb1c1027..35940058e 100644
--- a/src/ui/router/ApplicationRouter.js
+++ b/src/ui/router/ApplicationRouter.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -50,6 +50,10 @@ class ApplicationRouter extends EventEmitter {
this.started = false;
this.setHash = _.debounce(this.setHash.bind(this), 300);
+
+ openmct.once('destroy', () => {
+ this.destroy();
+ });
}
// Public Methods
diff --git a/src/ui/router/Browse.js b/src/ui/router/Browse.js
index 05106815a..1c8f62245 100644
--- a/src/ui/router/Browse.js
+++ b/src/ui/router/Browse.js
@@ -133,9 +133,7 @@ define([
composition.load()
.then(children => {
let lastChild = children[children.length - 1];
- if (!lastChild) {
- console.debug('Unable to navigate to anything. No root objects found.');
- } else {
+ if (lastChild) {
let lastChildId = openmct.objects.makeKeyString(lastChild.identifier);
openmct.router.setPath(`#/browse/${lastChildId}`);
}
diff --git a/src/ui/toolbar/components/toolbar-toggle-button.vue b/src/ui/toolbar/components/toolbar-toggle-button.vue
index e3d37c4c5..27ca3d62f 100644
--- a/src/ui/toolbar/components/toolbar-toggle-button.vue
+++ b/src/ui/toolbar/components/toolbar-toggle-button.vue
@@ -5,9 +5,15 @@
:title="nextValue.title"
:class="[nextValue.icon, {'c-icon-button--mixed': nonSpecific}]"
@click="cycle"
- ></div>
-</div>
-</template>
+ >
+ <div
+ v-if="nextValue.label"
+ class="c-icon-button__label"
+ >
+ {{ nextValue.label }}
+ </div>
+ </div>
+</div></template>
<script>
export default {
diff --git a/src/utils/agent/Agent.js b/src/utils/agent/Agent.js
index 065afcee4..f88f47eb3 100644
--- a/src/utils/agent/Agent.js
+++ b/src/utils/agent/Agent.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -89,7 +89,21 @@ export default class Agent {
* @returns {boolean} true in portrait mode
*/
isPortrait() {
- return this.window.innerWidth < this.window.innerHeight;
+ const { screen } = this.window;
+ const hasScreenOrientation = screen && Object.prototype.hasOwnProperty.call(screen, 'orientation');
+ const hasWindowOrientation = Object.prototype.hasOwnProperty.call(this.window, 'orientation');
+
+ if (hasScreenOrientation) {
+ return screen.orientation.type.includes('portrait');
+ } else if (hasWindowOrientation) {
+ // Use window.orientation API if available (e.g. Safari mobile)
+ // which returns [-90, 0, 90, 180] based on device orientation.
+ const { orientation } = this.window;
+
+ return Math.abs(orientation / 90) % 2 === 0;
+ } else {
+ return this.window.innerWidth < this.window.innerHeight;
+ }
}
/**
* Check if the user's device is in a landscape-style
diff --git a/src/utils/agent/AgentSpec.js b/src/utils/agent/AgentSpec.js
index 6a5ed9472..d803fea4a 100644
--- a/src/utils/agent/AgentSpec.js
+++ b/src/utils/agent/AgentSpec.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -68,7 +68,7 @@ describe("The Agent", function () {
expect(agent.isTablet()).toBeTruthy();
});
- it("detects display orientation", function () {
+ it("detects display orientation by innerHeight and innerWidth", function () {
agent = new Agent(testWindow);
testWindow.innerWidth = 1024;
testWindow.innerHeight = 400;
@@ -80,6 +80,34 @@ describe("The Agent", function () {
expect(agent.isLandscape()).toBeFalsy();
});
+ it("detects display orientation by screen.orientation", function () {
+ agent = new Agent(testWindow);
+ testWindow.screen = {
+ orientation: {
+ type: "landscape-primary"
+ }
+ };
+ expect(agent.isPortrait()).toBeFalsy();
+ expect(agent.isLandscape()).toBeTruthy();
+ testWindow.screen = {
+ orientation: {
+ type: "portrait-primary"
+ }
+ };
+ expect(agent.isPortrait()).toBeTruthy();
+ expect(agent.isLandscape()).toBeFalsy();
+ });
+
+ it("detects display orientation by window.orientation", function () {
+ agent = new Agent(testWindow);
+ testWindow.orientation = 90;
+ expect(agent.isPortrait()).toBeFalsy();
+ expect(agent.isLandscape()).toBeTruthy();
+ testWindow.orientation = 0;
+ expect(agent.isPortrait()).toBeTruthy();
+ expect(agent.isLandscape()).toBeFalsy();
+ });
+
it("detects touch support", function () {
testWindow.ontouchstart = null;
expect(new Agent(testWindow).isTouch()).toBe(true);
diff --git a/src/utils/clock/DefaultClock.js b/src/utils/clock/DefaultClock.js
index 40b6cbbec..91452edf2 100644
--- a/src/utils/clock/DefaultClock.js
+++ b/src/utils/clock/DefaultClock.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT Web, Copyright (c) 2014-2021, United States Government
+ * Open MCT Web, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/utils/clock/Ticker.js b/src/utils/clock/Ticker.js
index f536f6540..83bdd7e17 100644
--- a/src/utils/clock/Ticker.js
+++ b/src/utils/clock/Ticker.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2009-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/utils/duration.js b/src/utils/duration.js
index 3dd8e95d3..708d4b786 100644
--- a/src/utils/duration.js
+++ b/src/utils/duration.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/utils/raf.js b/src/utils/raf.js
new file mode 100644
index 000000000..d5c0c48fe
--- /dev/null
+++ b/src/utils/raf.js
@@ -0,0 +1,14 @@
+export default function raf(callback) {
+ let rendering = false;
+
+ return () => {
+ if (!rendering) {
+ rendering = true;
+
+ requestAnimationFrame(() => {
+ callback();
+ rendering = false;
+ });
+ }
+ };
+}
diff --git a/src/utils/rafSpec.js b/src/utils/rafSpec.js
new file mode 100644
index 000000000..0bf5ae9d9
--- /dev/null
+++ b/src/utils/rafSpec.js
@@ -0,0 +1,61 @@
+import raf from "./raf";
+
+describe('The raf utility function', () => {
+ it('Throttles function calls that arrive in quick succession using Request Animation Frame', () => {
+ const unthrottledFunction = jasmine.createSpy('unthrottledFunction');
+ const throttledCallback = jasmine.createSpy('throttledCallback');
+ const throttledFunction = raf(throttledCallback);
+
+ for (let i = 0; i < 10; i++) {
+ unthrottledFunction();
+ throttledFunction();
+ }
+
+ return new Promise((resolve) => {
+ requestAnimationFrame(resolve);
+ }).then(() => {
+ expect(unthrottledFunction).toHaveBeenCalledTimes(10);
+ expect(throttledCallback).toHaveBeenCalledTimes(1);
+ });
+ });
+ it('Only invokes callback once per animation frame', () => {
+ const throttledCallback = jasmine.createSpy('throttledCallback');
+ const throttledFunction = raf(throttledCallback);
+
+ for (let i = 0; i < 10; i++) {
+ throttledFunction();
+ }
+
+ return new Promise(resolve => {
+ requestAnimationFrame(resolve);
+ }).then(() => {
+ return new Promise(resolve => {
+ requestAnimationFrame(resolve);
+ });
+ }).then(() => {
+ expect(throttledCallback).toHaveBeenCalledTimes(1);
+ });
+ });
+ it('Invokes callback again if called in subsequent animation frame', () => {
+ const throttledCallback = jasmine.createSpy('throttledCallback');
+ const throttledFunction = raf(throttledCallback);
+
+ for (let i = 0; i < 10; i++) {
+ throttledFunction();
+ }
+
+ return new Promise(resolve => {
+ requestAnimationFrame(resolve);
+ }).then(() => {
+ for (let i = 0; i < 10; i++) {
+ throttledFunction();
+ }
+
+ return new Promise(resolve => {
+ requestAnimationFrame(resolve);
+ });
+ }).then(() => {
+ expect(throttledCallback).toHaveBeenCalledTimes(2);
+ });
+ });
+});
diff --git a/src/utils/template/templateHelpers.js b/src/utils/template/templateHelpers.js
new file mode 100644
index 000000000..70d381ce7
--- /dev/null
+++ b/src/utils/template/templateHelpers.js
@@ -0,0 +1,14 @@
+export function convertTemplateToHTML(templateString) {
+ const template = document.createElement('template');
+ template.innerHTML = templateString;
+
+ return template.content.cloneNode(true).children;
+}
+
+export function toggleClass(element, className) {
+ if (element.classList.contains(className)) {
+ element.classList.remove(className);
+ } else {
+ element.classList.add(className);
+ }
+}
diff --git a/src/utils/testing.js b/src/utils/testing.js
index 4d42d50b9..40ccd823d 100644
--- a/src/utils/testing.js
+++ b/src/utils/testing.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
diff --git a/src/utils/textHighlight/TextHighlight.vue b/src/utils/textHighlight/TextHighlight.vue
index 37ce232da..0c1cbb49c 100644
--- a/src/utils/textHighlight/TextHighlight.vue
+++ b/src/utils/textHighlight/TextHighlight.vue
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2021, United States Government
+ * Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -37,7 +37,7 @@
<script>
-import uuid from 'uuid';
+import { v4 as uuid } from 'uuid';
export default {
props: {
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 000000000..ee9b1e1e3
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,20 @@
+/* Note: Open MCT does not intend to support the entire Typescript ecosystem at this time.
+ * This file is intended to add Intellisense for IDEs like VSCode. For more information
+ * about Typescript, please discuss in https://github.com/nasa/openmct/discussions/4693
+*/
+{
+ "compilerOptions": {
+ "allowJs": true,
+ "checkJs": false,
+ "strict": true,
+ "esModuleInterop": true,
+ "noImplicitOverride": true,
+ "module": "esnext",
+ "moduleResolution": "node",
+
+ "paths": {
+ // matches the alias in webpack config, so that types for those imports are visible.
+ "@/*": ["src/*"]
+ }
+ }
+}
diff --git a/webpack.common.js b/webpack.common.js
index d12ded186..95a76fd40 100644
--- a/webpack.common.js
+++ b/webpack.common.js
@@ -1,20 +1,31 @@
+/* global __dirname */
+
const path = require('path');
const packageDefinition = require('./package.json');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const webpack = require('webpack');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
-const VueLoaderPlugin = require('vue-loader/lib/plugin');
-const gitRevision = require('child_process')
- .execSync('git rev-parse HEAD')
- .toString().trim();
-const gitBranch = require('child_process')
- .execSync('git rev-parse --abbrev-ref HEAD')
- .toString().trim();
+const {VueLoaderPlugin} = require('vue-loader');
+let gitRevision = 'error-retrieving-revision';
+let gitBranch = 'error-retrieving-branch';
+
+try {
+ gitRevision = require('child_process')
+ .execSync('git rev-parse HEAD')
+ .toString().trim();
+ gitBranch = require('child_process')
+ .execSync('git rev-parse --abbrev-ref HEAD')
+ .toString().trim();
+} catch (err) {
+ console.warn(err);
+}
-module.exports = {
+/** @type {import('webpack').Configuration} */
+const config = {
entry: {
openmct: './openmct.js',
+ generatorWorker: './example/generator/generatorWorker.js',
couchDBChangesFeed: './src/plugins/persistence/couch/CouchChangesFeed.js',
inMemorySearchWorker: './src/api/objects/InMemorySearchWorker.js',
espressoTheme: './src/plugins/themes/espresso-theme.scss',
@@ -22,24 +33,26 @@ module.exports = {
maelstromTheme: './src/plugins/themes/maelstrom-theme.scss'
},
output: {
- globalObject: "this",
+ globalObject: 'this',
filename: '[name].js',
- library: '[name]',
+ path: path.resolve(__dirname, 'dist'),
+ library: 'openmct',
libraryTarget: 'umd',
publicPath: '',
+ hashFunction: 'xxhash64',
clean: true
},
resolve: {
alias: {
"@": path.join(__dirname, "src"),
"legacyRegistry": path.join(__dirname, "src/legacyRegistry"),
- "saveAs": "file-saver",
+ "saveAs": "file-saver/src/FileSaver.js",
"csv": "comma-separated-values",
"EventEmitter": "eventemitter3",
"bourbon": "bourbon.scss",
"plotly-basic": "plotly.js-basic-dist",
"plotly-gl2d": "plotly.js-gl2d-dist",
- "d3-scale": path.join(__dirname, "node_modules/d3-scale/build/d3-scale.min.js"),
+ "d3-scale": path.join(__dirname, "node_modules/d3-scale/dist/d3-scale.min.js"),
"printj": path.join(__dirname, "node_modules/printj/dist/printj.min.js"),
"styles": path.join(__dirname, "src/styles"),
"MCT": path.join(__dirname, "src/MCT"),
@@ -67,6 +80,10 @@ module.exports = {
transform: function (content) {
return content.toString().replace(/dist\//g, '');
}
+ },
+ {
+ from: 'src/plugins/imagery/layers',
+ to: 'imagery'
}
]
}),
@@ -84,8 +101,13 @@ module.exports = {
{
loader: 'css-loader'
},
- 'resolve-url-loader',
- 'sass-loader'
+ {
+ loader: 'resolve-url-loader'
+ },
+ {
+ loader: 'sass-loader',
+ options: {sourceMap: true }
+ }
]
},
{
@@ -94,38 +116,33 @@ module.exports = {
},
{
test: /\.html$/,
- use: 'html-loader'
+ type: 'asset/source'
},
{
- test: /zepto/,
- use: [
- "imports-loader?this=>window",
- "exports-loader?Zepto"
- ]
+ test: /\.(jpg|jpeg|png|svg)$/,
+ type: 'asset/resource',
+ generator: {
+ filename: 'images/[name][ext]'
+ }
},
{
- test: /\.(jpg|jpeg|png|svg|ico|woff|woff2?|eot|ttf)$/,
- loader: 'file-loader',
- options: {
- name: '[name].[ext]',
- outputPath(url, resourcePath, context) {
- if (/\.(jpg|jpeg|png|svg)$/.test(url)) {
- return `images/${url}`;
- }
-
- if (/\.ico$/.test(url)) {
- return `icons/${url}`;
- }
-
- if (/\.(woff|woff2?|eot|ttf)$/.test(url)) {
- return `fonts/${url}`;
- } else {
- return `${url}`;
- }
- }
+ test: /\.ico$/,
+ type: 'asset/resource',
+ generator: {
+ filename: 'icons/[name][ext]'
+ }
+ },
+ {
+ test: /\.(woff|woff2?|eot|ttf)$/,
+ type: 'asset/resource',
+ generator: {
+ filename: 'fonts/[name][ext]'
}
}
]
},
stats: 'errors-warnings'
};
+
+// eslint-disable-next-line no-undef
+module.exports = config;
diff --git a/webpack.coverage.js b/webpack.coverage.js
new file mode 100644
index 000000000..cef2c5fe9
--- /dev/null
+++ b/webpack.coverage.js
@@ -0,0 +1,43 @@
+// This file extends the webpack.dev.js config to add istanbul coverage
+// instrumentation using babel-plugin-istanbul (see babel.coverage.js)
+
+const config = require('./webpack.dev');
+const path = require('path');
+const vueLoaderRule = config.module.rules.find(r => r.use === 'vue-loader');
+// eslint-disable-next-line no-undef
+const CI = process.env.CI === 'true';
+
+config.devtool = CI ? false : undefined;
+
+vueLoaderRule.use = {
+ loader: 'vue-loader'
+ // Attempt to use Babel with babel-plugin-istanbul
+
+ // TODO The purpose of this was to try to add coverage to JS expressions
+ // inside `<template>` markup, but it seems to add only coverage inside
+ // `<script>` tags.
+ // Issue: https://github.com/nasa/openmct/issues/4973
+ //
+ // options: {
+ // compiler: require('vue-template-babel-compiler'),
+ // compilerOptions: {
+ // babelOptions: require('./babel.coverage')
+ // }
+ // }
+};
+
+config.module.rules.push({
+ test: /\.js$/,
+ // test: /(\.js$)|(\?vue&type=template)/,
+ // exclude: /node_modules(?!.*\.vue)/,
+ exclude: /(Spec\.js$)|(node_modules)/,
+ use: {
+ loader: 'babel-loader',
+ options: {
+ // eslint-disable-next-line no-undef
+ configFile: path.resolve(process.cwd(), 'babel.coverage.js')
+ }
+ }
+});
+
+module.exports = config;
diff --git a/webpack.prod.js b/webpack.prod.js
index a64d4ce54..362b4eb50 100644
--- a/webpack.prod.js
+++ b/webpack.prod.js
@@ -16,5 +16,5 @@ module.exports = merge(common, {
__OPENMCT_ROOT_RELATIVE__: '""'
})
],
- devtool: 'source-map'
+ devtool: 'eval-source-map'
});